morph 0.1.0
Asynchronous bridge between GUI thread and business models
Loading...
Searching...
No Matches
owner_probe.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4#include <atomic>
5#include <cassert>
6#include <core/async/IExecutor.hpp>
7#include <cstdio>
8
9/// @file
10/// @brief The check every loop-owned body makes on entry: "am I on my owner?"
11///
12/// A component whose state belongs to one executor (an `IoLoop`'s loop, for the
13/// transports, the timeout scheduler and the network monitor) calls
14/// `noteOwner(site, owner)` at the top of each body that touches that state. In
15/// a debug build it asserts the calling thread is inside a task of `owner`.
16///
17/// A test replaces the assertion with an observation: it installs a probe, and
18/// every `noteOwner` hands the probe the site and the owner instead of
19/// asserting. The probe reads the executor scope itself, so a test proves the
20/// body ran posted without trusting the component's own answer, and a test that
21/// puts the wrong behaviour back sees a failed check rather than an abort.
22
23namespace morph::exec::detail {
24
25/// @brief What a test installs to observe owner checks: called with the site
26/// name and the owner the body expects, on the thread running the body.
27using OwnerProbe = void (*)(char const* site, ::core::async::IExecutor const& owner) noexcept;
28
29/// @brief The installed probe, or null.
30/// @return The process-wide probe slot.
31[[nodiscard]] inline std::atomic<OwnerProbe>& ownerProbe() noexcept {
32 static std::atomic<OwnerProbe> probe{nullptr};
33 return probe;
34}
35
36/// @brief Records that @p site is running a body that must run on @p owner.
37///
38/// With a probe installed, hands it the site and the owner. Otherwise asserts,
39/// in a debug build, that a task of @p owner is running on the calling thread.
40/// @param site A static string naming the body, e.g. `"TimeoutScheduler::schedule"`.
41/// @param owner The executor the body's state belongs to.
42/// @param onOwner Whether the component considers itself on its owner; what a
43/// debug build asserts when no probe is installed.
44inline void noteOwner(char const* site, ::core::async::IExecutor const& owner, bool onOwner) noexcept {
45 if (OwnerProbe const probe = ownerProbe().load(std::memory_order_acquire); probe != nullptr) {
46 probe(site, owner);
47 return;
48 }
49#ifndef NDEBUG
50 // The assertion's text is fixed; the site says which misuse it was: off the owner, or another the owner's
51 // component reports through here, such as a duplicate row key.
52 if (!onOwner) {
53 static_cast<void>(std::fputs("morph: ", stderr));
54 static_cast<void>(std::fputs(site, stderr));
55 static_cast<void>(std::fputs("\n", stderr));
56 }
57#endif
58 assert(onOwner && "a loop-owned body ran off its owner's thread");
59 static_cast<void>(onOwner);
60}
61
62} // namespace morph::exec::detail