1 /**
2 * Copyright (c) 2022 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 panda {
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 Coroutine::SetCurrent(co);
98 os::thread::SetThreadName(os::thread::GetNativeHandle(), co->GetName().c_str());
99 // NOTE(konstanting, #I67QXC): find a workaround to update ThreadId here
100 co->NativeCodeBegin();
101 {
102 ctx->InitializationDone();
103 if (co->IsSuspendOnStartup()) {
104 ctx->WaitUntilResumed();
105 } else {
106 ctx->SetStatus(Coroutine::Status::RUNNING);
107 }
108
109 ScopedManagedCodeThread s(co);
110 PandaVector<Value> args = std::move(co->GetManagedEntrypointArguments());
111 LOG(DEBUG, COROUTINES) << "ThreadProc: invoking the EP for coro " << co->GetName();
112 Value res = co->GetManagedEntrypoint()->Invoke(co, args.data());
113 LOG(DEBUG, COROUTINES) << "ThreadProc: invoke() finished for the EP of coro " << co->GetName();
114 co->RequestCompletion(res);
115 }
116 ctx->SetStatus(Coroutine::Status::TERMINATING);
117
118 auto *threadManager = static_cast<CoroutineManager *>(co->GetVM()->GetThreadManager());
119 if (threadManager->TerminateCoroutine(co)) {
120 Coroutine::SetCurrent(nullptr);
121 }
122 }
123
WaitUntilInitialized()124 void ThreadedCoroutineContext::WaitUntilInitialized()
125 {
126 os::memory::LockHolder l(cvMutex_);
127 while (GetStatus() != Coroutine::Status::RUNNABLE) {
128 cv_.Wait(&cvMutex_);
129 }
130 }
131
WaitUntilResumed()132 void ThreadedCoroutineContext::WaitUntilResumed()
133 {
134 os::memory::LockHolder l(cvMutex_);
135 while (GetStatus() != Coroutine::Status::RUNNING) {
136 cv_.Wait(&cvMutex_);
137 }
138 }
139
InitializationDone()140 void ThreadedCoroutineContext::InitializationDone()
141 {
142 if (GetCoroutine()->IsSuspendOnStartup()) {
143 os::memory::LockHolder l(cvMutex_);
144 SetStatus(Coroutine::Status::RUNNABLE);
145 cv_.Signal();
146 } else {
147 SetStatus(Coroutine::Status::RUNNABLE);
148 }
149 }
150
RequestSuspend(bool getsBlocked)151 void ThreadedCoroutineContext::RequestSuspend(bool getsBlocked)
152 {
153 os::memory::LockHolder l(cvMutex_);
154 SetStatus(getsBlocked ? Coroutine::Status::BLOCKED : Coroutine::Status::RUNNABLE);
155 }
156
RequestResume()157 void ThreadedCoroutineContext::RequestResume()
158 {
159 os::memory::LockHolder l(cvMutex_);
160 SetStatus(Coroutine::Status::RUNNING);
161 cv_.Signal();
162 }
163
RequestUnblock()164 void ThreadedCoroutineContext::RequestUnblock()
165 {
166 SetStatus(Coroutine::Status::RUNNABLE);
167 }
168
MainThreadFinished()169 void ThreadedCoroutineContext::MainThreadFinished()
170 {
171 SetStatus(Coroutine::Status::TERMINATING);
172 }
173
EnterAwaitLoop()174 void ThreadedCoroutineContext::EnterAwaitLoop()
175 {
176 SetStatus(Coroutine::Status::AWAIT_LOOP);
177 }
178
179 } // namespace panda
180