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