• 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_MANAGER_H
16 #define PANDA_RUNTIME_COROUTINES_STACKFUL_COROUTINE_MANAGER_H
17 
18 #include "runtime/coroutines/coroutine_manager.h"
19 #include "runtime/coroutines/stackful_coroutine.h"
20 #include "runtime/coroutines/stackful_coroutine_worker.h"
21 
22 namespace panda {
23 
24 /**
25  * @brief Stackful ("fiber"-based) coroutine manager implementation.
26  *
27  * In this implementation coroutines are user-level threads ("fibers") with manually allocated stacks.
28  *
29  * For interface function descriptions see CoroutineManager class declaration.
30  */
31 class StackfulCoroutineManager : public CoroutineManager {
32 public:
33     NO_COPY_SEMANTIC(StackfulCoroutineManager);
34     NO_MOVE_SEMANTIC(StackfulCoroutineManager);
35 
StackfulCoroutineManager(CoroutineFactory factory)36     explicit StackfulCoroutineManager(CoroutineFactory factory) : CoroutineManager(factory) {}
37     ~StackfulCoroutineManager() override = default;
38 
39     /* CoroutineManager interfaces, see CoroutineManager class for the details */
40     void Initialize(CoroutineManagerConfig config, Runtime *runtime, PandaVM *vm) override;
41     void Finalize() override;
42     void RegisterCoroutine(Coroutine *co) override;
43     bool TerminateCoroutine(Coroutine *co) override;
44     Coroutine *Launch(CompletionEvent *completionEvent, Method *entrypoint, PandaVector<Value> &&arguments,
45                       CoroutineAffinity affinity) override;
46     void Schedule() override;
47     void Await(CoroutineEvent *awaitee) RELEASE(awaitee) override;
48     void UnblockWaiters(CoroutineEvent *blocker) override;
49 
50     /* ThreadManager interfaces, see ThreadManager class for the details */
51     void WaitForDeregistration() override;
52     void SuspendAllThreads() override;
53     void ResumeAllThreads() override;
54     bool IsRunningThreadExist() override;
55 
56     /**
57      * @brief Creates a coroutine instance with a native function as an entry point
58      * @param entry native function to execute
59      * @param param param to pass to the EP
60      */
61     Coroutine *CreateNativeCoroutine(Runtime *runtime, PandaVM *vm,
62                                      Coroutine::NativeEntrypointInfo::NativeEntrypointFunc entry, void *param,
63                                      PandaString name);
64     /// destroy the "native" coroutine created earlier
65     void DestroyNativeCoroutine(Coroutine *co);
66 
67     void DestroyEntrypointfulCoroutine(Coroutine *co) override;
68 
69     /// called when a coroutine worker thread ends its execution
70     void OnWorkerShutdown();
71     /// called when a coroutine worker thread starts its execution
72     void OnWorkerStartup();
73 
74     /* debugging tools */
75     /**
76      * For StackfulCoroutineManager implementation: a fatal error is issued if an attempt to switch coroutines on
77      * current worker is detected when coroutine switch is disabled.
78      */
79     void DisableCoroutineSwitch() override;
80     void EnableCoroutineSwitch() override;
81     bool IsCoroutineSwitchDisabled() override;
82 
83 protected:
84     bool EnumerateThreadsImpl(const ThreadManager::Callback &cb, unsigned int incMask,
85                               unsigned int xorMask) const override;
86     CoroutineContext *CreateCoroutineContext(bool coroHasEntrypoint) override;
87     void DeleteCoroutineContext(CoroutineContext *ctx) override;
88 
89     size_t GetCoroutineCount() override;
90     size_t GetCoroutineCountLimit() override;
91 
92     StackfulCoroutineContext *GetCurrentContext();
93     StackfulCoroutineWorker *GetCurrentWorker();
94     bool IsJsMode();
95 
96     /**
97      * @brief reuse a cached coroutine instance in case when coroutine pool is enabled
98      * see Coroutine::ReInitialize for details
99      */
100     void ReuseCoroutineInstance(Coroutine *co, CompletionEvent *completionEvent, Method *entrypoint,
101                                 PandaVector<Value> &&arguments, PandaString name);
102 
103 private:
104     StackfulCoroutineContext *CreateCoroutineContextImpl(bool needStack);
105     StackfulCoroutineWorker *ChooseWorkerForCoroutine(CoroutineAffinity affinity);
106 
107     Coroutine *LaunchImpl(CompletionEvent *completionEvent, Method *entrypoint, PandaVector<Value> &&arguments,
108                           CoroutineAffinity affinity);
109     /**
110      * Tries to extract a coroutine instance from the pool for further reuse, returns nullptr in case when it is not
111      * possible.
112      */
113     Coroutine *TryGetCoroutineFromPool();
114 
115     /* workers API */
116     /**
117      * @brief create the arbitrary number of worker threads
118      * @param how_many total number of worker threads, including MAIN
119      */
120     void CreateWorkers(uint32_t howMany, Runtime *runtime, PandaVM *vm) REQUIRES(workersLock_);
121 
122     /* coroutine registry management */
123     void AddToRegistry(Coroutine *co);
124     void RemoveFromRegistry(Coroutine *co) REQUIRES(coroListLock_);
125 
126     /// call to check if we are done executing managed code and set appropriate member flags
127     void CheckProgramCompletion();
128     /// call when main coroutine is done executing its managed EP
129     void MainCoroutineCompleted();
130     /// @return number of running worker loop coroutines
131     size_t GetActiveWorkersCount();
132 
133     /* resource management */
134     uint8_t *AllocCoroutineStack();
135     void FreeCoroutineStack(uint8_t *stack);
136 
137     // for thread safety with GC
138     mutable os::memory::Mutex coroListLock_;
139     // all registered coros
140     PandaSet<Coroutine *> coroutines_ GUARDED_BY(coroListLock_);
141 
142     // worker threads-related members
143     PandaVector<StackfulCoroutineWorker *> workers_ GUARDED_BY(workersLock_);
144     size_t activeWorkersCount_ GUARDED_BY(workersLock_) = 0;
145     mutable os::memory::RecursiveMutex workersLock_;
146     mutable os::memory::ConditionVariable workersCv_;
147 
148     // events that control program completion
149     mutable os::memory::Mutex programCompletionLock_;
150     CoroutineEvent *programCompletionEvent_ = nullptr;
151 
152     // various counters
153     std::atomic_uint32_t coroutineCount_ = 0;
154     size_t coroutineCountLimit_ = 0;
155     size_t coroStackSizeBytes_ = 0;
156     bool jsMode_ = false;
157 
158     /**
159      * @brief holds pointers to the cached coroutine instances in order to speedup coroutine creation and destruction.
160      * linked coroutinecontext instances are cached too (we just keep the cached coroutines linked to their contexts).
161      * used only in case when --use-coroutine-pool=true
162      */
163     PandaVector<Coroutine *> coroutinePool_ GUARDED_BY(coroPoolLock_);
164     mutable os::memory::Mutex coroPoolLock_;
165 };
166 
167 }  // namespace panda
168 
169 #endif /* PANDA_RUNTIME_COROUTINES_STACKFUL_COROUTINE_MANAGER_H */
170