libs/capy/include/boost/capy/ex/work_guard.hpp

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