Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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_EXECUTOR_WORK_GUARD_HPP
11 : #define BOOST_CAPY_EXECUTOR_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 : @tparam Executor A type satisfying the executor concept.
64 :
65 : @see make_work_guard, executor
66 : */
67 : template<executor Executor>
68 : class executor_work_guard
69 : {
70 : Executor ex_;
71 : bool owns_;
72 :
73 : public:
74 : /** The underlying executor type. */
75 : using executor_type = Executor;
76 :
77 : /** Construct a work guard.
78 :
79 : Calls `ex.on_work_started()` to inform the executor that
80 : work is outstanding.
81 :
82 : @par Exception Safety
83 : No-throw guarantee.
84 :
85 : @par Postconditions
86 : @li `owns_work() == true`
87 : @li `get_executor() == ex`
88 :
89 : @param ex The executor to hold work on. Moved into the guard.
90 : */
91 : explicit
92 9 : executor_work_guard(Executor ex) noexcept
93 9 : : ex_(std::move(ex))
94 9 : , owns_(true)
95 : {
96 9 : ex_.on_work_started();
97 9 : }
98 :
99 : /** Copy constructor.
100 :
101 : Creates a new work guard holding work on the same executor.
102 : Calls `on_work_started()` on the executor.
103 :
104 : @par Exception Safety
105 : No-throw guarantee.
106 :
107 : @par Postconditions
108 : @li `owns_work() == other.owns_work()`
109 : @li `get_executor() == other.get_executor()`
110 :
111 : @param other The work guard to copy from.
112 : */
113 2 : executor_work_guard(executor_work_guard const& other) noexcept
114 2 : : ex_(other.ex_)
115 2 : , owns_(other.owns_)
116 : {
117 2 : if(owns_)
118 1 : ex_.on_work_started();
119 2 : }
120 :
121 : /** Move constructor.
122 :
123 : Transfers work ownership from `other` to `*this`. Does not
124 : call `on_work_started()` or `on_work_finished()`.
125 :
126 : @par Exception Safety
127 : No-throw guarantee.
128 :
129 : @par Postconditions
130 : @li `owns_work()` equals the prior value of `other.owns_work()`
131 : @li `other.owns_work() == false`
132 :
133 : @param other The work guard to move from.
134 : */
135 1 : executor_work_guard(executor_work_guard&& other) noexcept
136 1 : : ex_(std::move(other.ex_))
137 1 : , owns_(other.owns_)
138 : {
139 1 : other.owns_ = false;
140 1 : }
141 :
142 : /** Destructor.
143 :
144 : If `owns_work()` is `true`, calls `on_work_finished()` on
145 : the executor.
146 :
147 : @par Exception Safety
148 : No-throw guarantee.
149 : */
150 12 : ~executor_work_guard()
151 : {
152 12 : if(owns_)
153 7 : ex_.on_work_finished();
154 12 : }
155 :
156 : executor_work_guard& operator=(executor_work_guard const&) = delete;
157 :
158 : /** Return the underlying executor.
159 :
160 : @par Exception Safety
161 : No-throw guarantee.
162 :
163 : @return A copy of the stored executor.
164 : */
165 : executor_type
166 5 : get_executor() const noexcept
167 : {
168 5 : return ex_;
169 : }
170 :
171 : /** Return whether the guard owns work.
172 :
173 : @par Exception Safety
174 : No-throw guarantee.
175 :
176 : @return `true` if this guard will call `on_work_finished()`
177 : on destruction, `false` otherwise.
178 : */
179 : bool
180 12 : owns_work() const noexcept
181 : {
182 12 : return owns_;
183 : }
184 :
185 : /** Release ownership of the work.
186 :
187 : If `owns_work()` is `true`, calls `on_work_finished()` on
188 : the executor and sets ownership to `false`. Otherwise, has
189 : no effect.
190 :
191 : @par Exception Safety
192 : No-throw guarantee.
193 :
194 : @par Postconditions
195 : @li `owns_work() == false`
196 : */
197 : void
198 4 : reset() noexcept
199 : {
200 4 : if(owns_)
201 : {
202 3 : ex_.on_work_finished();
203 3 : owns_ = false;
204 : }
205 4 : }
206 : };
207 :
208 : //------------------------------------------------
209 :
210 : /** Create a work guard from an executor.
211 :
212 : @par Exception Safety
213 : No-throw guarantee.
214 :
215 : @param ex The executor to create the guard for.
216 :
217 : @return An `executor_work_guard` holding work on `ex`.
218 :
219 : @see executor_work_guard
220 : */
221 : template<executor Executor>
222 : executor_work_guard<Executor>
223 1 : make_work_guard(Executor ex)
224 : {
225 1 : return executor_work_guard<Executor>(std::move(ex));
226 : }
227 :
228 : } // capy
229 : } // boost
230 :
231 : #endif
|