• 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 protected:
93     bool EnumerateThreadsImpl(const ThreadManager::Callback &cb, unsigned int incMask,
94                               unsigned int xorMask) const override;
95     CoroutineContext *CreateCoroutineContext(bool coroHasEntrypoint) override;
96     void DeleteCoroutineContext(CoroutineContext *ctx) override;
97 
98     size_t GetCoroutineCount() override;
99     size_t GetCoroutineCountLimit() override;
100 
101     StackfulCoroutineContext *GetCurrentContext();
102     StackfulCoroutineWorker *GetCurrentWorker();
103 
104     /**
105      * @brief reuse a cached coroutine instance in case when coroutine pool is enabled
106      * see Coroutine::ReInitialize for details
107      */
108     void ReuseCoroutineInstance(Coroutine *co, CompletionEvent *completionEvent, Method *entrypoint,
109                                 PandaVector<Value> &&arguments, PandaString name);
110 
111 private:
112     StackfulCoroutineContext *CreateCoroutineContextImpl(bool needStack);
113     StackfulCoroutineWorker *ChooseWorkerForCoroutine(Coroutine *co);
114     stackful_coroutines::AffinityMask CalcAffinityMaskFromLaunchMode(CoroutineLaunchMode mode);
115 
116     Coroutine *LaunchImpl(CompletionEvent *completionEvent, Method *entrypoint, PandaVector<Value> &&arguments,
117                           CoroutineLaunchMode mode);
118     /**
119      * Tries to extract a coroutine instance from the pool for further reuse, returns nullptr in case when it is not
120      * possible.
121      */
122     Coroutine *TryGetCoroutineFromPool();
123 
124     /* workers API */
125     /**
126      * @brief create the arbitrary number of worker threads
127      * @param how_many total number of worker threads, including MAIN
128      */
129     void CreateWorkers(size_t howMany, Runtime *runtime, PandaVM *vm) REQUIRES(workersLock_);
130 
131     /* coroutine registry management */
132     void AddToRegistry(Coroutine *co);
133     void RemoveFromRegistry(Coroutine *co) REQUIRES(coroListLock_);
134 
135     /// call to check if we are done executing managed code and set appropriate member flags
136     void CheckProgramCompletion();
137     /// call when main coroutine is done executing its managed EP
138     void MainCoroutineCompleted();
139     /// wait till all the non-main coroutines with managed EP finish execution
140     void WaitForNonMainCoroutinesCompletion();
141     /// @return number of running worker loop coroutines
142     size_t GetActiveWorkersCount() const;
143     /// @return number of existing worker instances
144     size_t GetExistingWorkersCount() const;
145     /// dump coroutine stats to stdout
146     void DumpCoroutineStats() const;
147 
148     /* resource management */
149     uint8_t *AllocCoroutineStack();
150     void FreeCoroutineStack(uint8_t *stack);
151 
152     // for thread safety with GC
153     mutable os::memory::Mutex coroListLock_;
154     // all registered coros
155     PandaSet<Coroutine *> coroutines_ GUARDED_BY(coroListLock_);
156 
157     // worker threads-related members
158     PandaVector<StackfulCoroutineWorker *> workers_ GUARDED_BY(workersLock_);
159     size_t activeWorkersCount_ GUARDED_BY(workersLock_) = 0;
160     mutable os::memory::RecursiveMutex workersLock_;
161     mutable os::memory::ConditionVariable workersCv_;
162 
163     // events that control program completion
164     mutable os::memory::Mutex programCompletionLock_;
165     CoroutineEvent *programCompletionEvent_ = nullptr;
166 
167     // various counters
168     std::atomic_uint32_t coroutineCount_ = 0;
169     size_t coroutineCountLimit_ = 0;
170     size_t coroStackSizeBytes_ = 0;
171     bool jsMode_ = false;
172 
173     /**
174      * @brief holds pointers to the cached coroutine instances in order to speedup coroutine creation and destruction.
175      * linked coroutinecontext instances are cached too (we just keep the cached coroutines linked to their contexts).
176      * used only in case when --use-coroutine-pool=true
177      */
178     PandaVector<Coroutine *> coroutinePool_ GUARDED_BY(coroPoolLock_);
179     mutable os::memory::Mutex coroPoolLock_;
180 
181     // stats
182     CoroutineStats stats_;
183 };
184 
185 }  // namespace ark
186 
187 #endif /* PANDA_RUNTIME_COROUTINES_STACKFUL_COROUTINE_MANAGER_H */
188