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