Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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_RUN_SYNC_HPP
11 : #define BOOST_CAPY_RUN_SYNC_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/capy/ex/any_coro.hpp>
15 : #include <boost/capy/ex/execution_context.hpp>
16 : #include <boost/capy/task.hpp>
17 :
18 : #include <coroutine>
19 : #include <exception>
20 : #include <type_traits>
21 : #include <utility>
22 :
23 : namespace boost {
24 : namespace capy {
25 :
26 : namespace detail {
27 :
28 : /** Trivial execution context for synchronous execution. */
29 : class sync_context : public execution_context
30 : {
31 : };
32 :
33 : /** Trivial executor for synchronous execution.
34 :
35 : Returns the coroutine handle directly for symmetric transfer,
36 : enabling inline execution without scheduling.
37 : */
38 : struct sync_executor
39 : {
40 : static sync_context ctx_;
41 :
42 0 : bool operator==(sync_executor const&) const noexcept { return true; }
43 0 : execution_context& context() const noexcept { return ctx_; }
44 0 : void on_work_started() const noexcept {}
45 0 : void on_work_finished() const noexcept {}
46 :
47 23 : any_coro dispatch(any_coro h) const
48 : {
49 23 : return h;
50 : }
51 :
52 0 : void post(any_coro h) const
53 : {
54 0 : h.resume();
55 0 : }
56 : };
57 :
58 : inline sync_context sync_executor::ctx_;
59 :
60 : /** Synchronous task runner.
61 :
62 : Runs a coroutine task to completion on the caller's thread,
63 : returning the result directly or rethrowing any exception.
64 :
65 : This class is not intended for direct use. Use the `run_sync()`
66 : factory function instead.
67 :
68 : @par Thread Safety
69 : Not thread-safe. The task runs entirely on the calling thread.
70 :
71 : @see run_sync
72 : */
73 : class sync_runner
74 : {
75 : public:
76 : sync_runner() = default;
77 :
78 : sync_runner(sync_runner const&) = delete;
79 : sync_runner& operator=(sync_runner const&) = delete;
80 : sync_runner(sync_runner&&) = default;
81 : sync_runner& operator=(sync_runner&&) = default;
82 :
83 : /** Run a task to completion and return the result.
84 :
85 : Executes the task synchronously on the calling thread. The task
86 : runs to completion before this function returns.
87 :
88 : @par Exception Safety
89 : If the task throws an exception, it is rethrown to the caller.
90 :
91 : @param t The task to execute.
92 :
93 : @return The value returned by the task.
94 :
95 : @throws Any exception thrown by the task.
96 : */
97 : template<typename T>
98 23 : T operator()(task<T> t) &&
99 : {
100 23 : auto h = t.release();
101 : sync_executor ex;
102 :
103 23 : h.promise().continuation_ = std::noop_coroutine();
104 23 : h.promise().ex_ = ex;
105 23 : h.promise().caller_ex_ = ex;
106 23 : h.promise().needs_dispatch_ = false;
107 :
108 23 : ex.dispatch(any_coro{h}).resume();
109 :
110 23 : std::exception_ptr ep = h.promise().ep_;
111 :
112 : if constexpr (std::is_void_v<T>)
113 : {
114 7 : h.destroy();
115 7 : if (ep)
116 2 : std::rethrow_exception(ep);
117 : }
118 : else
119 : {
120 16 : if (ep)
121 : {
122 3 : h.destroy();
123 6 : std::rethrow_exception(ep);
124 : }
125 13 : auto& result_base = static_cast<detail::task_return_base<T>&>(
126 13 : h.promise());
127 13 : auto result = std::move(*result_base.result_);
128 13 : h.destroy();
129 15 : return result;
130 2 : }
131 23 : }
132 : };
133 :
134 : } // namespace detail
135 :
136 : /** Create a synchronous task runner.
137 :
138 : Returns a runner that executes a coroutine task to completion
139 : on the caller's thread. The task completes before this function
140 : returns, and the result is returned directly.
141 :
142 : @par Usage
143 : @code
144 : // Run a task and get the result
145 : int value = run_sync()(compute_value());
146 :
147 : // Run a void task
148 : run_sync()(do_work());
149 :
150 : // Exceptions propagate normally
151 : try {
152 : run_sync()(failing_task());
153 : } catch (std::exception const& e) {
154 : // handle error
155 : }
156 : @endcode
157 :
158 : @par Thread Safety
159 : The task runs entirely on the calling thread. No executor or
160 : execution context is required.
161 :
162 : @return A runner object with `operator()(task<T>)` that returns `T`.
163 :
164 : @see task
165 : @see run_async
166 : @see run_on
167 : */
168 : inline
169 : detail::sync_runner
170 23 : run_sync()
171 : {
172 23 : return detail::sync_runner{};
173 : }
174 :
175 : } // namespace capy
176 : } // namespace boost
177 :
178 : #endif
|