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_COROUTINE_MANAGER_H 16 #define PANDA_RUNTIME_COROUTINES_COROUTINE_MANAGER_H 17 18 #include "runtime/coroutines/coroutine_context.h" 19 #include "runtime/thread_manager.h" 20 #include "runtime/include/runtime.h" 21 #include "runtime/coroutines/coroutine.h" 22 #include "runtime/coroutines/coroutine_events.h" 23 24 namespace panda { 25 26 /// @brief describes the set of adjustable parameters for CoroutineManager and its descendants initialization 27 struct CoroutineManagerConfig { 28 static constexpr uint32_t WORKERS_COUNT_AUTO = 0; 29 30 /// JS-compatible mode, affects async functions, await() and other things 31 bool emulateJs = false; 32 /// Number of coroutine workers for the N:M mode 33 uint32_t workersCount = WORKERS_COUNT_AUTO; 34 }; 35 36 /** 37 * @brief defines the scheduling policy for a coroutine. Maybe in future we would like to add more types (MAIN_WORKER, 38 * EXACT_WORKER, etc.) 39 */ 40 enum class CoroutineAffinity { 41 /// no affinity 42 NONE, 43 /// schedule to the parent's worker only 44 SAME_WORKER, 45 }; 46 47 /** 48 * @brief The interface of all coroutine manager implementations. 49 * 50 * Manages (registers, unregisters, enumerates) and schedules coroutines for execution using the worker threads. 51 * Creates and destroys the main coroutine. 52 * Provides interfaces for coroutine synchronization. 53 */ 54 class CoroutineManager : public ThreadManager { 55 public: 56 /** 57 * @brief The coroutine factory interface. 58 * 59 * @param name the coroutine name (for debugging and logging purposes) 60 * 61 * @param ctx the instance of implementation-dependend native coroutine context. Usually is provided by the 62 * concrete CoroutineManager descendant. 63 * 64 * @param ep_info if provided (that means, ep_info != std::nullopt), defines the coroutine entry point to execute. 65 * It can be either (the bytecode method, its arguments and the completion event instance to hold the method return 66 * value) or (a native function and its parameter). See Coroutine::EntrypointInfo for details. If this parameter is 67 * std::nullopt (i.e. no entrypoint present) then the following rules apply: 68 * - the factory should only create the coroutine instance and expect that the bytecode for the coroutine will be 69 * invoked elsewhere within the context of the newly created coroutine 70 * - the coroutine should be destroyed manually by the runtime by calling the Destroy() method 71 * - the coroutine does not use the method/arguments passing interface and the completion event interface 72 * - no other coroutine will await this one (although it can await others). 73 * The "main" coroutine (the EP of the application) is the specific example of such "no entrypoint" coroutine 74 * If ep_info is provided then the newly created coroutine will execute the specified method and do all the 75 * initialization/finalization steps, including completion_event management and notification of waiters. 76 * 77 */ 78 using CoroutineFactory = Coroutine *(*)(Runtime *runtime, PandaVM *vm, PandaString name, CoroutineContext *ctx, 79 std::optional<Coroutine::EntrypointInfo> &&epInfo); 80 81 NO_COPY_SEMANTIC(CoroutineManager); 82 NO_MOVE_SEMANTIC(CoroutineManager); 83 84 /// Factory is used to create coroutines when needed. See CoroutineFactory for details. 85 explicit CoroutineManager(CoroutineFactory factory); 86 ~CoroutineManager() override = default; 87 88 /** 89 * @brief Should be called after CoroutineManager creation and before any other method calls. 90 * Initializes internal structures, creates the main coroutine. 91 * 92 * @param config describes the CoroutineManager operation mode 93 */ 94 virtual void Initialize(CoroutineManagerConfig config, Runtime *runtime, PandaVM *vm) = 0; 95 /// Should be called after all execution is finished. Destroys the main coroutine. 96 virtual void Finalize() = 0; 97 /// Add coroutine to registry (used for enumeration and tracking) and perform all the required actions 98 virtual void RegisterCoroutine(Coroutine *co) = 0; 99 /** 100 * @brief Remove coroutine from all internal structures, notify waiters about its completion, correctly 101 * delete coroutine and free its resources 102 * @return returnes true if coroutine has been deleted, false otherwise (e.g. in case of terminating main) 103 */ 104 virtual bool TerminateCoroutine(Coroutine *co) = 0; 105 /** 106 * @brief The public coroutine creation interface. 107 * 108 * @param completion_event the event used for notification when coroutine completes (also used to pass the return 109 * value to the language-level entities) 110 * @param entrypoint the coroutine entrypoint method 111 * @param arguments array of coroutine's entrypoint arguments 112 */ 113 virtual Coroutine *Launch(CompletionEvent *completionEvent, Method *entrypoint, PandaVector<Value> &&arguments, 114 CoroutineAffinity affinity) = 0; 115 /// Suspend the current coroutine and schedule the next ready one for execution 116 virtual void Schedule() = 0; 117 /** 118 * @brief Move the current coroutine to the waiting state until awaitee happens and schedule the 119 * next ready coroutine for execution. 120 */ 121 virtual void Await(CoroutineEvent *awaitee) RELEASE(awaitee) = 0; 122 /** 123 * @brief Notify the waiting coroutines that an event has happened, so they can stop waiting and 124 * become ready for execution 125 * @param blocker the blocking event which transitioned from pending to happened state 126 * 127 * NB! this functions deletes @param blocker (assuming that it was allocated via InternalAllocator)! 128 */ 129 virtual void UnblockWaiters(CoroutineEvent *blocker) = 0; 130 131 /** 132 * The designated interface for creating the main coroutine instance. 133 * The program EP will be invoked within its context. 134 */ 135 Coroutine *CreateMainCoroutine(Runtime *runtime, PandaVM *vm); 136 /// Delete the main coroutine instance and free its resources 137 void DestroyMainCoroutine(); 138 139 /** 140 * Create a coroutine instance (including the context) for internal purposes (e.g. verifier) and 141 * set it as the current one. 142 * The created coroutine instance will not have any method to execute. All the control flow must be managed 143 * by the caller. 144 * 145 * @return nullptr if resource limit reached or something went wrong; ptr to the coroutine otherwise 146 */ 147 Coroutine *CreateEntrypointlessCoroutine(Runtime *runtime, PandaVM *vm, bool makeCurrent, PandaString name); 148 void DestroyEntrypointlessCoroutine(Coroutine *co); 149 150 /// Destroy a coroutine with an entrypoint 151 virtual void DestroyEntrypointfulCoroutine(Coroutine *co); 152 153 /// @return unique coroutine id 154 virtual uint32_t AllocateCoroutineId(); 155 /// mark the specified @param id as free 156 virtual void FreeCoroutineId(uint32_t id); 157 158 /* debugging tools */ 159 /** 160 * Disable coroutine switch for the current worker. 161 * If an attempt to switch the active coroutine is performed when coroutine switch is disabled, the exact actions 162 * are defined by the concrete CoroutineManager implementation (they could include no-op, program halt or something 163 * else). 164 */ 165 virtual void DisableCoroutineSwitch() = 0; 166 /// Enable coroutine switch for the current worker. 167 virtual void EnableCoroutineSwitch() = 0; 168 /// @return true if coroutine switch for the current worker is disabled 169 virtual bool IsCoroutineSwitchDisabled() = 0; 170 171 protected: 172 /// Create native coroutine context instance (implementation dependent) 173 virtual CoroutineContext *CreateCoroutineContext(bool coroHasEntrypoint) = 0; 174 /// Delete native coroutine context instance (implementation dependent) 175 virtual void DeleteCoroutineContext(CoroutineContext *) = 0; 176 /** 177 * Create coroutine instance including the native context and link the context and the coroutine. 178 * The coroutine is created using the factory provided in the CoroutineManager constructor and the virtual 179 * context creation function, which should be defined in concrete CoroutineManager implementations. 180 * 181 * @return nullptr if resource limit reached or something went wrong; ptr to the coroutine otherwise 182 */ 183 Coroutine *CreateCoroutineInstance(CompletionEvent *completionEvent, Method *entrypoint, 184 PandaVector<Value> &&arguments, PandaString name); 185 /// Returns number of existing coroutines 186 virtual size_t GetCoroutineCount() = 0; 187 /** 188 * @brief returns number of coroutines that could be created till the resource limit is reached. 189 * The resource limit definition is specific to the exact coroutines/coroutine manager implementation. 190 */ 191 virtual size_t GetCoroutineCountLimit() = 0; 192 193 /// Can be used in descendants to create custom coroutines manually 194 CoroutineFactory GetCoroutineFactory(); 195 196 /// limit the number of IDs for performance reasons 197 static constexpr size_t MAX_COROUTINE_ID = std::min(0xffffU, Coroutine::MAX_COROUTINE_ID); 198 static constexpr size_t UNINITIALIZED_COROUTINE_ID = 0x0U; 199 200 private: 201 CoroutineFactory coFactory_ = nullptr; 202 203 // coroutine id management 204 os::memory::Mutex idsLock_; 205 std::bitset<MAX_COROUTINE_ID> coroutineIds_ GUARDED_BY(idsLock_); 206 uint32_t lastCoroutineId_ GUARDED_BY(idsLock_) = UNINITIALIZED_COROUTINE_ID; 207 }; 208 209 /// Disables coroutine switch on the current worker for some scope. Can be used recursively. 210 class ScopedDisableCoroutineSwitch { 211 public: ScopedDisableCoroutineSwitch(CoroutineManager * coroManager)212 explicit ScopedDisableCoroutineSwitch(CoroutineManager *coroManager) : coroManager_(coroManager) 213 { 214 ASSERT(coroManager_ != nullptr); 215 coroManager_->DisableCoroutineSwitch(); 216 } 217 ~ScopedDisableCoroutineSwitch()218 ~ScopedDisableCoroutineSwitch() 219 { 220 coroManager_->EnableCoroutineSwitch(); 221 } 222 223 private: 224 CoroutineManager *coroManager_; 225 226 NO_COPY_SEMANTIC(ScopedDisableCoroutineSwitch); 227 NO_MOVE_SEMANTIC(ScopedDisableCoroutineSwitch); 228 }; 229 230 } // namespace panda 231 232 #endif /* PANDA_RUNTIME_COROUTINES_COROUTINE_MANAGER_H */ 233