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