• 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 <algorithm>
17 #include "runtime/coroutines/coroutine.h"
18 #include "runtime/coroutines/stackful_coroutine.h"
19 #include "runtime/include/thread_scopes.h"
20 #include "libpandabase/macros.h"
21 #include "runtime/include/runtime.h"
22 #include "runtime/include/runtime_notification.h"
23 #include "runtime/include/panda_vm.h"
24 #include "runtime/coroutines/stackful_coroutine_manager.h"
25 
26 namespace panda {
27 
AllocCoroutineStack()28 uint8_t *StackfulCoroutineManager::AllocCoroutineStack()
29 {
30     Pool stackPool = PoolManager::GetMmapMemPool()->AllocPool<OSPagesAllocPolicy::NO_POLICY>(
31         coroStackSizeBytes_, SpaceType::SPACE_TYPE_NATIVE_STACKS, AllocatorType::NATIVE_STACKS_ALLOCATOR);
32     return static_cast<uint8_t *>(stackPool.GetMem());
33 }
34 
FreeCoroutineStack(uint8_t * stack)35 void StackfulCoroutineManager::FreeCoroutineStack(uint8_t *stack)
36 {
37     if (stack != nullptr) {
38         PoolManager::GetMmapMemPool()->FreePool(stack, coroStackSizeBytes_);
39     }
40 }
41 
CreateWorkers(uint32_t howMany,Runtime * runtime,PandaVM * vm)42 void StackfulCoroutineManager::CreateWorkers(uint32_t howMany, Runtime *runtime, PandaVM *vm)
43 {
44     auto allocator = Runtime::GetCurrent()->GetInternalAllocator();
45 
46     auto *wMain = allocator->New<StackfulCoroutineWorker>(
47         runtime, vm, this, StackfulCoroutineWorker::ScheduleLoopType::FIBER, "[main] worker 0");
48     workers_.push_back(wMain);
49 
50     for (uint32_t i = 1; i < howMany; ++i) {
51         auto *w = allocator->New<StackfulCoroutineWorker>(
52             runtime, vm, this, StackfulCoroutineWorker::ScheduleLoopType::THREAD, "worker " + ToPandaString(i));
53         workers_.push_back(w);
54     }
55 
56     auto *mainCo = CreateMainCoroutine(runtime, vm);
57     mainCo->GetContext<StackfulCoroutineContext>()->SetWorker(wMain);
58     Coroutine::SetCurrent(mainCo);
59     activeWorkersCount_ = 1;  // 1 is for MAIN
60 
61     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::CreateWorkers(): waiting for workers startup";
62     while (activeWorkersCount_ < howMany) {
63         // NOTE(konstanting, #I67QXC): need timed wait?..
64         workersCv_.Wait(&workersLock_);
65     }
66 }
67 
OnWorkerShutdown()68 void StackfulCoroutineManager::OnWorkerShutdown()
69 {
70     os::memory::LockHolder lock(workersLock_);
71     --activeWorkersCount_;
72     workersCv_.Signal();
73     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::OnWorkerShutdown(): COMPLETED, workers left = "
74                            << activeWorkersCount_;
75 }
76 
OnWorkerStartup()77 void StackfulCoroutineManager::OnWorkerStartup()
78 {
79     os::memory::LockHolder lock(workersLock_);
80     ++activeWorkersCount_;
81     workersCv_.Signal();
82     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::OnWorkerStartup(): COMPLETED, active workers = "
83                            << activeWorkersCount_;
84 }
85 
DisableCoroutineSwitch()86 void StackfulCoroutineManager::DisableCoroutineSwitch()
87 {
88     GetCurrentWorker()->DisableCoroutineSwitch();
89 }
90 
EnableCoroutineSwitch()91 void StackfulCoroutineManager::EnableCoroutineSwitch()
92 {
93     GetCurrentWorker()->EnableCoroutineSwitch();
94 }
95 
IsCoroutineSwitchDisabled()96 bool StackfulCoroutineManager::IsCoroutineSwitchDisabled()
97 {
98     return GetCurrentWorker()->IsCoroutineSwitchDisabled();
99 }
100 
Initialize(CoroutineManagerConfig config,Runtime * runtime,PandaVM * vm)101 void StackfulCoroutineManager::Initialize(CoroutineManagerConfig config, Runtime *runtime, PandaVM *vm)
102 {
103     // set limits
104     coroStackSizeBytes_ = Runtime::GetCurrent()->GetOptions().GetCoroutineStackSizePages() * os::mem::GetPageSize();
105     if (coroStackSizeBytes_ != AlignUp(coroStackSizeBytes_, PANDA_POOL_ALIGNMENT_IN_BYTES)) {
106         size_t alignmentPages = PANDA_POOL_ALIGNMENT_IN_BYTES / os::mem::GetPageSize();
107         LOG(FATAL, COROUTINES) << "Coroutine stack size should be >= " << alignmentPages
108                                << " pages and should be aligned to " << alignmentPages << "-page boundary!";
109     }
110     size_t coroStackAreaSizeBytes = Runtime::GetCurrent()->GetOptions().GetCoroutinesStackMemLimit();
111     coroutineCountLimit_ = coroStackAreaSizeBytes / coroStackSizeBytes_;
112     jsMode_ = config.emulateJs;
113 
114     // create and activate workers
115     uint32_t targetNumberOfWorkers = (config.workersCount == CoroutineManagerConfig::WORKERS_COUNT_AUTO)
116                                          ? std::thread::hardware_concurrency()
117                                          : static_cast<uint32_t>(config.workersCount);
118     if (config.workersCount == CoroutineManagerConfig::WORKERS_COUNT_AUTO) {
119         LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager(): AUTO mode selected, will set number of coroutine "
120                                   "workers to number of CPUs = "
121                                << targetNumberOfWorkers;
122     }
123     os::memory::LockHolder lock(workersLock_);
124     CreateWorkers(targetNumberOfWorkers, runtime, vm);
125     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager(): successfully created and activated " << workers_.size()
126                            << " coroutine workers";
127     programCompletionEvent_ = Runtime::GetCurrent()->GetInternalAllocator()->New<GenericEvent>();
128 }
129 
Finalize()130 void StackfulCoroutineManager::Finalize()
131 {
132     os::memory::LockHolder lock(coroPoolLock_);
133 
134     auto allocator = Runtime::GetCurrent()->GetInternalAllocator();
135     allocator->Delete(programCompletionEvent_);
136     for (auto *co : coroutinePool_) {
137         co->DestroyInternalResources();
138         CoroutineManager::DestroyEntrypointfulCoroutine(co);
139     }
140     coroutinePool_.clear();
141 }
142 
AddToRegistry(Coroutine * co)143 void StackfulCoroutineManager::AddToRegistry(Coroutine *co)
144 {
145     os::memory::LockHolder lock(coroListLock_);
146     co->GetVM()->GetGC()->OnThreadCreate(co);
147     coroutines_.insert(co);
148     coroutineCount_++;
149 }
150 
RemoveFromRegistry(Coroutine * co)151 void StackfulCoroutineManager::RemoveFromRegistry(Coroutine *co)
152 {
153     coroutines_.erase(co);
154     coroutineCount_--;
155 }
156 
RegisterCoroutine(Coroutine * co)157 void StackfulCoroutineManager::RegisterCoroutine(Coroutine *co)
158 {
159     AddToRegistry(co);
160 }
161 
TerminateCoroutine(Coroutine * co)162 bool StackfulCoroutineManager::TerminateCoroutine(Coroutine *co)
163 {
164     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::TerminateCoroutine() started";
165     co->NativeCodeEnd();
166     co->UpdateStatus(ThreadStatus::TERMINATING);
167 
168     {
169         os::memory::LockHolder lList(coroListLock_);
170         RemoveFromRegistry(co);
171         // DestroyInternalResources()/CleanupInternalResources() must be called in one critical section with
172         // RemoveFromRegistry (under core_list_lock_). This functions transfer cards from coro's post_barrier buffer to
173         // UpdateRemsetThread internally. Situation when cards still remain and UpdateRemsetThread cannot visit the
174         // coro (because it is already removed) must be impossible.
175         if (Runtime::GetOptions().IsUseCoroutinePool() && co->HasManagedEntrypoint()) {
176             co->CleanupInternalResources();
177         } else {
178             co->DestroyInternalResources();
179         }
180         co->UpdateStatus(ThreadStatus::FINISHED);
181     }
182     Runtime::GetCurrent()->GetNotificationManager()->ThreadEndEvent(co);
183 
184     if (co->HasManagedEntrypoint()) {
185         UnblockWaiters(co->GetCompletionEvent());
186         CheckProgramCompletion();
187         GetCurrentWorker()->RequestFinalization(co);
188     } else if (co->HasNativeEntrypoint()) {
189         GetCurrentWorker()->RequestFinalization(co);
190     } else {
191         // entrypointless and NOT native: e.g. MAIN
192         // (do nothing, as entrypointless coroutines should should be destroyed manually)
193     }
194 
195     return false;
196 }
197 
GetActiveWorkersCount()198 size_t StackfulCoroutineManager::GetActiveWorkersCount()
199 {
200     os::memory::LockHolder lkWorkers(workersLock_);
201     return activeWorkersCount_;
202 }
203 
CheckProgramCompletion()204 void StackfulCoroutineManager::CheckProgramCompletion()
205 {
206     os::memory::LockHolder lkCompletion(programCompletionLock_);
207     size_t activeWorkerCoros = GetActiveWorkersCount();
208     if (coroutineCount_ == 1 + activeWorkerCoros) {  // 1 here is for MAIN
209         LOG(DEBUG, COROUTINES)
210             << "StackfulCoroutineManager::CheckProgramCompletion(): all coroutines finished execution!";
211         // programCompletionEvent_ acts as a stackful-friendly cond var
212         programCompletionEvent_->SetHappened();
213         UnblockWaiters(programCompletionEvent_);
214     } else {
215         LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::CheckProgramCompletion(): still "
216                                << coroutineCount_ - 1 - activeWorkerCoros << " coroutines exist...";
217     }
218 }
219 
CreateCoroutineContext(bool coroHasEntrypoint)220 CoroutineContext *StackfulCoroutineManager::CreateCoroutineContext(bool coroHasEntrypoint)
221 {
222     return CreateCoroutineContextImpl(coroHasEntrypoint);
223 }
224 
DeleteCoroutineContext(CoroutineContext * ctx)225 void StackfulCoroutineManager::DeleteCoroutineContext(CoroutineContext *ctx)
226 {
227     FreeCoroutineStack(static_cast<StackfulCoroutineContext *>(ctx)->GetStackLoAddrPtr());
228     Runtime::GetCurrent()->GetInternalAllocator()->Delete(ctx);
229 }
230 
GetCoroutineCount()231 size_t StackfulCoroutineManager::GetCoroutineCount()
232 {
233     return coroutineCount_;
234 }
235 
GetCoroutineCountLimit()236 size_t StackfulCoroutineManager::GetCoroutineCountLimit()
237 {
238     return coroutineCountLimit_;
239 }
240 
Launch(CompletionEvent * completionEvent,Method * entrypoint,PandaVector<Value> && arguments,CoroutineAffinity affinity)241 Coroutine *StackfulCoroutineManager::Launch(CompletionEvent *completionEvent, Method *entrypoint,
242                                             PandaVector<Value> &&arguments, CoroutineAffinity affinity)
243 {
244     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::Launch started";
245 
246     auto *result = LaunchImpl(completionEvent, entrypoint, std::move(arguments), affinity);
247     if (result == nullptr) {
248         ThrowOutOfMemoryError("Launch failed");
249     }
250 
251     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::Launch finished";
252     return result;
253 }
254 
Await(CoroutineEvent * awaitee)255 void StackfulCoroutineManager::Await(CoroutineEvent *awaitee)
256 {
257     ASSERT(awaitee != nullptr);
258     [[maybe_unused]] auto *waiter = Coroutine::GetCurrent();
259     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::Await started by " + waiter->GetName();
260     if (!GetCurrentWorker()->WaitForEvent(awaitee)) {
261         LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::Await finished (no await happened)";
262         return;
263     }
264     // NB: at this point the awaitee is likely already deleted
265     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::Await finished by " + waiter->GetName();
266 }
267 
UnblockWaiters(CoroutineEvent * blocker)268 void StackfulCoroutineManager::UnblockWaiters(CoroutineEvent *blocker)
269 {
270     os::memory::LockHolder lkWorkers(workersLock_);
271     ASSERT(blocker != nullptr);
272 #ifndef NDEBUG
273     {
274         os::memory::LockHolder lkBlocker(*blocker);
275         ASSERT(blocker->Happened());
276     }
277 #endif
278 
279     for (auto *w : workers_) {
280         w->UnblockWaiters(blocker);
281     }
282 }
283 
Schedule()284 void StackfulCoroutineManager::Schedule()
285 {
286     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::Schedule() request from "
287                            << Coroutine::GetCurrent()->GetName();
288     GetCurrentWorker()->RequestSchedule();
289 }
290 
EnumerateThreadsImpl(const ThreadManager::Callback & cb,unsigned int incMask,unsigned int xorMask) const291 bool StackfulCoroutineManager::EnumerateThreadsImpl(const ThreadManager::Callback &cb, unsigned int incMask,
292                                                     unsigned int xorMask) const
293 {
294     os::memory::LockHolder lock(coroListLock_);
295     for (auto *t : coroutines_) {
296         if (!ApplyCallbackToThread(cb, t, incMask, xorMask)) {
297             return false;
298         }
299     }
300     return true;
301 }
302 
SuspendAllThreads()303 void StackfulCoroutineManager::SuspendAllThreads()
304 {
305     os::memory::LockHolder lock(coroListLock_);
306     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::SuspendAllThreads started";
307     for (auto *t : coroutines_) {
308         t->SuspendImpl(true);
309     }
310     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::SuspendAllThreads finished";
311 }
312 
ResumeAllThreads()313 void StackfulCoroutineManager::ResumeAllThreads()
314 {
315     os::memory::LockHolder lock(coroListLock_);
316     for (auto *t : coroutines_) {
317         t->ResumeImpl(true);
318     }
319 }
320 
IsRunningThreadExist()321 bool StackfulCoroutineManager::IsRunningThreadExist()
322 {
323     UNREACHABLE();
324     // NOTE(konstanting): correct implementation. Which coroutine do we consider running?
325     return false;
326 }
327 
WaitForDeregistration()328 void StackfulCoroutineManager::WaitForDeregistration()
329 {
330     MainCoroutineCompleted();
331 }
332 
ReuseCoroutineInstance(Coroutine * co,CompletionEvent * completionEvent,Method * entrypoint,PandaVector<Value> && arguments,PandaString name)333 void StackfulCoroutineManager::ReuseCoroutineInstance(Coroutine *co, CompletionEvent *completionEvent,
334                                                       Method *entrypoint, PandaVector<Value> &&arguments,
335                                                       PandaString name)
336 {
337     auto *ctx = co->GetContext<CoroutineContext>();
338     co->ReInitialize(std::move(name), ctx,
339                      Coroutine::ManagedEntrypointInfo {completionEvent, entrypoint, std::move(arguments)});
340 }
341 
TryGetCoroutineFromPool()342 Coroutine *StackfulCoroutineManager::TryGetCoroutineFromPool()
343 {
344     os::memory::LockHolder lkPool(coroPoolLock_);
345     if (coroutinePool_.empty()) {
346         return nullptr;
347     }
348     Coroutine *co = coroutinePool_.back();
349     coroutinePool_.pop_back();
350     return co;
351 }
352 
ChooseWorkerForCoroutine(CoroutineAffinity affinity)353 StackfulCoroutineWorker *StackfulCoroutineManager::ChooseWorkerForCoroutine(CoroutineAffinity affinity)
354 {
355     switch (affinity) {
356         case CoroutineAffinity::SAME_WORKER: {
357             return GetCurrentWorker();
358         }
359         case CoroutineAffinity::NONE:
360         default: {
361             // choosing the least loaded worker
362             os::memory::LockHolder lkWorkers(workersLock_);
363             auto w = std::min_element(workers_.begin(), workers_.end(),
364                                       [](const StackfulCoroutineWorker *a, const StackfulCoroutineWorker *b) {
365                                           return a->GetLoadFactor() < b->GetLoadFactor();
366                                       });
367             return *w;
368         }
369     }
370 }
371 
LaunchImpl(CompletionEvent * completionEvent,Method * entrypoint,PandaVector<Value> && arguments,CoroutineAffinity affinity)372 Coroutine *StackfulCoroutineManager::LaunchImpl(CompletionEvent *completionEvent, Method *entrypoint,
373                                                 PandaVector<Value> &&arguments, CoroutineAffinity affinity)
374 {
375 #ifndef NDEBUG
376     GetCurrentWorker()->PrintRunnables("LaunchImpl begin");
377 #endif
378     auto coroName = entrypoint->GetFullName();
379 
380     Coroutine *co = nullptr;
381     if (Runtime::GetOptions().IsUseCoroutinePool()) {
382         co = TryGetCoroutineFromPool();
383     }
384     if (co != nullptr) {
385         ReuseCoroutineInstance(co, completionEvent, entrypoint, std::move(arguments), std::move(coroName));
386     } else {
387         co = CreateCoroutineInstance(completionEvent, entrypoint, std::move(arguments), std::move(coroName));
388     }
389     if (co == nullptr) {
390         LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::LaunchImpl: failed to create a coroutine!";
391         return co;
392     }
393     Runtime::GetCurrent()->GetNotificationManager()->ThreadStartEvent(co);
394 
395     auto *w = ChooseWorkerForCoroutine(affinity);
396     w->AddRunnableCoroutine(co, IsJsMode());
397 
398 #ifndef NDEBUG
399     GetCurrentWorker()->PrintRunnables("LaunchImpl end");
400 #endif
401     return co;
402 }
403 
MainCoroutineCompleted()404 void StackfulCoroutineManager::MainCoroutineCompleted()
405 {
406     // precondition: MAIN is already in the native mode
407     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::MainCoroutineCompleted(): STARTED";
408 
409     // block till only schedule loop coroutines are present
410     LOG(DEBUG, COROUTINES)
411         << "StackfulCoroutineManager::MainCoroutineCompleted(): waiting for other coroutines to complete";
412 
413     {
414         os::memory::LockHolder lkCompletion(programCompletionLock_);
415         auto *main = Coroutine::GetCurrent();
416         while (coroutineCount_ > 1 + GetActiveWorkersCount()) {  // 1 is for MAIN
417             programCompletionEvent_->SetNotHappened();
418             programCompletionEvent_->Lock();
419             programCompletionLock_.Unlock();
420             ScopedManagedCodeThread s(main);  // perf?
421             GetCurrentWorker()->WaitForEvent(programCompletionEvent_);
422             LOG(DEBUG, COROUTINES)
423                 << "StackfulCoroutineManager::MainCoroutineCompleted(): possibly spurious wakeup from wait...";
424             // NOTE(konstanting, #I67QXC): test for the spurious wakeup
425             programCompletionLock_.Lock();
426         }
427         ASSERT(coroutineCount_ == (1 + GetActiveWorkersCount()));
428     }
429 
430     // NOTE(konstanting, #I67QXC): correct state transitions for MAIN
431     GetCurrentContext()->MainThreadFinished();
432     GetCurrentContext()->EnterAwaitLoop();
433 
434     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::MainCoroutineCompleted(): stopping workers";
435     {
436         os::memory::LockHolder lock(workersLock_);
437         for (auto *worker : workers_) {
438             worker->SetActive(false);
439         }
440         while (activeWorkersCount_ > 1) {  // 1 is for MAIN
441             // NOTE(konstanting, #I67QXC): need timed wait?..
442             workersCv_.Wait(&workersLock_);
443         }
444     }
445 
446     LOG(DEBUG, COROUTINES)
447         << "StackfulCoroutineManager::MainCoroutineCompleted(): stopping await loop on the main worker";
448     while (coroutineCount_ > 1) {
449         GetCurrentWorker()->FinalizeFiberScheduleLoop();
450     }
451 
452     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::MainCoroutineCompleted(): deleting workers";
453     {
454         os::memory::LockHolder lkWorkers(workersLock_);
455         for (auto *worker : workers_) {
456             Runtime::GetCurrent()->GetInternalAllocator()->Delete(worker);
457         }
458     }
459 
460     LOG(DEBUG, COROUTINES) << "StackfulCoroutineManager::MainCoroutineCompleted(): DONE";
461 }
462 
GetCurrentContext()463 StackfulCoroutineContext *StackfulCoroutineManager::GetCurrentContext()
464 {
465     auto *co = Coroutine::GetCurrent();
466     return co->GetContext<StackfulCoroutineContext>();
467 }
468 
GetCurrentWorker()469 StackfulCoroutineWorker *StackfulCoroutineManager::GetCurrentWorker()
470 {
471     return GetCurrentContext()->GetWorker();
472 }
473 
IsJsMode()474 bool StackfulCoroutineManager::IsJsMode()
475 {
476     return jsMode_;
477 }
478 
DestroyEntrypointfulCoroutine(Coroutine * co)479 void StackfulCoroutineManager::DestroyEntrypointfulCoroutine(Coroutine *co)
480 {
481     if (Runtime::GetOptions().IsUseCoroutinePool() && co->HasManagedEntrypoint()) {
482         co->CleanUp();
483         os::memory::LockHolder lock(coroPoolLock_);
484         coroutinePool_.push_back(co);
485     } else {
486         CoroutineManager::DestroyEntrypointfulCoroutine(co);
487     }
488 }
489 
CreateCoroutineContextImpl(bool needStack)490 StackfulCoroutineContext *StackfulCoroutineManager::CreateCoroutineContextImpl(bool needStack)
491 {
492     uint8_t *stack = nullptr;
493     size_t stackSizeBytes = 0;
494     if (needStack) {
495         stack = AllocCoroutineStack();
496         if (stack == nullptr) {
497             return nullptr;
498         }
499         stackSizeBytes = coroStackSizeBytes_;
500     }
501     return Runtime::GetCurrent()->GetInternalAllocator()->New<StackfulCoroutineContext>(stack, stackSizeBytes);
502 }
503 
CreateNativeCoroutine(Runtime * runtime,PandaVM * vm,Coroutine::NativeEntrypointInfo::NativeEntrypointFunc entry,void * param,PandaString name)504 Coroutine *StackfulCoroutineManager::CreateNativeCoroutine(Runtime *runtime, PandaVM *vm,
505                                                            Coroutine::NativeEntrypointInfo::NativeEntrypointFunc entry,
506                                                            void *param, PandaString name)
507 {
508     if (GetCoroutineCount() >= GetCoroutineCountLimit()) {
509         // resource limit reached
510         return nullptr;
511     }
512     StackfulCoroutineContext *ctx = CreateCoroutineContextImpl(true);
513     if (ctx == nullptr) {
514         // do not proceed if we cannot create a context for the new coroutine
515         return nullptr;
516     }
517     auto *co = GetCoroutineFactory()(runtime, vm, std::move(name), ctx, Coroutine::NativeEntrypointInfo(entry, param));
518     ASSERT(co != nullptr);
519 
520     // Let's assume that even the "native" coroutine can eventually try to execute some managed code.
521     // In that case pre/post barrier buffers are necessary.
522     co->InitBuffers();
523     return co;
524 }
525 
DestroyNativeCoroutine(Coroutine * co)526 void StackfulCoroutineManager::DestroyNativeCoroutine(Coroutine *co)
527 {
528     DestroyEntrypointlessCoroutine(co);
529 }
530 
531 }  // namespace panda
532