1 /**
2 * Copyright (c) 2022-2024 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, #I67QXC): 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 co->ProcessPresentAndAnnouncedCallbacks();
118 }
119 ctx->SetStatus(Coroutine::Status::TERMINATING);
120
121 if (threadManager->TerminateCoroutine(co)) {
122 Coroutine::SetCurrent(nullptr);
123 }
124 }
125
WaitUntilInitialized()126 void ThreadedCoroutineContext::WaitUntilInitialized()
127 {
128 os::memory::LockHolder l(cvMutex_);
129 while (GetStatus() != Coroutine::Status::RUNNABLE) {
130 cv_.Wait(&cvMutex_);
131 }
132 }
133
WaitUntilResumed()134 void ThreadedCoroutineContext::WaitUntilResumed()
135 {
136 os::memory::LockHolder l(cvMutex_);
137 while (GetStatus() != Coroutine::Status::RUNNING) {
138 cv_.Wait(&cvMutex_);
139 }
140 }
141
InitializationDone()142 void ThreadedCoroutineContext::InitializationDone()
143 {
144 if (GetCoroutine()->IsSuspendOnStartup()) {
145 os::memory::LockHolder l(cvMutex_);
146 SetStatus(Coroutine::Status::RUNNABLE);
147 cv_.Signal();
148 } else {
149 SetStatus(Coroutine::Status::RUNNABLE);
150 }
151 }
152
RequestSuspend(bool getsBlocked)153 void ThreadedCoroutineContext::RequestSuspend(bool getsBlocked)
154 {
155 os::memory::LockHolder l(cvMutex_);
156 SetStatus(getsBlocked ? Coroutine::Status::BLOCKED : Coroutine::Status::RUNNABLE);
157 }
158
RequestResume()159 void ThreadedCoroutineContext::RequestResume()
160 {
161 os::memory::LockHolder l(cvMutex_);
162 SetStatus(Coroutine::Status::RUNNING);
163 cv_.Signal();
164 }
165
RequestUnblock()166 void ThreadedCoroutineContext::RequestUnblock()
167 {
168 SetStatus(Coroutine::Status::RUNNABLE);
169 }
170
MainThreadFinished()171 void ThreadedCoroutineContext::MainThreadFinished()
172 {
173 SetStatus(Coroutine::Status::TERMINATING);
174 }
175
EnterAwaitLoop()176 void ThreadedCoroutineContext::EnterAwaitLoop()
177 {
178 SetStatus(Coroutine::Status::AWAIT_LOOP);
179 }
180
181 } // namespace ark
182