• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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