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