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