LCOV - code coverage report
Current view: top level - include/hpactor/sched - coroutine_task.hpp (source / functions) Coverage Total Hit
Test: HPActor Coverage Lines: 97.2 % 36 35
Test Date: 2026-05-20 02:24:49 Functions: 100.0 % 14 14
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             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
        

Generated by: LCOV version 2.0-1