stlab 2.3.0
Modern, modular C++ algorithms, data structures, and concurrency primitives
Loading...
Searching...
No Matches
main_executor.hpp
Go to the documentation of this file.
1/*
2 Copyright 2015 Adobe
3 Distributed under the Boost Software License, Version 1.0.
4 (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5*/
6
7/**************************************************************************************************/
8
9#ifndef STLAB_CONCURRENCY_MAIN_EXECUTOR_HPP
10#define STLAB_CONCURRENCY_MAIN_EXECUTOR_HPP
11
22
23#include <stlab/config.hpp>
24
25#if STLAB_MAIN_EXECUTOR(QT5) || STLAB_MAIN_EXECUTOR(QT6)
26#include <QtGlobal>
27#if (STLAB_MAIN_EXECUTOR(QT5) && \
28 (QT_VERSION < QT_VERSION_CHECK(5, 0, 0) || QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) || \
29 STLAB_MAIN_EXECUTOR(QT6) && \
30 (QT_VERSION < QT_VERSION_CHECK(6, 0, 0) || QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)))
31#error "Mismatching Qt versions"
32#endif
33#include <QCoreApplication>
34#include <QEvent>
35#include <memory>
37#elif STLAB_MAIN_EXECUTOR(LIBDISPATCH)
38#include <dispatch/dispatch.h>
39#elif STLAB_MAIN_EXECUTOR(EMSCRIPTEN)
41#endif
42
43/**************************************************************************************************/
44
45namespace stlab {
46STLAB_VERSION_NAMESPACE_BEGIN()
47
48
53
54/**************************************************************************************************/
55
56namespace detail {
57
58/**************************************************************************************************/
59
60#if STLAB_MAIN_EXECUTOR(QT5) || STLAB_MAIN_EXECUTOR(QT6)
61
62class main_executor_type {
63 using result_type = void;
64
65 struct event_receiver;
66
67 class executor_event : public QEvent {
68 stlab::task<void()> _f;
69 std::unique_ptr<event_receiver> _receiver;
70
71 public:
72 executor_event() : QEvent(QEvent::User), _receiver(new event_receiver()) {
73 _receiver->moveToThread(QCoreApplication::instance()->thread());
74 }
75
76 template <typename F>
77 void set_task(F&& f) {
78 _f = std::forward<F>(f);
79 }
80
81 void execute() { _f(); }
82
83 QObject* receiver() const { return _receiver.get(); }
84 };
85
86 struct event_receiver : public QObject {
87 bool event(QEvent* event) override {
88 auto myEvent = dynamic_cast<executor_event*>(event);
89 if (myEvent) {
90 myEvent->execute();
91 return true;
92 }
93 return false;
94 }
95 };
96
97public:
98 template <typename F>
99 auto operator()(F f) const -> std::enable_if_t<std::is_nothrow_invocable_v<F>> {
100 auto event = std::make_unique<executor_event>();
101 event->set_task(std::move(f));
102 auto receiver = event->receiver();
103 QCoreApplication::postEvent(receiver, event.release());
104 }
105};
106
107/**************************************************************************************************/
108
109#elif STLAB_MAIN_EXECUTOR(LIBDISPATCH)
110
111struct main_executor_type {
112 using result_type = void;
113
114 template <typename F>
115 auto operator()(F f) const -> std::enable_if_t<std::is_nothrow_invocable_v<F>> {
116 using f_t = decltype(f);
117
118 dispatch_async_f(dispatch_get_main_queue(), new f_t(std::move(f)), [](void* f_) {
119 auto f = static_cast<f_t*>(f_);
120 (*f)();
121 delete f;
122 });
123 }
124};
125
126#elif STLAB_MAIN_EXECUTOR(EMSCRIPTEN)
127
128struct main_executor_type {
129 using result_type = void;
130
131 template <class F>
132 auto operator()(F&& f) const -> std::enable_if_t<std::is_nothrow_invocable_v<F>> {
133 using function_type = typename std::remove_reference<F>::type;
134 auto p = new function_type(std::forward<F>(f));
135
136 /*
137 `emscripten_async_run_in_main_runtime_thread()` schedules a function to run on the main
138 JS thread, however, the code can be executed at any POSIX thread cancellation point if
139 wasm code is executing on the JS main thread.
140 Executing the code from a POSIX thread cancellation point can cause problems, including
141 deadlocks and data corruption. Consider:
142 ```
143 mutex.lock(); // <-- If reentered, would deadlock here
144 new T; // <-- POSIX cancellation point, could reenter
145 ```
146 The call to `emscripten_async_call()` bounces the call to execute as part of the main
147 run-loop on the current (main) thread. This avoids nasty reentrancy issues if executed
148 from a POSIX thread cancellation point.
149 */
150
151 emscripten_async_run_in_main_runtime_thread(
152 EM_FUNC_SIG_VI, static_cast<void (*)(void*)>([](void* f_) {
153 emscripten_async_call(
154 [](void* f_) {
155 auto f = static_cast<function_type*>(f_);
156 // Note the absence of exception handling.
157 // Operations queued to the task system cannot throw as a precondition.
158 // We use packaged tasks to marshal exceptions.
159 (*f)();
160 delete f;
161 },
162 f_, 0);
163 }),
164 p);
165 }
166};
167
168#elif STLAB_MAIN_EXECUTOR(NONE)
169
170// For documentation only
171struct main_executor_type {
172 using result_type = void;
173
174 template <typename F>
175 void operator()(F f) const {}
176};
177
178#endif
179
180} // namespace detail
181
187inline constexpr auto main_executor = detail::main_executor_type{};
188
189/**************************************************************************************************/
190
192
193STLAB_VERSION_NAMESPACE_END()
194} // namespace stlab
195
196/**************************************************************************************************/
197
198#endif // STLAB_CONCURRENCY_MAIN_EXECUTOR_HPP
Thread-pool executors mapping to the OS scheduler (libdispatch, Windows pool, portable).
constexpr auto main_executor
Runs void() noexcept tasks on the process main thread (Qt, libdispatch, or Emscripten as configured).
Definition main_executor.hpp:187
typename noexcept_deducer< task_, F >::type task
task_ with noexcept deduced from the function type F (e.g. void() vs void() noexcept).
Definition task.hpp:324
Definition reverse.hpp:28
Move-only callable wrapper for executor scheduling (task<Signature>).