• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2023-2025 Huawei Device Co., Ltd.
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 #ifndef PANDA_RUNTIME_COROUTINES_STACKFUL_COROUTINE_H
16 #define PANDA_RUNTIME_COROUTINES_STACKFUL_COROUTINE_H
17 
18 #include "runtime/fibers/fiber_context.h"
19 #include "runtime/coroutines/coroutine_context.h"
20 #include "runtime/include/panda_vm.h"
21 #include "runtime/coroutines/stackful_common.h"
22 #include "runtime/coroutines/stackful_coroutine_worker.h"
23 
24 namespace ark {
25 
26 /**
27  * @brief Native context of a coroutine. "Fiber"-based implementation.
28  *
29  * This implementation uses panda fibers library to implement native coroutine context.
30  */
31 class StackfulCoroutineContext : public CoroutineContext {
32 public:
33     NO_COPY_SEMANTIC(StackfulCoroutineContext);
34     NO_MOVE_SEMANTIC(StackfulCoroutineContext);
35 
36     /**
37      * @param stack specifies the lowest address of the stack region to use;
38      * it should have at least @param stack_size_bytes bytes accessible. If the stack grows down on the
39      * target architecture, then the initial stack pointer of the coroutine will be set to
40      * (stack + stack_size_bytes)
41      */
42     explicit StackfulCoroutineContext(uint8_t *stack, size_t stackSizeBytes);
43     ~StackfulCoroutineContext() override;
44 
45     /**
46      * Prepares the context for execution, links it to the managed context part (Coroutine instance) and registers the
47      * created coroutine in the CoroutineManager (in the RUNNABLE status)
48      */
49     void AttachToCoroutine(Coroutine *co) override;
50     /**
51      * Manually destroys the context. Should be called by the Coroutine instance as a part of main coroutine
52      * destruction. All other coroutines and their contexts are destroyed by the CoroutineManager once the coroutine
53      * entrypoint execution finishes
54      */
55     void Destroy() override;
56 
57     void CleanUp() override;
58 
59     bool RetrieveStackInfo(void *&stackAddr, size_t &stackSize, size_t &guardSize) override;
60 
61     /**
62      * Suspends the execution context, sets its status to either Status::RUNNABLE or Status::BLOCKED, depending on the
63      * suspend reason.
64      */
65     void RequestSuspend(bool getsBlocked) override;
66     /// Resumes the suspended context, sets status to RUNNING.
67     void RequestResume() override;
68     /// Unblock the coroutine and set its status to Status::RUNNABLE
69     void RequestUnblock() override;
70 
71     // should be called then the main thread is done executing the program entrypoint
72     void MainThreadFinished();
73     /// Moves the main coroutine to Status::AWAIT_LOOP
74     void EnterAwaitLoop();
75 
76     /// Coroutine status is a part of native context, because it might require some synchronization on access
77     Coroutine::Status GetStatus() const override;
78 
79     /**
80      * Transfer control to the target context
81      * NB: this method will return only after the control is transferred back to the caller context
82      */
83     bool SwitchTo(StackfulCoroutineContext *target);
84 
85     /// @return the lowest address of the coroutine native stack (provided in the ctx contructor)
GetStackLoAddrPtr()86     uint8_t *GetStackLoAddrPtr() const
87     {
88         return stack_;
89     }
90 
91     /// Executes a foreign lambda function within this context (does not corrupt the saved context)
92     template <class L>
ExecuteOnThisContext(L * lambda,StackfulCoroutineContext * requester)93     bool ExecuteOnThisContext(L *lambda, StackfulCoroutineContext *requester)
94     {
95         ASSERT_PRINT(false, "This method should not be called");
96         ASSERT(requester != nullptr);
97         return rpcCallContext_.Execute(lambda, &requester->context_, &context_);
98     }
99 
100     /// get the currently assigned worker thread
GetWorker()101     StackfulCoroutineWorker *GetWorker() const
102     {
103         auto *coro = GetCoroutine();
104         ASSERT(coro != nullptr);
105         return reinterpret_cast<StackfulCoroutineWorker *>(coro->GetWorker());
106     }
107 
108     /// @return current coroutine's affinity bits
GetAffinityMask()109     stackful_coroutines::AffinityMask GetAffinityMask() const
110     {
111         return affinityMask_;
112     }
113 
SetAffinityMask(stackful_coroutines::AffinityMask mask)114     void SetAffinityMask(stackful_coroutines::AffinityMask mask)
115     {
116         affinityMask_ = mask;
117     }
118 
119     /// @return true if migration from worker is allowed
IsMigrationAllowed()120     bool IsMigrationAllowed() const
121     {
122         std::bitset<stackful_coroutines::MAX_WORKERS_COUNT> mask(affinityMask_);
123         return mask.count() > 1;
124     }
125 
126 protected:
127     void SetStatus(Coroutine::Status newStatus) override;
128     StackfulCoroutineManager *GetManager() const;
129 
130 private:
131     void ThreadProcImpl();
132     static void CoroThreadProc(void *ctx);
133 
134     /// @brief The remote lambda call functionality implementation.
135     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
136     class RemoteCall {
137     public:
138         NO_COPY_SEMANTIC(RemoteCall);
139         NO_MOVE_SEMANTIC(RemoteCall);
140 
141         template <class L>
Execute(L * lambda,fibers::FiberContext * requesterContextPtr,fibers::FiberContext * hostContextPtr)142         bool Execute(L *lambda, fibers::FiberContext *requesterContextPtr, fibers::FiberContext *hostContextPtr)
143         {
144             ASSERT(Coroutine::GetCurrent()->GetVM()->GetThreadManager()->GetMainThread() !=
145                    ManagedThread::GetCurrent());
146 
147             callInProgress_ = true;
148             requesterContextPtr_ = requesterContextPtr;
149             lambda_ = lambda;
150 
151             fibers::CopyContext(&guestContext_, hostContextPtr);
152             fibers::UpdateContextKeepStack(&guestContext_, RemoteCall::Proxy<L>, this);
153             fibers::SwitchContext(requesterContextPtr_, &guestContext_);
154 
155             return true;
156         }
157 
158         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
159         RemoteCall() = default;
160         ~RemoteCall() = default;
161 
162     private:
163         template <class L>
Proxy(void * ctx)164         static void Proxy(void *ctx)
165         {
166             auto *thisInstance = static_cast<RemoteCall *>(ctx);
167             ASSERT(thisInstance->callInProgress_);
168 
169             (*static_cast<L *>(thisInstance->lambda_))();
170 
171             thisInstance->callInProgress_ = false;
172             fibers::SwitchContext(&thisInstance->guestContext_, thisInstance->requesterContextPtr_);
173         }
174 
175         bool callInProgress_ = false;
176         fibers::FiberContext *requesterContextPtr_ = nullptr;
177         fibers::FiberContext guestContext_;
178         void *lambda_ = nullptr;
179     } rpcCallContext_;
180 
181     uint8_t *stack_ = nullptr;
182     size_t stackSizeBytes_ = 0;
183     fibers::FiberContext context_;
184     Coroutine::Status status_ {Coroutine::Status::CREATED};
185     stackful_coroutines::AffinityMask affinityMask_ = stackful_coroutines::AFFINITY_MASK_NONE;
186 #if defined(PANDA_TSAN_ON)
187     void *tsanFiberCtx_ {nullptr};
188 #endif /* PANDA_TSAN_ON */
189 };
190 
191 }  // namespace ark
192 
193 #endif  // PANDA_RUNTIME_COROUTINES_STACKFUL_COROUTINE_H
194