1 /** 2 * Copyright (c) 2023-2024 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 = default; 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(requester != nullptr); 96 return rpcCallContext_.Execute(lambda, &requester->context_, &context_); 97 } 98 99 /// assign this coroutine to a worker thread SetWorker(StackfulCoroutineWorker * w)100 void SetWorker(StackfulCoroutineWorker *w) 101 { 102 worker_ = w; 103 } 104 105 /// get the currently assigned worker thread GetWorker()106 StackfulCoroutineWorker *GetWorker() const 107 { 108 return worker_; 109 } 110 111 /// @return current coroutine's affinity bits GetAffinityMask()112 stackful_coroutines::AffinityMask GetAffinityMask() const 113 { 114 return affinityMask_; 115 } 116 SetAffinityMask(stackful_coroutines::AffinityMask mask)117 void SetAffinityMask(stackful_coroutines::AffinityMask mask) 118 { 119 affinityMask_ = mask; 120 } 121 122 protected: 123 void SetStatus(Coroutine::Status newStatus) override; 124 125 private: 126 void ThreadProcImpl(); 127 static void CoroThreadProc(void *ctx); 128 129 /// @brief The remote lambda call functionality implementation. 130 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) 131 class RemoteCall { 132 public: 133 NO_COPY_SEMANTIC(RemoteCall); 134 NO_MOVE_SEMANTIC(RemoteCall); 135 136 template <class L> Execute(L * lambda,fibers::FiberContext * requesterContextPtr,fibers::FiberContext * hostContextPtr)137 bool Execute(L *lambda, fibers::FiberContext *requesterContextPtr, fibers::FiberContext *hostContextPtr) 138 { 139 ASSERT(Coroutine::GetCurrent()->GetVM()->GetThreadManager()->GetMainThread() != 140 ManagedThread::GetCurrent()); 141 142 callInProgress_ = true; 143 requesterContextPtr_ = requesterContextPtr; 144 lambda_ = lambda; 145 146 fibers::CopyContext(&guestContext_, hostContextPtr); 147 fibers::UpdateContextKeepStack(&guestContext_, RemoteCall::Proxy<L>, this); 148 fibers::SwitchContext(requesterContextPtr_, &guestContext_); 149 150 return true; 151 } 152 153 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) 154 RemoteCall() = default; 155 ~RemoteCall() = default; 156 157 private: 158 template <class L> Proxy(void * ctx)159 static void Proxy(void *ctx) 160 { 161 auto *thisInstance = static_cast<RemoteCall *>(ctx); 162 ASSERT(thisInstance->callInProgress_); 163 164 (*static_cast<L *>(thisInstance->lambda_))(); 165 166 thisInstance->callInProgress_ = false; 167 fibers::SwitchContext(&thisInstance->guestContext_, thisInstance->requesterContextPtr_); 168 } 169 170 bool callInProgress_ = false; 171 fibers::FiberContext *requesterContextPtr_ = nullptr; 172 fibers::FiberContext guestContext_; 173 void *lambda_ = nullptr; 174 } rpcCallContext_; 175 176 uint8_t *stack_ = nullptr; 177 size_t stackSizeBytes_ = 0; 178 fibers::FiberContext context_; 179 Coroutine::Status status_ {Coroutine::Status::CREATED}; 180 StackfulCoroutineWorker *worker_ = nullptr; 181 stackful_coroutines::AffinityMask affinityMask_ = stackful_coroutines::AFFINITY_MASK_NONE; 182 }; 183 184 } // namespace ark 185 186 #endif // PANDA_RUNTIME_COROUTINES_STACKFUL_COROUTINE_H 187