|
morph 0.1.0
Asynchronous bridge between GUI thread and business models
|
IExecutor implementation that posts tasks to a Qt event loop. More...
#include <qt_executor.hpp>
Public Member Functions | |
| QtExecutor (QObject *context=QCoreApplication::instance()) | |
Constructs an executor that posts tasks to context's thread. | |
| QtExecutor (const QtExecutor &)=delete | |
| QtExecutor & | operator= (const QtExecutor &)=delete |
| QtExecutor (QtExecutor &&)=delete | |
| QtExecutor & | operator= (QtExecutor &&)=delete |
| ~QtExecutor () override=default | |
| Destroys the lifetime token, so any still-queued task becomes a no-op. | |
| void | post (std::function< void()> fn) override |
Posts fn to the Qt event loop for execution on the context's thread. | |
| Public Member Functions inherited from morph::exec::IExecutor | |
| IExecutor (const IExecutor &)=delete | |
| IExecutor & | operator= (const IExecutor &)=delete |
| IExecutor (IExecutor &&)=delete | |
| IExecutor & | operator= (IExecutor &&)=delete |
| virtual bool | isSerial () const noexcept |
| Whether this executor runs one task at a time, each after the tasks posted before it: a strand, a GUI loop, a pumped MainThreadExecutor. | |
| ::core::async::IExecutor & | coreExecutor () noexcept |
| This executor as a core-cpp executor: the identity an core::async::ExecutorScope names. | |
IExecutor implementation that posts tasks to a Qt event loop.
Uses QMetaObject::invokeMethod with Qt::QueuedConnection so the callable always runs on the thread that owns the configured context object — by default QCoreApplication::instance(), i.e. the GUI thread.
Thread-safe: post() may be called from any thread. No QObject subclass is required by the caller.
Each delivered task runs inside a core::async::ExecutorScope naming this executor, so morph::exec::runningOn(*this) is true inside it.
A task still queued when this executor is destroyed is dropped, not run. post() enqueues and returns; it does not run the callable, and the queued Qt event outlives the post() call that created it. Joining the worker pool that made the call proves the post() happened — never that the event was delivered. Without a guard, an event still on the Qt queue at teardown is delivered against a freed executor and reads _context off freed memory.
Bridge::executeVia reaches that state on an ordinary run: it chains three Completion objects per dispatched action, each settled from inside the previous one's delivered callback, so a caller waiting only on its own top-level completion can observe "done" while an intermediate post is still queued. When that stale event is pumped, its body calls post() for the next link in the chain — against a freed executor. It segfaults on an uninstrumented build, not only under a sanitizer.
Each queued task therefore carries a weak observer of this executor's lifetime and does nothing if the executor is already gone. Dropping is the correct outcome rather than a lesser evil: a chain being torn down has nobody left to observe its result, and Completion's orphan logging already covers a completion that never resolves.
Boundary of the guarantee. The check assumes this executor is destroyed on the same thread that runs its context's event loop, which holds for every owner in this repository. Destroying one from another thread while its loop is mid-delivery still needs external synchronisation: the token covers teardown with events still queued, not a genuine cross-thread race.
Definition at line 53 of file qt_executor.hpp.
|
inlineexplicit |
Constructs an executor that posts tasks to context's thread.
| context | QObject whose owning thread's event loop runs posted tasks; QMetaObject::invokeMethod dispatches to whichever thread context->thread() reports at the time each task is posted, so tasks posted after context is moved to a different thread run there. Defaults to QCoreApplication::instance(), i.e. the GUI thread. Passing nullptr (e.g. when constructed before QCoreApplication exists) makes post() a no-op, matching QMetaObject::invokeMethod's own handling of a null target. Borrowed, not owned: a non-null context must outlive this executor. |
Definition at line 66 of file qt_executor.hpp.
|
inlineoverridevirtual |
Posts fn to the Qt event loop for execution on the context's thread.
Returns immediately. fn is invoked asynchronously once the event loop processes the queued event.
Runs only if this executor is still alive when the event is delivered; see the class docstring.
| fn | Callable to execute on the context object's thread. |
Implements morph::exec::IExecutor.
Definition at line 86 of file qt_executor.hpp.