• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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