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