1 -
//
 
2 -
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.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 Ex A type satisfying the Executor concept.
 
64 -

 
65 -
    @see make_work_guard, Executor
 
66 -
*/
 
67 -
template<Executor Ex>
 
68 -
class executor_work_guard
 
69 -
{
 
70 -
    Ex ex_;
 
71 -
    bool owns_;
 
72 -

 
73 -
public:
 
74 -
    /** The underlying executor type. */
 
75 -
    using executor_type = Ex;
 
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 -
    executor_work_guard(Ex ex) noexcept
 
93 -
        : ex_(std::move(ex))
 
94 -
        , owns_(true)
 
95 -
    {
 
96 -
        ex_.on_work_started();
 
97 -
    }
 
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 -
    executor_work_guard(executor_work_guard const& other) noexcept
 
114 -
        : ex_(other.ex_)
 
115 -
        , owns_(other.owns_)
 
116 -
    {
 
117 -
        if(owns_)
 
118 -
            ex_.on_work_started();
 
119 -
    }
 
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 -
    executor_work_guard(executor_work_guard&& other) noexcept
 
136 -
        : ex_(std::move(other.ex_))
 
137 -
        , owns_(other.owns_)
 
138 -
    {
 
139 -
        other.owns_ = false;
 
140 -
    }
 
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 -
    ~executor_work_guard()
 
151 -
    {
 
152 -
        if(owns_)
 
153 -
            ex_.on_work_finished();
 
154 -
    }
 
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 -
    get_executor() const noexcept
 
167 -
    {
 
168 -
        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 -
    owns_work() const noexcept
 
181 -
    {
 
182 -
        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 -
    reset() noexcept
 
199 -
    {
 
200 -
        if(owns_)
 
201 -
        {
 
202 -
            ex_.on_work_finished();
 
203 -
            owns_ = false;
 
204 -
        }
 
205 -
    }
 
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 Ex>
 
222 -
executor_work_guard<Ex>
 
223 -
make_work_guard(Ex ex)
 
224 -
{
 
225 -
    return executor_work_guard<Ex>(std::move(ex));
 
226 -
}
 
227 -

 
228 -
} // capy
 
229 -
} // boost
 
230 -

 
231 -
#endif