1 /**
2 * Copyright (c) 2023-2025 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
16 #include <optional>
17 #include "runtime/coroutines/coroutine_manager.h"
18
19 namespace ark {
20
CoroutineManager(CoroutineFactory factory)21 CoroutineManager::CoroutineManager(CoroutineFactory factory) : coFactory_(factory)
22 {
23 ASSERT(factory != nullptr);
24 }
25
CreateMainCoroutine(Runtime * runtime,PandaVM * vm)26 Coroutine *CoroutineManager::CreateMainCoroutine(Runtime *runtime, PandaVM *vm)
27 {
28 CoroutineContext *ctx = CreateCoroutineContext(false);
29 auto *main = coFactory_(runtime, vm, "_main_", ctx, std::nullopt, Coroutine::Type::MUTATOR,
30 CoroutinePriority::MEDIUM_PRIORITY);
31 ASSERT(main != nullptr);
32
33 Coroutine::SetCurrent(main);
34 main->InitBuffers();
35 main->LinkToExternalHolder(true);
36 main->RequestResume();
37 main->NativeCodeBegin();
38
39 SetMainThread(main);
40 return main;
41 }
42
DestroyMainCoroutine()43 void CoroutineManager::DestroyMainCoroutine()
44 {
45 auto *main = static_cast<Coroutine *>(GetMainThread());
46 auto *context = main->GetContext<CoroutineContext>();
47 main->Destroy();
48 Coroutine::SetCurrent(nullptr);
49 Runtime::GetCurrent()->GetInternalAllocator()->Delete(main);
50 DeleteCoroutineContext(context);
51 }
52
CreateEntrypointlessCoroutine(Runtime * runtime,PandaVM * vm,bool makeCurrent,PandaString name,Coroutine::Type type,CoroutinePriority priority)53 Coroutine *CoroutineManager::CreateEntrypointlessCoroutine(Runtime *runtime, PandaVM *vm, bool makeCurrent,
54 PandaString name, Coroutine::Type type,
55 CoroutinePriority priority)
56 {
57 if (GetCoroutineCount() >= GetCoroutineCountLimit()) {
58 // resource limit reached
59 return nullptr;
60 }
61 CoroutineContext *ctx = CreateCoroutineContext(false);
62 if (ctx == nullptr) {
63 // do not proceed if we cannot create a context for the new coroutine
64 return nullptr;
65 }
66 auto *co = coFactory_(runtime, vm, std::move(name), ctx, std::nullopt, type, priority);
67 ASSERT(co != nullptr);
68 co->InitBuffers();
69 if (makeCurrent) {
70 Coroutine::SetCurrent(co);
71 co->LinkToExternalHolder(false);
72 co->RequestResume();
73 co->NativeCodeBegin();
74 }
75 return co;
76 }
77
DestroyEntrypointlessCoroutine(Coroutine * co)78 void CoroutineManager::DestroyEntrypointlessCoroutine(Coroutine *co)
79 {
80 ASSERT(co != nullptr);
81 ASSERT(!co->HasManagedEntrypoint() && !co->HasNativeEntrypoint());
82
83 auto *context = co->GetContext<CoroutineContext>();
84 co->Destroy();
85 Runtime::GetCurrent()->GetInternalAllocator()->Delete(co);
86 DeleteCoroutineContext(context);
87 }
88
CreateCoroutineInstance(EntrypointInfo && epInfo,PandaString name,Coroutine::Type type,CoroutinePriority priority)89 Coroutine *CoroutineManager::CreateCoroutineInstance(EntrypointInfo &&epInfo, PandaString name, Coroutine::Type type,
90 CoroutinePriority priority)
91 {
92 if (GetCoroutineCount() >= GetCoroutineCountLimit()) {
93 // resource limit reached
94 return nullptr;
95 }
96 CoroutineContext *ctx = CreateCoroutineContext(true);
97 if (ctx == nullptr) {
98 // do not proceed if we cannot create a context for the new coroutine
99 return nullptr;
100 }
101 // assuming that the main coro is already created and Coroutine::GetCurrent is not nullptr
102 ASSERT(Coroutine::GetCurrent() != nullptr);
103 auto *coro = coFactory_(Runtime::GetCurrent(), Coroutine::GetCurrent()->GetVM(), std::move(name), ctx,
104 std::move(epInfo), type, priority);
105 return coro;
106 }
107
DestroyEntrypointfulCoroutine(Coroutine * co)108 void CoroutineManager::DestroyEntrypointfulCoroutine(Coroutine *co)
109 {
110 DeleteCoroutineContext(co->GetContext<CoroutineContext>());
111 Runtime::GetCurrent()->GetInternalAllocator()->Delete(co);
112 }
113
GetCoroutineFactory()114 CoroutineManager::CoroutineFactory CoroutineManager::GetCoroutineFactory()
115 {
116 return coFactory_;
117 }
118
AllocateCoroutineId()119 uint32_t CoroutineManager::AllocateCoroutineId()
120 {
121 // Taken by copy-paste from MTThreadManager. Need to generalize if possible.
122 // NOTE(konstanting, #IAD5MH): try to generalize internal ID allocation
123 os::memory::LockHolder lock(idsLock_);
124 for (size_t i = 0; i < coroutineIds_.size(); i++) {
125 lastCoroutineId_ = (lastCoroutineId_ + 1) % coroutineIds_.size();
126 if (!coroutineIds_[lastCoroutineId_]) {
127 coroutineIds_.set(lastCoroutineId_);
128 return lastCoroutineId_ + 1; // 0 is reserved as uninitialized value.
129 }
130 }
131 LOG(FATAL, COROUTINES) << "Out of coroutine ids";
132 UNREACHABLE();
133 }
134
FreeCoroutineId(uint32_t id)135 void CoroutineManager::FreeCoroutineId(uint32_t id)
136 {
137 // Taken by copy-paste from MTThreadManager. Need to generalize if possible.
138 // NOTE(konstanting, #IAD5MH): try to generalize internal ID allocation
139 id--; // 0 is reserved as uninitialized value.
140 os::memory::LockHolder lock(idsLock_);
141 ASSERT(coroutineIds_[id]);
142 coroutineIds_.reset(id);
143 }
144
SetSchedulingPolicy(CoroutineSchedulingPolicy policy)145 void CoroutineManager::SetSchedulingPolicy(CoroutineSchedulingPolicy policy)
146 {
147 os::memory::LockHolder lock(policyLock_);
148 schedulingPolicy_ = policy;
149 }
150
GetSchedulingPolicy() const151 CoroutineSchedulingPolicy CoroutineManager::GetSchedulingPolicy() const
152 {
153 os::memory::LockHolder lock(policyLock_);
154 return schedulingPolicy_;
155 }
156
157 } // namespace ark
158