Branch data Line data Source code
1 : : // Copyright 2026 HPActor Contributors
2 : : //
3 : : // Licensed under the Apache License, Version 2.0 (the "License");
4 : : // you may not use this file except in compliance with the License.
5 : : // You may obtain a copy of the License at
6 : : //
7 : : // http://www.apache.org/licenses/LICENSE-2.0
8 : : //
9 : : // Unless required by applicable law or agreed to in writing, software
10 : : // distributed under the License is distributed on an "AS IS" BASIS,
11 : : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : // See the License for the specific language governing permissions and
13 : : // limitations under the License.
14 : :
15 : : #pragma once
16 : :
17 : : #include <hpactor/actor/actor_state.hpp>
18 : : #include <hpactor/hpactor_config.hpp>
19 : : #include <hpactor/types/types.hpp>
20 : :
21 : : #include <atomic>
22 : : #include <cstdint>
23 : : #include <thread>
24 : :
25 : : #if HPACTOR_SUPPORT_COROUTINES
26 : : # include <coroutine>
27 : : #endif
28 : :
29 : : namespace hpactor::sched {
30 : :
31 : : // Forward declarations
32 : : class ActorCoroutine;
33 : : class WorkerThread;
34 : : class CoroutineTask;
35 : :
36 : : #if HPACTOR_SUPPORT_COROUTINES
37 : :
38 : : // CoroutinePromise: promise_type for actor coroutines
39 : : // Controls lifecycle: initial_suspend → Running → (Suspend | Terminate)
40 : : struct CoroutinePromise {
41 : : using handle_type = std::coroutine_handle<CoroutinePromise>;
42 : :
43 : : ActorId actor_id;
44 : : ActorState state; // owned fallback (used when not wired to actor)
45 : : ActorState* actor_state{&state}; // active state — may point to
46 : : // EventBasedActor::actor_state_
47 : : WorkerThread* owner{nullptr};
48 : :
49 : : // Mailbox integration
50 : : void* mailbox{nullptr}; // MPSCMailbox<MessageNode>*
51 : : std::atomic<bool> mailbox_was_empty{true};
52 : :
53 : : // Continuation for chained awaiters
54 : : std::coroutine_handle<> continuation;
55 : :
56 : 18 : CoroutinePromise() = default;
57 : : ~CoroutinePromise() = default;
58 : :
59 : : // Start suspended — scheduler decides when to resume
60 : 13 : std::suspend_always initial_suspend() noexcept {
61 : 13 : return {};
62 : : }
63 : :
64 : : // Final suspend — keep coroutine frame alive for restart.
65 : : // Actor runtime calls resume() again to re-enter act().
66 : 5 : std::suspend_always final_suspend() noexcept {
67 : 5 : return {};
68 : : }
69 : :
70 : : // Called on co_return
71 : 5 : void return_void() noexcept {
72 : 5 : actor_state->set(ActorState::kTerminated);
73 : 5 : }
74 : :
75 : : // Called on unhandled exception
76 : : void unhandled_exception() noexcept {
77 : : actor_state->set(ActorState::kTerminated);
78 : : }
79 : :
80 : : CoroutineTask get_return_object();
81 : :
82 : : // State access
83 : : void set_running() {
84 : : actor_state->set(ActorState::kRunning);
85 : : }
86 : : void set_idle() {
87 : : actor_state->set(ActorState::kIdle);
88 : : }
89 : : void set_ready() {
90 : : actor_state->set(ActorState::kReady);
91 : : }
92 : : void set_io_waiting() {
93 : : actor_state->set(ActorState::kIOWaiting);
94 : : }
95 : : void set_terminated() {
96 : : actor_state->set(ActorState::kTerminated);
97 : : }
98 : :
99 : : bool is_idle() const {
100 : : return actor_state->is_idle();
101 : : }
102 : : bool is_running() const {
103 : : return actor_state->is_running();
104 : : }
105 : : bool is_terminated() const {
106 : : return actor_state->is_terminated();
107 : : }
108 : :
109 : : // Called by MPSCActorMailbox when a message arrives while actor is idle.
110 : : // If actor is suspended (waiting in MailboxAwaiter), resume it.
111 : : void notify_mailbox_nonempty() {
112 : : if (continuation && !continuation.done()) {
113 : : continuation.resume();
114 : : }
115 : : }
116 : : };
117 : :
118 : : // CoroutineTask: return type of actor coroutines
119 : : // Wraps std::coroutine_handle<CoroutinePromise> and manages actor lifecycle
120 : : class CoroutineTask {
121 : : public:
122 : : using handle_type = std::coroutine_handle<CoroutinePromise>;
123 : :
124 : 121 : CoroutineTask() noexcept : handle_(nullptr) {}
125 : 13 : explicit CoroutineTask(handle_type handle) noexcept : handle_(handle) {}
126 : :
127 : 19 : CoroutineTask(CoroutineTask&& other) noexcept : handle_(other.handle_) {
128 : 19 : other.handle_ = nullptr;
129 : 19 : }
130 : :
131 : : CoroutineTask(const CoroutineTask&) = delete;
132 : : CoroutineTask& operator=(const CoroutineTask&) = delete;
133 : :
134 : 11 : CoroutineTask& operator=(CoroutineTask&& other) noexcept {
135 : 11 : if (this != &other) {
136 : 11 : if (handle_)
137 : 0 : handle_.destroy();
138 : 11 : handle_ = other.handle_;
139 : 11 : other.handle_ = nullptr;
140 : : }
141 : 11 : return *this;
142 : : }
143 : :
144 : 153 : ~CoroutineTask() {
145 : 153 : if (handle_)
146 : 13 : handle_.destroy();
147 : 153 : }
148 : :
149 : 59 : explicit operator bool() const noexcept {
150 : 59 : return handle_ != nullptr;
151 : : }
152 : :
153 : 20 : handle_type handle() const noexcept {
154 : 20 : return handle_;
155 : : }
156 : :
157 : : // Resume the coroutine
158 : 12 : void resume() {
159 : 12 : if (handle_ && !handle_.done()) {
160 : 12 : handle_.resume();
161 : : }
162 : 12 : }
163 : :
164 : : // Check if done
165 : 26 : bool done() const {
166 : 26 : return !handle_ || handle_.done();
167 : : }
168 : :
169 : : private:
170 : : handle_type handle_;
171 : : };
172 : :
173 : : } // namespace hpactor::sched
174 : :
175 : : // Specialize std::coroutine_traits so CoroutineTask can be used as a
176 : : // coroutine return type.
177 : : template <> struct std::coroutine_traits<hpactor::sched::CoroutineTask> {
178 : : using promise_type = hpactor::sched::CoroutinePromise;
179 : : };
180 : :
181 : : template <typename T>
182 : : struct std::coroutine_traits<hpactor::sched::CoroutineTask, T&> {
183 : : using promise_type = hpactor::sched::CoroutinePromise;
184 : : };
185 : :
186 : : template <typename T>
187 : : struct std::coroutine_traits<hpactor::sched::CoroutineTask, const T&> {
188 : : using promise_type = hpactor::sched::CoroutinePromise;
189 : : };
190 : :
191 : : namespace hpactor::sched {
192 : :
193 : : // Out-of-line definition for get_return_object
194 : 13 : inline CoroutineTask CoroutinePromise::get_return_object() {
195 : 13 : return CoroutineTask{handle_type::from_promise(*this)};
196 : : }
197 : :
198 : : #else // !HPACTOR_SUPPORT_COROUTINES
199 : :
200 : : // C++17 fallback: stub CoroutinePromise and CoroutineTask so that
201 : : // the rest of the type system (ActorState, ActorId, etc.) still compiles.
202 : : struct CoroutinePromise {
203 : : ActorId actor_id;
204 : : ActorState state;
205 : : WorkerThread* owner{nullptr};
206 : : void* mailbox{nullptr};
207 : : std::atomic<bool> mailbox_was_empty{true};
208 : :
209 : : CoroutinePromise() = default;
210 : : ~CoroutinePromise() = default;
211 : :
212 : : void set_running() {
213 : : state.set(ActorState::kRunning);
214 : : }
215 : : void set_idle() {
216 : : state.set(ActorState::kIdle);
217 : : }
218 : : void set_ready() {
219 : : state.set(ActorState::kReady);
220 : : }
221 : : void set_io_waiting() {
222 : : state.set(ActorState::kIOWaiting);
223 : : }
224 : : void set_terminated() {
225 : : state.set(ActorState::kTerminated);
226 : : }
227 : : bool is_idle() const {
228 : : return state.is_idle();
229 : : }
230 : : bool is_running() const {
231 : : return state.is_running();
232 : : }
233 : : bool is_terminated() const {
234 : : return state.is_terminated();
235 : : }
236 : : void notify_mailbox_nonempty() {}
237 : : };
238 : :
239 : : class CoroutineTask {
240 : : public:
241 : : CoroutineTask() noexcept {}
242 : : explicit CoroutineTask(std::nullptr_t) noexcept {}
243 : :
244 : : CoroutineTask(CoroutineTask&& /*other*/) noexcept {}
245 : : CoroutineTask& operator=(CoroutineTask&& /*other*/) noexcept {
246 : : return *this;
247 : : }
248 : :
249 : : CoroutineTask(const CoroutineTask&) = delete;
250 : : CoroutineTask& operator=(const CoroutineTask&) = delete;
251 : :
252 : : ~CoroutineTask() = default;
253 : :
254 : : explicit operator bool() const noexcept {
255 : : return false;
256 : : }
257 : : bool done() const noexcept {
258 : : return true;
259 : : }
260 : : void resume() {}
261 : : };
262 : :
263 : : #endif // HPACTOR_SUPPORT_COROUTINES
264 : :
265 : : } // namespace hpactor::sched
|