1 /**
2 * Copyright (c) 2022-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_context.h"
17 #include "runtime/include/panda_vm.h"
18 #include "runtime/include/thread_scopes.h"
19 #include "runtime/coroutines/coroutine_manager.h"
20 #include "runtime/coroutines/threaded_coroutine.h"
21
22 namespace ark {
23
GetCoroutineNativeHandle()24 os::thread::NativeHandleType ThreadedCoroutineContext::GetCoroutineNativeHandle()
25 {
26 return nativeHandle_;
27 }
28
SetCoroutineNativeHandle(os::thread::NativeHandleType h)29 void ThreadedCoroutineContext::SetCoroutineNativeHandle(os::thread::NativeHandleType h)
30 {
31 nativeHandle_ = h;
32 }
33
GetStatus() const34 Coroutine::Status ThreadedCoroutineContext::GetStatus() const
35 {
36 return status_;
37 }
38
SetStatus(Coroutine::Status newStatus)39 void ThreadedCoroutineContext::SetStatus(Coroutine::Status newStatus)
40 {
41 #ifndef NDEBUG
42 PandaString setter = (Thread::GetCurrent() == nullptr) ? "null" : Coroutine::GetCurrent()->GetName();
43 LOG(DEBUG, COROUTINES) << GetCoroutine()->GetName() << ": " << status_ << " -> " << newStatus << " by " << setter;
44 #endif
45 status_ = newStatus;
46 }
47
AttachToCoroutine(Coroutine * co)48 void ThreadedCoroutineContext::AttachToCoroutine(Coroutine *co)
49 {
50 CoroutineContext::AttachToCoroutine(co);
51
52 auto *threadManager = static_cast<CoroutineManager *>(co->GetVM()->GetThreadManager());
53 threadManager->RegisterCoroutine(co);
54 if (co->HasManagedEntrypoint()) {
55 std::thread t(ThreadProc, this);
56 SetCoroutineNativeHandle(t.native_handle());
57 t.detach();
58 } else {
59 // coro has no EP, will execute in the current thread context
60 SetCoroutineNativeHandle(os::thread::GetNativeHandle());
61 }
62 }
63
Destroy()64 void ThreadedCoroutineContext::Destroy()
65 {
66 auto *co = GetCoroutine();
67 if (co->HasManagedEntrypoint()) {
68 // coroutines with an entry point should not be destroyed manually!
69 UNREACHABLE();
70 }
71 ASSERT(co == Coroutine::GetCurrent());
72 ASSERT(co->GetStatus() != ThreadStatus::FINISHED);
73
74 co->UpdateStatus(ThreadStatus::TERMINATING);
75
76 auto *threadManager = static_cast<CoroutineManager *>(co->GetVM()->GetThreadManager());
77 if (threadManager->TerminateCoroutine(co)) {
78 // detach
79 Coroutine::SetCurrent(nullptr);
80 }
81 }
82
RetrieveStackInfo(void * & stackAddr,size_t & stackSize,size_t & guardSize)83 bool ThreadedCoroutineContext::RetrieveStackInfo(void *&stackAddr, size_t &stackSize, size_t &guardSize)
84 {
85 int error = os::thread::ThreadGetStackInfo(GetCoroutineNativeHandle(), &stackAddr, &stackSize, &guardSize);
86 if (error != 0) {
87 LOG(ERROR, RUNTIME) << "ThreadedCoroutineContext::RetrieveStackInfo: fail to get stack info, error = "
88 << strerror(errno);
89 }
90 return error == 0;
91 }
92
93 /*static*/
ThreadProc(ThreadedCoroutineContext * ctx)94 void ThreadedCoroutineContext::ThreadProc(ThreadedCoroutineContext *ctx)
95 {
96 auto *co = ctx->GetCoroutine();
97 auto *threadManager = static_cast<CoroutineManager *>(co->GetVM()->GetThreadManager());
98 Coroutine::SetCurrent(co);
99 UpdateId(os::thread::GetCurrentThreadId(), co);
100 os::thread::SetThreadName(os::thread::GetNativeHandle(), co->GetName().c_str());
101 // NOTE(konstanting, #IAD5MH): find a workaround to update ThreadId here
102 co->NativeCodeBegin();
103 {
104 ctx->InitializationDone();
105 if (co->IsSuspendOnStartup()) {
106 ctx->WaitUntilResumed();
107 } else {
108 ctx->SetStatus(Coroutine::Status::RUNNING);
109 }
110
111 ScopedManagedCodeThread s(co);
112 PandaVector<Value> args = std::move(co->GetManagedEntrypointArguments());
113 LOG(DEBUG, COROUTINES) << "ThreadProc: invoking the EP for coro " << co->GetName();
114 Value res = co->GetManagedEntrypoint()->Invoke(co, args.data());
115 LOG(DEBUG, COROUTINES) << "ThreadProc: invoke() finished for the EP of coro " << co->GetName();
116 co->RequestCompletion(res);
117 }
118 ctx->SetStatus(Coroutine::Status::TERMINATING);
119
120 if (threadManager->TerminateCoroutine(co)) {
121 Coroutine::SetCurrent(nullptr);
122 }
123 }
124
WaitUntilInitialized()125 void ThreadedCoroutineContext::WaitUntilInitialized()
126 {
127 os::memory::LockHolder l(cvMutex_);
128 while (GetStatus() != Coroutine::Status::RUNNABLE) {
129 cv_.Wait(&cvMutex_);
130 }
131 }
132
WaitUntilResumed()133 void ThreadedCoroutineContext::WaitUntilResumed()
134 {
135 os::memory::LockHolder l(cvMutex_);
136 while (GetStatus() != Coroutine::Status::RUNNING) {
137 cv_.Wait(&cvMutex_);
138 }
139 }
140
InitializationDone()141 void ThreadedCoroutineContext::InitializationDone()
142 {
143 if (GetCoroutine()->IsSuspendOnStartup()) {
144 os::memory::LockHolder l(cvMutex_);
145 SetStatus(Coroutine::Status::RUNNABLE);
146 cv_.Signal();
147 } else {
148 SetStatus(Coroutine::Status::RUNNABLE);
149 }
150 }
151
RequestSuspend(bool getsBlocked)152 void ThreadedCoroutineContext::RequestSuspend(bool getsBlocked)
153 {
154 os::memory::LockHolder l(cvMutex_);
155 SetStatus(getsBlocked ? Coroutine::Status::BLOCKED : Coroutine::Status::RUNNABLE);
156 }
157
RequestResume()158 void ThreadedCoroutineContext::RequestResume()
159 {
160 os::memory::LockHolder l(cvMutex_);
161 SetStatus(Coroutine::Status::RUNNING);
162 cv_.Signal();
163 }
164
RequestUnblock()165 void ThreadedCoroutineContext::RequestUnblock()
166 {
167 SetStatus(Coroutine::Status::RUNNABLE);
168 }
169
MainThreadFinished()170 void ThreadedCoroutineContext::MainThreadFinished()
171 {
172 SetStatus(Coroutine::Status::TERMINATING);
173 }
174
EnterAwaitLoop()175 void ThreadedCoroutineContext::EnterAwaitLoop()
176 {
177 SetStatus(Coroutine::Status::AWAIT_LOOP);
178 }
179
180 } // namespace ark
181