1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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_EXECUTOR_WORK_GUARD_HPP
10  
#ifndef BOOST_CAPY_EXECUTOR_WORK_GUARD_HPP
11  
#define BOOST_CAPY_EXECUTOR_WORK_GUARD_HPP
11  
#define BOOST_CAPY_EXECUTOR_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  
    @tparam Ex A type satisfying the Executor concept.
63  
    @tparam Ex A type satisfying the Executor concept.
64  

64  

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

72  

73  
public:
73  
public:
74  
    /** The underlying executor type. */
74  
    /** The underlying executor type. */
75  
    using executor_type = Ex;
75  
    using executor_type = Ex;
76  

76  

77  
    /** Construct a work guard.
77  
    /** Construct a work guard.
78  

78  

79  
        Calls `ex.on_work_started()` to inform the executor that
79  
        Calls `ex.on_work_started()` to inform the executor that
80  
        work is outstanding.
80  
        work is outstanding.
81  

81  

82  
        @par Exception Safety
82  
        @par Exception Safety
83  
        No-throw guarantee.
83  
        No-throw guarantee.
84  

84  

85  
        @par Postconditions
85  
        @par Postconditions
86  
        @li `owns_work() == true`
86  
        @li `owns_work() == true`
87  
        @li `get_executor() == ex`
87  
        @li `get_executor() == ex`
88  

88  

89  
        @param ex The executor to hold work on. Moved into the guard.
89  
        @param ex The executor to hold work on. Moved into the guard.
90  
    */
90  
    */
91  
    explicit
91  
    explicit
92  
    executor_work_guard(Ex ex) noexcept
92  
    executor_work_guard(Ex ex) noexcept
93  
        : ex_(std::move(ex))
93  
        : ex_(std::move(ex))
94  
        , owns_(true)
94  
        , owns_(true)
95  
    {
95  
    {
96  
        ex_.on_work_started();
96  
        ex_.on_work_started();
97  
    }
97  
    }
98  

98  

99  
    /** Copy constructor.
99  
    /** Copy constructor.
100  

100  

101  
        Creates a new work guard holding work on the same executor.
101  
        Creates a new work guard holding work on the same executor.
102  
        Calls `on_work_started()` on the executor.
102  
        Calls `on_work_started()` on the executor.
103  

103  

104  
        @par Exception Safety
104  
        @par Exception Safety
105  
        No-throw guarantee.
105  
        No-throw guarantee.
106  

106  

107  
        @par Postconditions
107  
        @par Postconditions
108  
        @li `owns_work() == other.owns_work()`
108  
        @li `owns_work() == other.owns_work()`
109  
        @li `get_executor() == other.get_executor()`
109  
        @li `get_executor() == other.get_executor()`
110  

110  

111  
        @param other The work guard to copy from.
111  
        @param other The work guard to copy from.
112  
    */
112  
    */
113  
    executor_work_guard(executor_work_guard const& other) noexcept
113  
    executor_work_guard(executor_work_guard const& other) noexcept
114  
        : ex_(other.ex_)
114  
        : ex_(other.ex_)
115  
        , owns_(other.owns_)
115  
        , owns_(other.owns_)
116  
    {
116  
    {
117  
        if(owns_)
117  
        if(owns_)
118  
            ex_.on_work_started();
118  
            ex_.on_work_started();
119  
    }
119  
    }
120  

120  

121  
    /** Move constructor.
121  
    /** Move constructor.
122  

122  

123  
        Transfers work ownership from `other` to `*this`. Does not
123  
        Transfers work ownership from `other` to `*this`. Does not
124  
        call `on_work_started()` or `on_work_finished()`.
124  
        call `on_work_started()` or `on_work_finished()`.
125  

125  

126  
        @par Exception Safety
126  
        @par Exception Safety
127  
        No-throw guarantee.
127  
        No-throw guarantee.
128  

128  

129  
        @par Postconditions
129  
        @par Postconditions
130  
        @li `owns_work()` equals the prior value of `other.owns_work()`
130  
        @li `owns_work()` equals the prior value of `other.owns_work()`
131  
        @li `other.owns_work() == false`
131  
        @li `other.owns_work() == false`
132  

132  

133  
        @param other The work guard to move from.
133  
        @param other The work guard to move from.
134  
    */
134  
    */
135  
    executor_work_guard(executor_work_guard&& other) noexcept
135  
    executor_work_guard(executor_work_guard&& other) noexcept
136  
        : ex_(std::move(other.ex_))
136  
        : ex_(std::move(other.ex_))
137  
        , owns_(other.owns_)
137  
        , owns_(other.owns_)
138  
    {
138  
    {
139  
        other.owns_ = false;
139  
        other.owns_ = false;
140  
    }
140  
    }
141  

141  

142  
    /** Destructor.
142  
    /** Destructor.
143  

143  

144  
        If `owns_work()` is `true`, calls `on_work_finished()` on
144  
        If `owns_work()` is `true`, calls `on_work_finished()` on
145  
        the executor.
145  
        the executor.
146  

146  

147  
        @par Exception Safety
147  
        @par Exception Safety
148  
        No-throw guarantee.
148  
        No-throw guarantee.
149  
    */
149  
    */
150  
    ~executor_work_guard()
150  
    ~executor_work_guard()
151  
    {
151  
    {
152  
        if(owns_)
152  
        if(owns_)
153  
            ex_.on_work_finished();
153  
            ex_.on_work_finished();
154  
    }
154  
    }
155  

155  

156  
    executor_work_guard& operator=(executor_work_guard const&) = delete;
156  
    executor_work_guard& operator=(executor_work_guard const&) = delete;
157  

157  

158  
    /** Return the underlying executor.
158  
    /** Return the underlying executor.
159  

159  

160  
        @par Exception Safety
160  
        @par Exception Safety
161  
        No-throw guarantee.
161  
        No-throw guarantee.
162  

162  

163  
        @return A copy of the stored executor.
163  
        @return A copy of the stored executor.
164  
    */
164  
    */
165  
    executor_type
165  
    executor_type
166  
    get_executor() const noexcept
166  
    get_executor() const noexcept
167  
    {
167  
    {
168  
        return ex_;
168  
        return ex_;
169  
    }
169  
    }
170  

170  

171  
    /** Return whether the guard owns work.
171  
    /** Return whether the guard owns work.
172  

172  

173  
        @par Exception Safety
173  
        @par Exception Safety
174  
        No-throw guarantee.
174  
        No-throw guarantee.
175  

175  

176  
        @return `true` if this guard will call `on_work_finished()`
176  
        @return `true` if this guard will call `on_work_finished()`
177  
            on destruction, `false` otherwise.
177  
            on destruction, `false` otherwise.
178  
    */
178  
    */
179  
    bool
179  
    bool
180  
    owns_work() const noexcept
180  
    owns_work() const noexcept
181  
    {
181  
    {
182  
        return owns_;
182  
        return owns_;
183  
    }
183  
    }
184  

184  

185  
    /** Release ownership of the work.
185  
    /** Release ownership of the work.
186  

186  

187  
        If `owns_work()` is `true`, calls `on_work_finished()` on
187  
        If `owns_work()` is `true`, calls `on_work_finished()` on
188  
        the executor and sets ownership to `false`. Otherwise, has
188  
        the executor and sets ownership to `false`. Otherwise, has
189  
        no effect.
189  
        no effect.
190  

190  

191  
        @par Exception Safety
191  
        @par Exception Safety
192  
        No-throw guarantee.
192  
        No-throw guarantee.
193  

193  

194  
        @par Postconditions
194  
        @par Postconditions
195  
        @li `owns_work() == false`
195  
        @li `owns_work() == false`
196  
    */
196  
    */
197  
    void
197  
    void
198  
    reset() noexcept
198  
    reset() noexcept
199  
    {
199  
    {
200  
        if(owns_)
200  
        if(owns_)
201  
        {
201  
        {
202  
            ex_.on_work_finished();
202  
            ex_.on_work_finished();
203  
            owns_ = false;
203  
            owns_ = false;
204  
        }
204  
        }
205  
    }
205  
    }
206  
};
206  
};
207  

207  

208  
//------------------------------------------------
208  
//------------------------------------------------
209  

209  

210  
/** Create a work guard from an executor.
210  
/** Create a work guard from an executor.
211  

211  

212  
    @par Exception Safety
212  
    @par Exception Safety
213  
    No-throw guarantee.
213  
    No-throw guarantee.
214  

214  

215  
    @param ex The executor to create the guard for.
215  
    @param ex The executor to create the guard for.
216  

216  

217  
    @return An `executor_work_guard` holding work on `ex`.
217  
    @return An `executor_work_guard` holding work on `ex`.
218  

218  

219  
    @see executor_work_guard
219  
    @see executor_work_guard
220  
*/
220  
*/
221  
template<Executor Ex>
221  
template<Executor Ex>
222  
executor_work_guard<Ex>
222  
executor_work_guard<Ex>
223  
make_work_guard(Ex ex)
223  
make_work_guard(Ex ex)
224  
{
224  
{
225  
    return executor_work_guard<Ex>(std::move(ex));
225  
    return executor_work_guard<Ex>(std::move(ex));
226  
}
226  
}
227  

227  

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

230  

231  
#endif
231  
#endif