TLA Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2026 Michael Vandeberg
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/cppalliance/capy
9 : //
10 :
11 : #ifndef BOOST_CAPY_WORK_GUARD_HPP
12 : #define BOOST_CAPY_WORK_GUARD_HPP
13 :
14 : #include <boost/capy/detail/config.hpp>
15 : #include <boost/capy/ex/execution_context.hpp>
16 : #include <boost/capy/concept/executor.hpp>
17 :
18 : #include <utility>
19 :
20 : namespace boost {
21 : namespace capy {
22 :
23 : /** RAII guard that keeps an executor's context from completing.
24 :
25 : This class holds "work" on an executor, preventing the associated
26 : execution context's `run()` function from returning due to lack of
27 : work. It calls `on_work_started()` on construction and
28 : `on_work_finished()` on destruction, ensuring proper work tracking.
29 :
30 : The guard is useful when you need to keep an execution context
31 : running while waiting for external events or when work is
32 : posted later.
33 :
34 : @par RAII Semantics
35 :
36 : @li Construction calls `ex.on_work_started()`.
37 : @li Destruction calls `ex.on_work_finished()` if `owns_work()`.
38 : @li Copy construction creates a new work reference (calls
39 : `on_work_started()` again).
40 : @li Move construction transfers ownership without additional calls.
41 :
42 : @par Thread Safety
43 :
44 : Distinct objects may be accessed concurrently. Access to a single
45 : object requires external synchronization.
46 :
47 : @par Example
48 : @par !example work_guard
49 :
50 :
51 : @note The executor is returned by reference, allowing callers to
52 : manage the executor's lifetime directly. This is essential in
53 : coroutine-first designs where the executor often outlives individual
54 : coroutine frames.
55 :
56 : @tparam Ex A type satisfying the Executor concept.
57 :
58 : @see make_work_guard, Executor
59 : */
60 : template<Executor Ex>
61 : class work_guard
62 : {
63 : Ex ex_;
64 : bool owns_;
65 :
66 : public:
67 : /** Names the executor type this `work_guard<Ex>` guards. */
68 : using executor_type = Ex;
69 :
70 : /** Construct a work guard.
71 :
72 : Calls `ex.on_work_started()` to inform the executor that
73 : work is outstanding.
74 :
75 : @par Exception Safety
76 : No-throw guarantee.
77 :
78 : @par Postconditions
79 : @li `owns_work() == true`
80 : @li `executor() == ex`
81 :
82 : @param ex The executor to hold work on. Moved into the guard.
83 : */
84 : explicit
85 HIT 1962 : work_guard(Ex ex) noexcept
86 1962 : : ex_(std::move(ex))
87 1962 : , owns_(true)
88 : {
89 1962 : ex_.on_work_started();
90 1962 : }
91 :
92 : /** Construct a copy.
93 :
94 : Creates a new work guard holding work on the same executor.
95 : Calls `on_work_started()` on the executor.
96 :
97 : @par Exception Safety
98 : No-throw guarantee.
99 :
100 : @par Postconditions
101 : @li `owns_work() == other.owns_work()`
102 : @li `executor() == other.executor()`
103 :
104 : @param other The work guard to copy from.
105 : */
106 2 : work_guard(work_guard const& other) noexcept
107 2 : : ex_(other.ex_)
108 2 : , owns_(other.owns_)
109 : {
110 2 : if(owns_)
111 1 : ex_.on_work_started();
112 2 : }
113 :
114 : /** Construct by moving.
115 :
116 : Transfers work ownership from `other` to `*this`. Does not
117 : call `on_work_started()` or `on_work_finished()`.
118 :
119 : @par Exception Safety
120 : No-throw guarantee.
121 :
122 : @par Postconditions
123 : @li `owns_work()` equals the prior value of `other.owns_work()`
124 : @li `other.owns_work() == false`
125 :
126 : @param other The work guard to move from.
127 : */
128 1 : work_guard(work_guard&& other) noexcept
129 1 : : ex_(std::move(other.ex_))
130 1 : , owns_(other.owns_)
131 : {
132 1 : other.owns_ = false;
133 1 : }
134 :
135 : /** Destructor.
136 :
137 : If `owns_work()` is `true`, calls `on_work_finished()` on
138 : the executor.
139 :
140 : @par Exception Safety
141 : No-throw guarantee.
142 : */
143 1965 : ~work_guard()
144 : {
145 1965 : if(owns_)
146 1959 : ex_.on_work_finished();
147 1965 : }
148 :
149 : /** Copy assignment is disabled.
150 :
151 : A guard takes its work reference at construction and releases it at
152 : destruction or through @ref reset. No operation rebinds an existing
153 : guard to a different executor.
154 :
155 : @param other The work guard that would be assigned from.
156 :
157 : @return A reference to `*this`.
158 : */
159 : work_guard& operator=(work_guard const& other) = delete;
160 :
161 : /** Return the underlying executor by reference.
162 :
163 : The reference remains valid for the lifetime of this guard,
164 : enabling callers to manage executor lifetime explicitly.
165 :
166 : @par Exception Safety
167 : No-throw guarantee.
168 :
169 : @return A reference to the stored executor.
170 : */
171 : executor_type const&
172 3907 : executor() const noexcept
173 : {
174 3907 : return ex_;
175 : }
176 :
177 : /** Return whether the guard owns work.
178 :
179 : @par Exception Safety
180 : No-throw guarantee.
181 :
182 : @return `true` if this guard calls `on_work_finished()`
183 : on destruction, `false` otherwise.
184 : */
185 : bool
186 12 : owns_work() const noexcept
187 : {
188 12 : return owns_;
189 : }
190 :
191 : /** Release ownership of the work.
192 :
193 : If `owns_work()` is `true`, calls `on_work_finished()` on
194 : the executor and sets ownership to `false`. Otherwise, has
195 : no effect.
196 :
197 : @par Exception Safety
198 : No-throw guarantee.
199 :
200 : @par Postconditions
201 : @li `owns_work() == false`
202 : */
203 : void
204 5 : reset() noexcept
205 : {
206 5 : if(owns_)
207 : {
208 4 : ex_.on_work_finished();
209 4 : owns_ = false;
210 : }
211 5 : }
212 : };
213 :
214 : /** Create a work guard from an executor.
215 :
216 : @par Exception Safety
217 : No-throw guarantee.
218 :
219 : @param ex The executor to create the guard for.
220 :
221 : @return A `work_guard` holding work on `ex`.
222 :
223 : @see work_guard
224 : */
225 : template<Executor Ex>
226 : work_guard<Ex>
227 3 : make_work_guard(Ex ex)
228 : {
229 3 : return work_guard<Ex>(std::move(ex));
230 : }
231 :
232 : } // capy
233 : } // boost
234 :
235 : #endif
|