morph 0.1.0
Asynchronous bridge between GUI thread and business models
Loading...
Searching...
No Matches
owner_affinity.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4#include <core/async/ExecutorContext.hpp>
5#include <thread>
6
7#include "../../attributes.hpp"
8#include "../executor.hpp"
9#include "owner_probe.hpp"
10
11/// @file
12/// @brief "Am I on my owner?" for a component that belongs to one executor.
13///
14/// Specified in `docs/spec/core/executor.md`, "Owner affinity".
15
16namespace morph::exec::detail {
17
18/// @brief The owner a component belongs to, and the check each of its
19/// owner-only bodies makes on entry.
20///
21/// A component is on its owner inside a task the owner runs (`runningOn`),
22/// and — when it was constructed on a thread running no executor's task — on
23/// that thread. The second half is what a GUI owner needs: its thread runs
24/// code outside the owner's tasks too (a Qt slot, a QML handler, a test body,
25/// `main()` before its event loop starts), and that code is the owner. A
26/// component constructed inside an executor's task — on a pool or a strand —
27/// records no thread and is checked by `runningOn` alone. Copying an affinity
28/// hands a collaborator (the bridge's backend) the same answer.
29class OwnerAffinity {
30public:
31 /// @param owner The executor the component belongs to. Borrowed: it must
32 /// outlive the component.
33 explicit OwnerAffinity(IExecutor& owner MORPH_LIFETIMEBOUND) noexcept
34 : _owner{&owner},
35 _thread{::core::async::ExecutorScope::innermost() == nullptr ? std::this_thread::get_id()
36 : std::thread::id{}} {}
37
38 /// @brief The owner.
39 /// @return The executor passed at construction.
40 [[nodiscard]] IExecutor& owner() const noexcept { return *_owner; }
41
42 /// @brief Whether the calling thread is on the owner.
43 /// @return True inside a task of the owner, or on the thread that
44 /// constructed this affinity outside every executor's task.
45 [[nodiscard]] bool here() const noexcept {
46 return runningOn(*_owner) || (_thread != std::thread::id{} && std::this_thread::get_id() == _thread);
47 }
48
49 /// @brief Records that @p site is running a body that must run on the owner:
50 /// asserts in a debug build, or reports to an installed probe.
51 /// @param site A static string naming the body, e.g. `"Bridge::executeVia"`.
52 void note(char const* site) const noexcept { noteOwner(site, _owner->coreExecutor(), here()); }
53
54private:
55 IExecutor* _owner;
56 std::thread::id _thread;
57};
58
59} // namespace morph::exec::detail
The check every loop-owned body makes on entry: "am I on my owner?".