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