| 198 | |
| 199 | template <typename F> |
| 200 | auto postToViewerAndWait(VisualizerImpl* viewer, F&& fn) -> std::invoke_result_t<F> { |
| 201 | using ResultT = std::invoke_result_t<F>; |
| 202 | constexpr std::string_view shutdown_error = "Viewer is shutting down"; |
| 203 | constexpr std::string_view task_error = "Viewer work failed"; |
| 204 | |
| 205 | if (viewer->isOnViewerThread()) { |
| 206 | if (!viewer->acceptsPostedWork()) { |
| 207 | return std::unexpected(std::string(shutdown_error)); |
| 208 | } |
| 209 | try { |
| 210 | return std::invoke(std::forward<F>(fn)); |
| 211 | } catch (const std::exception& e) { |
| 212 | return std::unexpected(std::format("{}: {}", task_error, e.what())); |
| 213 | } catch (...) { |
| 214 | return std::unexpected(std::string(task_error)); |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | auto task = std::make_shared<std::decay_t<F>>(std::forward<F>(fn)); |
| 219 | auto promise = std::make_shared<std::promise<ResultT>>(); |
| 220 | auto completed = std::make_shared<std::atomic_bool>(false); |
| 221 | auto future = promise->get_future(); |
| 222 | |
| 223 | auto finish_with_value = [promise, completed](ResultT value) mutable { |
| 224 | if (!completed->exchange(true)) { |
| 225 | promise->set_value(std::move(value)); |
| 226 | } |
| 227 | }; |
| 228 | auto finish_with_exception = [promise, completed](std::exception_ptr error) { |
| 229 | if (!completed->exchange(true)) { |
| 230 | promise->set_exception(std::move(error)); |
| 231 | } |
| 232 | }; |
| 233 | |
| 234 | const bool posted = viewer->postWork(VisualizerImpl::WorkItem{ |
| 235 | .run = |
| 236 | [task, finish_with_value, finish_with_exception]() mutable { |
| 237 | try { |
| 238 | finish_with_value(std::invoke(*task)); |
| 239 | } catch (...) { |
| 240 | finish_with_exception(std::current_exception()); |
| 241 | } |
| 242 | }, |
| 243 | .cancel = |
| 244 | [finish_with_value, shutdown_error]() mutable { |
| 245 | finish_with_value(std::unexpected(std::string(shutdown_error))); |
| 246 | }}); |
| 247 | |
| 248 | if (!posted) { |
| 249 | return std::unexpected(std::string(shutdown_error)); |
| 250 | } |
| 251 | |
| 252 | try { |
| 253 | return future.get(); |
| 254 | } catch (const std::exception& e) { |
| 255 | return std::unexpected(std::format("{}: {}", task_error, e.what())); |
| 256 | } catch (...) { |
| 257 | return std::unexpected(std::string(task_error)); |
no test coverage detected