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 : @code
49 : thread_pool pool(1);
50 :
51 : // Keep the pool from completing while we set things up
52 : auto guard = make_work_guard(pool.get_executor());
53 :
54 : // ... post work to pool ...
55 :
56 : // Allow the pool to complete when work is done
57 : guard.reset();
58 :
59 : pool.join();
60 : @endcode
61 :
62 : @note The executor is returned by reference, allowing callers to
63 : manage the executor's lifetime directly. This is essential in
64 : coroutine-first designs where the executor often outlives individual
65 : coroutine frames.
66 :
67 : @tparam Ex A type satisfying the Executor concept.
68 :
69 : @see make_work_guard, Executor
70 : */
71 : template<Executor Ex>
72 : class work_guard
73 : {
74 : Ex ex_;
75 : bool owns_;
76 :
77 : public:
78 : /** Names the executor type this `work_guard<Ex>` guards. */
79 : using executor_type = Ex;
80 :
81 : /** Construct a work guard.
82 :
83 : Calls `ex.on_work_started()` to inform the executor that
84 : work is outstanding.
85 :
86 : @par Exception Safety
87 : No-throw guarantee.
88 :
89 : @par Postconditions
90 : @li `owns_work() == true`
91 : @li `executor() == ex`
92 :
93 : @param ex The executor to hold work on. Moved into the guard.
94 : */
95 : explicit
96 HIT 1953 : work_guard(Ex ex) noexcept
97 1953 : : ex_(std::move(ex))
98 1953 : , owns_(true)
99 : {
100 1953 : ex_.on_work_started();
101 1953 : }
102 :
103 : /** Construct a copy.
104 :
105 : Creates a new work guard holding work on the same executor.
106 : Calls `on_work_started()` on the executor.
107 :
108 : @par Exception Safety
109 : No-throw guarantee.
110 :
111 : @par Postconditions
112 : @li `owns_work() == other.owns_work()`
113 : @li `executor() == other.executor()`
114 :
115 : @param other The work guard to copy from.
116 : */
117 2 : work_guard(work_guard const& other) noexcept
118 2 : : ex_(other.ex_)
119 2 : , owns_(other.owns_)
120 : {
121 2 : if(owns_)
122 1 : ex_.on_work_started();
123 2 : }
124 :
125 : /** Construct by moving.
126 :
127 : Transfers work ownership from `other` to `*this`. Does not
128 : call `on_work_started()` or `on_work_finished()`.
129 :
130 : @par Exception Safety
131 : No-throw guarantee.
132 :
133 : @par Postconditions
134 : @li `owns_work()` equals the prior value of `other.owns_work()`
135 : @li `other.owns_work() == false`
136 :
137 : @param other The work guard to move from.
138 : */
139 1 : work_guard(work_guard&& other) noexcept
140 1 : : ex_(std::move(other.ex_))
141 1 : , owns_(other.owns_)
142 : {
143 1 : other.owns_ = false;
144 1 : }
145 :
146 : /** Destructor.
147 :
148 : If `owns_work()` is `true`, calls `on_work_finished()` on
149 : the executor.
150 :
151 : @par Exception Safety
152 : No-throw guarantee.
153 : */
154 1956 : ~work_guard()
155 : {
156 1956 : if(owns_)
157 1950 : ex_.on_work_finished();
158 1956 : }
159 :
160 : /** Copy assignment is disabled.
161 :
162 : A guard takes its work reference at construction and releases it at
163 : destruction or through @ref reset. No operation rebinds an existing
164 : guard to a different executor.
165 :
166 : @param other The work guard that would be assigned from.
167 :
168 : @return A reference to `*this`.
169 : */
170 : work_guard& operator=(work_guard const& other) = delete;
171 :
172 : /** Return the underlying executor by reference.
173 :
174 : The reference remains valid for the lifetime of this guard,
175 : enabling callers to manage executor lifetime explicitly.
176 :
177 : @par Exception Safety
178 : No-throw guarantee.
179 :
180 : @return A reference to the stored executor.
181 : */
182 : executor_type const&
183 3889 : executor() const noexcept
184 : {
185 3889 : return ex_;
186 : }
187 :
188 : /** Return whether the guard owns work.
189 :
190 : @par Exception Safety
191 : No-throw guarantee.
192 :
193 : @return `true` if this guard calls `on_work_finished()`
194 : on destruction, `false` otherwise.
195 : */
196 : bool
197 12 : owns_work() const noexcept
198 : {
199 12 : return owns_;
200 : }
201 :
202 : /** Release ownership of the work.
203 :
204 : If `owns_work()` is `true`, calls `on_work_finished()` on
205 : the executor and sets ownership to `false`. Otherwise, has
206 : no effect.
207 :
208 : @par Exception Safety
209 : No-throw guarantee.
210 :
211 : @par Postconditions
212 : @li `owns_work() == false`
213 : */
214 : void
215 5 : reset() noexcept
216 : {
217 5 : if(owns_)
218 : {
219 4 : ex_.on_work_finished();
220 4 : owns_ = false;
221 : }
222 5 : }
223 : };
224 :
225 : /** Create a work guard from an executor.
226 :
227 : @par Exception Safety
228 : No-throw guarantee.
229 :
230 : @param ex The executor to create the guard for.
231 :
232 : @return A `work_guard` holding work on `ex`.
233 :
234 : @see work_guard
235 : */
236 : template<Executor Ex>
237 : work_guard<Ex>
238 3 : make_work_guard(Ex ex)
239 : {
240 3 : return work_guard<Ex>(std::move(ex));
241 : }
242 :
243 : } // capy
244 : } // boost
245 :
246 : #endif
|