• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "runtime/coroutines/coroutine.h"
17 #include "runtime/coroutines/coroutine_context.h"
18 #include "runtime/coroutines/coroutine_manager.h"
19 #include "runtime/coroutines/coroutine_events.h"
20 #include "runtime/include/panda_vm.h"
21 
22 namespace ark {
23 #ifdef ARK_HYBRID
VisitCoroutine(void * coroutine,CommonRootVisitor visitor)24 extern "C" void VisitCoroutine(void *coroutine, CommonRootVisitor visitor)
25 {
26     reinterpret_cast<Coroutine *>(coroutine)->Visit(visitor);
27 }
28 #endif
29 
Create(Runtime * runtime,PandaVM * vm,PandaString name,CoroutineContext * context,std::optional<EntrypointInfo> && epInfo,Type type,CoroutinePriority priority)30 Coroutine *Coroutine::Create(Runtime *runtime, PandaVM *vm, PandaString name, CoroutineContext *context,
31                              std::optional<EntrypointInfo> &&epInfo, Type type, CoroutinePriority priority)
32 {
33     mem::InternalAllocatorPtr allocator = runtime->GetInternalAllocator();
34     auto *co = allocator->New<Coroutine>(os::thread::GetCurrentThreadId(), allocator, vm,
35                                          ark::panda_file::SourceLang::PANDA_ASSEMBLY, std::move(name), context,
36                                          std::move(epInfo), type, priority);
37     ASSERT(co != nullptr);
38     co->Initialize();
39     return co;
40 }
41 
Coroutine(ThreadId id,mem::InternalAllocatorPtr allocator,PandaVM * vm,ark::panda_file::SourceLang threadLang,PandaString name,CoroutineContext * context,std::optional<EntrypointInfo> && epInfo,Type type,CoroutinePriority priority)42 Coroutine::Coroutine(ThreadId id, mem::InternalAllocatorPtr allocator, PandaVM *vm,
43                      ark::panda_file::SourceLang threadLang, PandaString name, CoroutineContext *context,
44                      std::optional<EntrypointInfo> &&epInfo, Type type, CoroutinePriority priority)
45     : ManagedThread(id, allocator, vm, Thread::ThreadType::THREAD_TYPE_TASK, threadLang),
46       name_(std::move(name)),
47       context_(context),
48       manager_(static_cast<CoroutineManager *>(GetVM()->GetThreadManager())),
49       startSuspended_(epInfo.has_value()),
50       type_(type),
51       priority_(priority)
52 {
53     ASSERT(vm != nullptr);
54     ASSERT(context != nullptr);
55     ASSERT(manager_ != nullptr);
56     SetEntrypointData(std::move(epInfo));
57     coroutineId_ = GetManager()->AllocateCoroutineId();
58 }
59 
~Coroutine()60 Coroutine::~Coroutine()
61 {
62     GetManager()->FreeCoroutineId(coroutineId_);
63 }
64 
ReInitialize(PandaString name,CoroutineContext * context,std::optional<EntrypointInfo> && epInfo,CoroutinePriority priority)65 void Coroutine::ReInitialize(PandaString name, CoroutineContext *context, std::optional<EntrypointInfo> &&epInfo,
66                              CoroutinePriority priority)
67 {
68     ASSERT(context != nullptr);
69     name_ = std::move(name);
70     startSuspended_ = epInfo.has_value();
71     context_ = context;
72 
73     SetEntrypointData(std::move(epInfo));
74     context_->AttachToCoroutine(this);
75     priority_ = priority;
76 }
77 
SetEntrypointData(std::optional<EntrypointInfo> && epInfo)78 void Coroutine::SetEntrypointData(std::optional<EntrypointInfo> &&epInfo)
79 {
80     if (epInfo.has_value()) {
81         auto &info = epInfo.value();
82         if (std::holds_alternative<ManagedEntrypointInfo>(info)) {
83             auto &managedEp = std::get<ManagedEntrypointInfo>(info);
84             entrypoint_.emplace<ManagedEntrypointData>(managedEp.completionEvent, managedEp.entrypoint,
85                                                        std::move(managedEp.arguments));
86         } else if (std::holds_alternative<NativeEntrypointInfo>(info)) {
87             auto &nativeEp = std::get<NativeEntrypointInfo>(info);
88             entrypoint_ = NativeEntrypointData(nativeEp.entrypoint, nativeEp.param);
89         }
90     }
91 }
92 
CleanUp()93 void Coroutine::CleanUp()
94 {
95     ManagedThread::CleanUp();
96     name_ = "";
97     entrypoint_ = std::monostate();
98     startSuspended_ = false;
99     immediateLauncher_ = nullptr;
100     worker_ = nullptr;
101     context_->CleanUp();
102 }
103 
~ManagedEntrypointData()104 Coroutine::ManagedEntrypointData::~ManagedEntrypointData()
105 {
106     // delete the event as it is owned by the ManagedEntrypointData instance
107     Runtime::GetCurrent()->GetInternalAllocator()->Delete(completionEvent);
108 }
109 
GetName() const110 PandaString Coroutine::GetName() const
111 {
112     return name_;
113 }
114 
GetCoroutineStatus() const115 Coroutine::Status Coroutine::GetCoroutineStatus() const
116 {
117     return context_->GetStatus();
118 }
119 
SetCoroutineStatus(Coroutine::Status newStatus)120 void Coroutine::SetCoroutineStatus(Coroutine::Status newStatus)
121 {
122     context_->SetStatus(newStatus);
123 }
124 
OnStatusChanged(Status oldStatus,Status newStatus)125 void Coroutine::OnStatusChanged(Status oldStatus, Status newStatus)
126 {
127     // issue required status change events to the CoroutineManager
128     bool hasWorker = (GetWorker() != nullptr);
129     bool wasActive = hasWorker && (oldStatus == Coroutine::Status::RUNNABLE || oldStatus == Coroutine::Status::RUNNING);
130     bool isActive = hasWorker && (newStatus == Coroutine::Status::RUNNABLE || newStatus == Coroutine::Status::RUNNING);
131     if (UNLIKELY(wasActive != isActive)) {
132         if (wasActive && !isActive) {
133             GetManager()->OnCoroBecameNonActive(this);
134         } else if (!wasActive && isActive) {
135             GetManager()->OnCoroBecameActive(this);
136         }
137     }
138 }
139 
LinkToExternalHolder(bool useSharedHolder)140 void Coroutine::LinkToExternalHolder([[maybe_unused]] bool useSharedHolder)
141 {
142 #ifdef ARK_HYBRID
143     auto wasCreated = CreateExternalHolderIfNeeded(useSharedHolder);
144     if (wasCreated) {
145         GetThreadHolder()->RegisterCoroutine(this);
146     }
147 #endif
148 }
149 
Destroy()150 void Coroutine::Destroy()
151 {
152     context_->Destroy();
153 }
154 
Initialize()155 void Coroutine::Initialize()
156 {
157     context_->AttachToCoroutine(this);
158     // NOTE(cao ying, #26951): we need to remove this after refactoring the ark_aot initialization sequence,
159     // which should not include the unnecessary parts
160     // NOTE(cao ying, #26507): long command line causes SEGV under OHOS during the stack overflow
161     // checker initialization. Currently long command lines are not required for ark, but are required
162     // for ark_aot. So let's disable the stack overflow checker for ark_aot as a hotfix.
163     if (Runtime::GetCurrent()->GetOptions().IsArkAot()) {
164         return;
165     }
166     InitForStackOverflowCheck(ManagedThread::STACK_OVERFLOW_RESERVED_SIZE,
167                               ManagedThread::STACK_OVERFLOW_PROTECTED_SIZE);
168 }
169 
RetrieveStackInfo(void * & stackAddr,size_t & stackSize,size_t & guardSize)170 bool Coroutine::RetrieveStackInfo(void *&stackAddr, size_t &stackSize, size_t &guardSize)
171 {
172     if (HasManagedEntrypoint() || HasNativeEntrypoint()) {
173         // has EP and separate native context for its execution
174         return context_->RetrieveStackInfo(stackAddr, stackSize, guardSize);
175     }
176     // does not have EP, executes on OS-provided context and stack
177     return ManagedThread::RetrieveStackInfo(stackAddr, stackSize, guardSize);
178 }
179 
RequestSuspend(bool getsBlocked)180 void Coroutine::RequestSuspend(bool getsBlocked)
181 {
182     context_->RequestSuspend(getsBlocked);
183 }
184 
RequestResume()185 void Coroutine::RequestResume()
186 {
187     context_->RequestResume();
188 }
189 
RequestUnblock()190 void Coroutine::RequestUnblock()
191 {
192     context_->RequestUnblock();
193 }
194 
RequestCompletion(Value returnValue)195 void Coroutine::RequestCompletion([[maybe_unused]] Value returnValue)
196 {
197     auto *e = GetCompletionEvent();
198     e->Happen();
199 }
200 
IsActive()201 bool Coroutine::IsActive()
202 {
203     bool workerAssigned = (GetWorker() != nullptr);
204     auto status = GetCoroutineStatus();
205     bool isRunnableOrRunning = (status == Status::RUNNABLE) || (status == Status::RUNNING);
206     return (workerAssigned && isRunnableOrRunning);
207 }
208 
SetWorker(CoroutineWorker * w)209 void Coroutine::SetWorker(CoroutineWorker *w)
210 {
211     auto *oldWorker = worker_;
212     worker_ = w;
213     if (w != oldWorker) {
214         OnHostWorkerChanged();
215     }
216     // being ACTIVE requires an assigned worker!
217     if ((oldWorker == nullptr) && (w != nullptr)) {
218         GetManager()->OnCoroBecameActive(this);
219     } else if ((oldWorker != nullptr) && (w == nullptr)) {
220         GetManager()->OnCoroBecameNonActive(this);
221     }
222 }
223 
operator <<(std::ostream & os,Coroutine::Status status)224 std::ostream &operator<<(std::ostream &os, Coroutine::Status status)
225 {
226     switch (status) {
227         case Coroutine::Status::CREATED:
228             os << "CREATED";
229             break;
230         case Coroutine::Status::RUNNABLE:
231             os << "RUNNABLE";
232             break;
233         case Coroutine::Status::RUNNING:
234             os << "RUNNING";
235             break;
236         case Coroutine::Status::BLOCKED:
237             os << "BLOCKED";
238             break;
239         case Coroutine::Status::TERMINATING:
240             os << "TERMINATING";
241             break;
242         case Coroutine::Status::AWAIT_LOOP:
243             os << "AWAIT_LOOP";
244             break;
245         default:
246             break;
247     }
248     return os;
249 }
250 
251 }  // namespace ark
252