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