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