morph
0.1.0
Asynchronous bridge between GUI thread and business models
Toggle main menu visibility
Loading...
Searching...
No Matches
owner_strand.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 <core/async/Strand.hpp>
6
#include <functional>
7
#include <type_traits>
8
#include <utility>
9
10
#include "../attributes.hpp"
11
#include "executor.hpp"
12
#include "
strand.hpp
"
13
14
/// @file
15
/// @brief `OwnerStrand`: one core-cpp strand over a morph executor, which is
16
/// itself a morph executor — the owner a component's state belongs to.
17
///
18
/// Specified in `docs/spec/core/executor.md`, "Owner strands".
19
20
namespace
morph::exec {
21
22
/// @brief One strand over a morph executor, usable wherever a morph executor
23
/// is: the owner a component's state belongs to.
24
///
25
/// Tasks run one at a time, in the order they were posted, on the threads of
26
/// the base executor. A component keeps its state in tasks on one of these
27
/// and needs no lock for it: `RemoteServer` keeps its registry here, and
28
/// `ReconnectCoordinator` its reconnect sequence.
29
///
30
/// Every task runs inside an `ExecutorScope` naming this object's
31
/// `coreExecutor()`, so inside a task `runningOn(*this)` is true — as is
32
/// `runningOn(base)`, whose scope the strand's batch runs within — and a
33
/// coroutine that suspends in a task and resumes on the current executor comes
34
/// back here. Outside every task both are false.
35
///
36
/// Being a `morph::exec::IExecutor` is what lets a component hand its owner out
37
/// as a `Completion`'s callback executor, or to a collaborator that must run
38
/// on the same owner (`SyncWorker`, on the coordinator's strand).
39
class
OwnerStrand
final :
public
::morph::exec::IExecutor
{
40
public
:
41
/// @param base Where the strand's tasks run. Borrowed: it must outlive this
42
/// object and keep running tasks until it is closed.
43
explicit
OwnerStrand
(
::morph::exec::IExecutor
& base MORPH_LIFETIMEBOUND)
44
: _strand{base.
coreExecutor
(),
45
::core::async::StrandOptions{.aroundTask = ::core::async::AroundTask::of(_hook)}} {}
46
47
OwnerStrand
(
const
OwnerStrand
&) =
delete
;
48
OwnerStrand
& operator=(
const
OwnerStrand
&) =
delete
;
49
OwnerStrand
(
OwnerStrand
&&) =
delete
;
50
OwnerStrand
& operator=(
OwnerStrand
&&) =
delete
;
51
52
/// @brief Closes the strand: what is still queued is dropped, and a task
53
/// running on another thread is waited for.
54
~OwnerStrand
()
override
{ _strand.close(); }
55
56
/// @brief Queues @p task after everything posted before it. A throw out of
57
/// it is logged. Dropped once the strand is closed.
58
/// @param task Callable to run on the strand.
59
void
post
(std::function<
void
()> task)
override
{
60
_strand.post(detail::LoggedTask<std::function<
void
()>>{std::move(task)});
61
}
62
63
/// @brief Queues @p task, held by value in the strand's one allocation for
64
/// it, after everything posted before it. A throw out of it is
65
/// logged. Dropped once the strand is closed.
66
///
67
/// What a component posts to its own strand: no `std::function` in
68
/// between, so a move-only capture is fine and a post costs one allocation.
69
/// @tparam F The callable's type.
70
/// @param task Callable to run on the strand.
71
template
<
typename
F>
72
void
postTask
(F&& task) {
73
_strand.post(detail::LoggedTask<std::decay_t<F>>{std::forward<F>(task)});
74
}
75
76
/// @brief Whether the calling thread is inside one of this strand's tasks.
77
/// @return True inside a task, at any depth.
78
[[nodiscard]]
bool
runningHere
() const noexcept {
return
_strand.runningHere(); }
79
80
/// @brief Whether nothing is queued or running.
81
/// @return True when idle. Racy by nature where other threads post.
82
[[nodiscard]]
bool
idle
()
const
{
return
_strand.idle(); }
83
84
/// @brief Closes the strand, as the destructor does. Idempotent.
85
void
close
() { _strand.close(); }
86
87
/// @brief Refuses the strand's try-forms and keeps running what is queued;
88
/// `post` is still admitted until `close()`. Idempotent.
89
void
seal
() { _strand.seal(); }
90
91
private
:
92
/// States this strand's identity around each task it runs.
93
struct
Hook {
94
OwnerStrand
* self;
95
void
operator()(::core::async::RunTask run)
const
{
96
::core::async::ExecutorScope
const
scope{self->coreExecutor()};
97
run();
98
}
99
};
100
101
Hook _hook{
this
};
102
/// After the hook, so it is destroyed first: it calls the hook.
103
::core::async::Strand _strand;
104
};
105
106
}
// namespace morph::exec
morph::exec::OwnerStrand
One strand over a morph executor, usable wherever a morph executor is: the owner a component's state ...
Definition
owner_strand.hpp:39
morph::exec::OwnerStrand::idle
bool idle() const
Whether nothing is queued or running.
Definition
owner_strand.hpp:82
morph::exec::OwnerStrand::post
void post(std::function< void()> task) override
Queues task after everything posted before it. A throw out of it is logged. Dropped once the strand i...
Definition
owner_strand.hpp:59
morph::exec::OwnerStrand::close
void close()
Closes the strand, as the destructor does. Idempotent.
Definition
owner_strand.hpp:85
morph::exec::OwnerStrand::postTask
void postTask(F &&task)
Queues task, held by value in the strand's one allocation for it, after everything posted before it....
Definition
owner_strand.hpp:72
morph::exec::OwnerStrand::runningHere
bool runningHere() const noexcept
Whether the calling thread is inside one of this strand's tasks.
Definition
owner_strand.hpp:78
morph::exec::OwnerStrand::~OwnerStrand
~OwnerStrand() override
Closes the strand: what is still queued is dropped, and a task running on another thread is waited fo...
Definition
owner_strand.hpp:54
morph::exec::OwnerStrand::OwnerStrand
OwnerStrand(::morph::exec::IExecutor &base)
Definition
owner_strand.hpp:43
morph::exec::OwnerStrand::seal
void seal()
Refuses the strand's try-forms and keeps running what is queued; post is still admitted until close()...
Definition
owner_strand.hpp:89
strand.hpp
morph's strands: one per model instance, from core-cpp's core::async::KeyedStrands,...
morph::exec::IExecutor
Abstract executor interface.
Definition
executor.hpp:70
morph::exec::IExecutor::coreExecutor
::core::async::IExecutor & coreExecutor() noexcept
This executor as a core-cpp executor: the identity an core::async::ExecutorScope names.
Definition
executor.hpp:100
morph
core
owner_strand.hpp
Generated by
1.17.0