• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-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 "libpandabase/utils/utils.h"
17 #include "plugins/ets/runtime/ets_exceptions.h"
18 #include "plugins/ets/runtime/ets_panda_file_items.h"
19 #include "plugins/ets/runtime/lambda_utils.h"
20 #include "plugins/ets/runtime/types/ets_method.h"
21 #include "plugins/ets/runtime/types/ets_object.h"
22 #include "plugins/ets/runtime/types/ets_void.h"
23 #include "plugins/ets/runtime/types/ets_string.h"
24 #include "runtime/include/thread.h"
25 #include "runtime/include/thread_scopes.h"
26 #include "runtime/handle_scope.h"
27 #include "runtime/handle_scope-inl.h"
28 #include "runtime/mem/refstorage/reference.h"
29 
30 namespace panda::ets::intrinsics {
31 
32 /**
33  * Class tracks GC tasks already processed by GC.
34  * Also the class tracks concurrent mark GC phase and calls
35  * the callback if it specified.
36  */
37 class GCTaskTracker : public mem::GCListener {
38 public:
39     void InitIfNeeded(mem::GC *gc);
40     bool IsInitialized();
41     void AddTaskId(uint64_t id);
42     bool HasId(uint64_t id);
43     void SetCallbackForTask(uint32_t taskId, mem::Reference *callbackRef);
44     void GCStarted(const GCTask &task, size_t heapSize) override;
45     void GCPhaseStarted(mem::GCPhase phase) override;
46     void GCFinished(const GCTask &task, size_t heapSizeBeforeGc, size_t heapSize) override;
47     void RemoveId(uint64_t id);
48 
49 private:
50     bool initialized_ = false;
51     std::vector<uint64_t> taskIds_ GUARDED_BY(lock_);
52     uint32_t currentTaskId_ = 0;
53     uint32_t callbackTaskId_ = 0;
54     mem::Reference *callbackRef_ = nullptr;
55     os::memory::Mutex lock_;
56 };
57 
InitIfNeeded(mem::GC * gc)58 void GCTaskTracker::InitIfNeeded(mem::GC *gc)
59 {
60     if (initialized_) {
61         return;
62     }
63     gc->AddListener(this);
64     initialized_ = true;
65 }
66 
IsInitialized()67 bool GCTaskTracker::IsInitialized()
68 {
69     return initialized_;
70 }
71 
AddTaskId(uint64_t id)72 void GCTaskTracker::AddTaskId(uint64_t id)
73 {
74     os::memory::LockHolder lock(lock_);
75     taskIds_.push_back(id);
76 }
77 
HasId(uint64_t id)78 bool GCTaskTracker::HasId(uint64_t id)
79 {
80     os::memory::LockHolder lock(lock_);
81     return std::find(taskIds_.begin(), taskIds_.end(), id) != taskIds_.end();
82 }
83 
SetCallbackForTask(uint32_t taskId,mem::Reference * callbackRef)84 void GCTaskTracker::SetCallbackForTask(uint32_t taskId, mem::Reference *callbackRef)
85 {
86     callbackTaskId_ = taskId;
87     callbackRef_ = callbackRef;
88 }
89 
GCStarted(const GCTask & task,size_t heapSize)90 void GCTaskTracker::GCStarted(const GCTask &task, [[maybe_unused]] size_t heapSize)
91 {
92     currentTaskId_ = task.GetId();
93 }
94 
GCPhaseStarted(mem::GCPhase phase)95 void GCTaskTracker::GCPhaseStarted(mem::GCPhase phase)
96 {
97     if (phase != mem::GCPhase::GC_PHASE_MARK || callbackRef_ == nullptr || currentTaskId_ != callbackTaskId_) {
98         return;
99     }
100     auto *coroutine = EtsCoroutine::GetCurrent();
101     auto *obj = reinterpret_cast<EtsObject *>(coroutine->GetPandaVM()->GetGlobalObjectStorage()->Get(callbackRef_));
102     Value arg(obj->GetCoreType());
103     os::memory::ReadLockHolder lock(*coroutine->GetPandaVM()->GetRendezvous()->GetMutatorLock());
104     LambdaUtils::InvokeVoid(coroutine, obj);
105 }
106 
GCFinished(const GCTask & task,size_t heapSizeBeforeGc,size_t heapSize)107 void GCTaskTracker::GCFinished(const GCTask &task, [[maybe_unused]] size_t heapSizeBeforeGc,
108                                [[maybe_unused]] size_t heapSize)
109 {
110     RemoveId(task.GetId());
111 }
112 
RemoveId(uint64_t id)113 void GCTaskTracker::RemoveId(uint64_t id)
114 {
115     currentTaskId_ = 0;
116     if (id == callbackTaskId_ && callbackRef_ != nullptr) {
117         EtsCoroutine::GetCurrent()->GetPandaVM()->GetGlobalObjectStorage()->Remove(callbackRef_);
118         callbackRef_ = nullptr;
119     }
120     if (id != 0) {
121         os::memory::LockHolder lock(lock_);
122         auto it = std::find(taskIds_.begin(), taskIds_.end(), id);
123         // There may be no such id if the corresponding GC has been triggered not by startGC
124         if (it != taskIds_.end()) {
125             taskIds_.erase(it);
126         }
127     }
128 }
129 
130 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
131 GCTaskTracker g_gGctaskTracker;
132 
ClampToSizeT(EtsLong n)133 static inline size_t ClampToSizeT(EtsLong n)
134 {
135     if constexpr (sizeof(EtsLong) > sizeof(size_t)) {
136         if (UNLIKELY(n > static_cast<EtsLong>(std::numeric_limits<size_t>::max()))) {
137             return std::numeric_limits<size_t>::max();
138         }
139     }
140     return n;
141 }
142 
GCCauseFromInt(EtsInt cause)143 static GCTaskCause GCCauseFromInt(EtsInt cause)
144 {
145     if (cause == 0_I) {
146         return GCTaskCause::YOUNG_GC_CAUSE;
147     }
148     if (cause == 1_I) {
149         return GCTaskCause::HEAP_USAGE_THRESHOLD_CAUSE;
150     }
151     if (cause == 2_I) {
152         return GCTaskCause::MIXED;
153     }
154     if (cause == 3_I) {
155         return GCTaskCause::OOM_CAUSE;
156     }
157     return GCTaskCause::INVALID_CAUSE;
158 }
159 
160 /**
161  * The function triggers specific GC.
162  * @param cause - integer denotes type of GC. Possible values are: YOUNG_CAUSE = 0, THRESHOLD_CAUSE = 1,
163  *                MIXED_CAUSE = 2, FULL_CAUSE = 3
164  * @return gc id. The id should be passed to waitForFinishGC to ensure the GC is finished.
165  *  - The function may return 0 in case the GC is executed in-place. It means there is no need to wait such GC.
166  *  - The function may return -1 in case the task is canceled.
167  */
StdGCStartGC(EtsInt cause,EtsObject * callback)168 extern "C" EtsLong StdGCStartGC(EtsInt cause, EtsObject *callback)
169 {
170     auto *coroutine = EtsCoroutine::GetCurrent();
171     ASSERT(coroutine != nullptr);
172     bool runGcInPlace = Runtime::GetOptions().IsRunGcInPlace("ets");
173 
174     GCTaskCause reason = GCCauseFromInt(cause);
175     if (reason == GCTaskCause::INVALID_CAUSE) {
176         ThrowEtsException(coroutine, panda_file_items::class_descriptors::ILLEGAL_ARGUMENT_EXCEPTION,
177                           "Invalid GC cause");
178         return -1;
179     }
180     auto *gc = coroutine->GetVM()->GetGC();
181     if (!gc->CheckGCCause(reason)) {
182         PandaStringStream eMsg;
183         eMsg << mem::GCStringFromType(gc->GetType()) << " does not support " << reason << " cause";
184         ThrowEtsException(coroutine, panda_file_items::class_descriptors::ILLEGAL_ARGUMENT_EXCEPTION, eMsg.str());
185         return -1;
186     }
187     g_gGctaskTracker.InitIfNeeded(gc);
188     auto task = MakePandaUnique<GCTask>(reason);
189     uint32_t id = task->GetId();
190     if (callback != nullptr) {
191         auto *callbackRef = coroutine->GetPandaVM()->GetGlobalObjectStorage()->Add(callback->GetCoreType(),
192                                                                                    mem::Reference::ObjectType::GLOBAL);
193         g_gGctaskTracker.SetCallbackForTask(id, callbackRef);
194         // Run GC in place, because need to run callback in managed part
195         runGcInPlace = true;
196     }
197 
198     // Young GC runs in place
199     if (reason == GCTaskCause::YOUNG_GC_CAUSE) {
200         runGcInPlace = true;
201     }
202     if (runGcInPlace) {
203         return gc->WaitForGCInManaged(*task) ? 0 : -1;
204     }
205     // Run GC in GC-thread
206     if ((reason == GCTaskCause::HEAP_USAGE_THRESHOLD_CAUSE) && gc->IsPostponeEnabled()) {
207         ThrowEtsException(coroutine, panda_file_items::class_descriptors::ILLEGAL_STATE_EXCEPTION,
208                           "Calling GC threshold not in place after calling postponeGCStart");
209         return -1;
210     }
211     g_gGctaskTracker.AddTaskId(id);
212     if (!gc->Trigger(std::move(task))) {
213         g_gGctaskTracker.RemoveId(id);
214         return -1;
215     }
216     return static_cast<EtsLong>(id);
217 }
218 
219 /**
220  * The function returns when the specified GC gets finished.
221  * @param gc_id - id of the GC which is returned by startGc.
222  * If gc_id is 0 or -1 the function returns immediately.
223  */
StdGCWaitForFinishGC(EtsLong gcId)224 extern "C" EtsVoid *StdGCWaitForFinishGC(EtsLong gcId)
225 {
226     ManagedThread *thread = ManagedThread::GetCurrent();
227     ASSERT(thread != nullptr);
228     if (gcId <= 0) {
229         return EtsVoid::GetInstance();
230     }
231     auto id = static_cast<uint64_t>(gcId);
232     ASSERT(g_gGctaskTracker.IsInitialized());
233     ScopedNativeCodeThread s(thread);
234     while (g_gGctaskTracker.HasId(id)) {
235         constexpr uint64_t WAIT_TIME_MS = 10;
236         os::thread::NativeSleep(WAIT_TIME_MS);
237     }
238     return EtsVoid::GetInstance();
239 }
240 
StdGCIsScheduledGCTriggered()241 extern "C" EtsBoolean StdGCIsScheduledGCTriggered()
242 {
243     auto thread = ManagedThread::GetCurrent();
244     ASSERT(thread != nullptr);
245     auto *vm = thread->GetVM();
246     auto *trigger = vm->GetGCTrigger();
247 
248     if (trigger->GetType() != mem::GCTriggerType::ON_NTH_ALLOC) {
249         return ToEtsBoolean(false);
250     }
251     auto schedTrigger = reinterpret_cast<mem::SchedGCOnNthAllocTrigger *>(vm->GetGCTrigger());
252     return ToEtsBoolean(schedTrigger->IsTriggered());
253 }
254 
StdGCPostponeGCStart()255 extern "C" EtsVoid *StdGCPostponeGCStart()
256 {
257     auto coroutine = EtsCoroutine::GetCurrent();
258     ASSERT(coroutine != nullptr);
259     auto *gc = coroutine->GetVM()->GetGC();
260     if (!gc->IsPostponeGCSupported()) {
261         ThrowEtsException(coroutine, panda_file_items::class_descriptors::UNSUPPORTED_OPERATION_EXCEPTION,
262                           "GC postpone is not supported for this GC type");
263         return EtsVoid::GetInstance();
264     }
265     if (gc->IsPostponeEnabled()) {
266         ThrowEtsException(coroutine, panda_file_items::class_descriptors::ILLEGAL_STATE_EXCEPTION,
267                           "Calling postponeGCStart without calling postponeGCEnd");
268         return EtsVoid::GetInstance();
269     }
270     gc->PostponeGCStart();
271     return EtsVoid::GetInstance();
272 }
273 
StdGCPostponeGCEnd()274 extern "C" EtsVoid *StdGCPostponeGCEnd()
275 {
276     auto coroutine = EtsCoroutine::GetCurrent();
277     ASSERT(coroutine != nullptr);
278     auto *gc = coroutine->GetVM()->GetGC();
279     if (!gc->IsPostponeGCSupported()) {
280         ThrowEtsException(coroutine, panda_file_items::class_descriptors::UNSUPPORTED_OPERATION_EXCEPTION,
281                           "GC postpone is not supported for this GC type");
282         return EtsVoid::GetInstance();
283     }
284     if (!gc->IsPostponeEnabled()) {
285         ThrowEtsException(coroutine, panda_file_items::class_descriptors::ILLEGAL_STATE_EXCEPTION,
286                           "Calling postponeGCEnd without calling postponeGCStart");
287         return EtsVoid::GetInstance();
288     }
289     gc->PostponeGCEnd();
290     return EtsVoid::GetInstance();
291 }
292 
293 template <class ResArrayType>
StdGCAllocatePinnedPrimitiveTypeArray(EtsLong length)294 [[nodiscard]] static ResArrayType *StdGCAllocatePinnedPrimitiveTypeArray(EtsLong length)
295 {
296     auto *coroutine = EtsCoroutine::GetCurrent();
297     ASSERT(coroutine != nullptr);
298 
299     if (length < 0) {
300         ThrowEtsException(coroutine, panda_file_items::class_descriptors::NEGATIVE_ARRAY_SIZE_EXCEPTION,
301                           "The value must be non negative");
302         return nullptr;
303     }
304     auto *vm = coroutine->GetVM();
305     if (!vm->GetGC()->IsPinningSupported()) {
306         ThrowEtsException(coroutine, panda_file_items::class_descriptors::UNSUPPORTED_OPERATION_EXCEPTION,
307                           "Object pinning does not support with current GC");
308         return nullptr;
309     }
310     auto *array = ResArrayType::Create(length);
311 
312     if (array == nullptr) {
313         PandaStringStream ss;
314         ss << "Could not allocate array of " << length << " elements";
315         ThrowEtsException(coroutine, panda_file_items::class_descriptors::OUT_OF_MEMORY_ERROR, ss.str());
316         return nullptr;
317     }
318     vm->GetHeapManager()->PinObject(array->GetCoreType());
319 
320     return array;
321 }
322 
StdGCAllocatePinnedBooleanArray(EtsLong length)323 extern "C" EtsBooleanArray *StdGCAllocatePinnedBooleanArray(EtsLong length)
324 {
325     return StdGCAllocatePinnedPrimitiveTypeArray<EtsBooleanArray>(length);
326 }
327 
StdGCAllocatePinnedByteArray(EtsLong length)328 extern "C" EtsByteArray *StdGCAllocatePinnedByteArray(EtsLong length)
329 {
330     return StdGCAllocatePinnedPrimitiveTypeArray<EtsByteArray>(length);
331 }
332 
StdGCAllocatePinnedCharArray(EtsLong length)333 extern "C" EtsCharArray *StdGCAllocatePinnedCharArray(EtsLong length)
334 {
335     return StdGCAllocatePinnedPrimitiveTypeArray<EtsCharArray>(length);
336 }
337 
StdGCAllocatePinnedShortArray(EtsLong length)338 extern "C" EtsShortArray *StdGCAllocatePinnedShortArray(EtsLong length)
339 {
340     return StdGCAllocatePinnedPrimitiveTypeArray<EtsShortArray>(length);
341 }
342 
StdGCAllocatePinnedIntArray(EtsLong length)343 extern "C" EtsIntArray *StdGCAllocatePinnedIntArray(EtsLong length)
344 {
345     return StdGCAllocatePinnedPrimitiveTypeArray<EtsIntArray>(length);
346 }
347 
StdGCAllocatePinnedLongArray(EtsLong length)348 extern "C" EtsLongArray *StdGCAllocatePinnedLongArray(EtsLong length)
349 {
350     return StdGCAllocatePinnedPrimitiveTypeArray<EtsLongArray>(length);
351 }
352 
StdGCAllocatePinnedFloatArray(EtsLong length)353 extern "C" EtsFloatArray *StdGCAllocatePinnedFloatArray(EtsLong length)
354 {
355     return StdGCAllocatePinnedPrimitiveTypeArray<EtsFloatArray>(length);
356 }
357 
StdGCAllocatePinnedDoubleArray(EtsLong length)358 extern "C" EtsDoubleArray *StdGCAllocatePinnedDoubleArray(EtsLong length)
359 {
360     return StdGCAllocatePinnedPrimitiveTypeArray<EtsDoubleArray>(length);
361 }
362 
StdGCGetObjectSpaceType(EtsObject * obj)363 extern "C" EtsInt StdGCGetObjectSpaceType(EtsObject *obj)
364 {
365     if (obj == nullptr) {
366         auto *coroutine = EtsCoroutine::GetCurrent();
367         ASSERT(coroutine != nullptr);
368         ThrowEtsException(coroutine, panda_file_items::class_descriptors::NULL_POINTER_EXCEPTION, "Non heap object");
369         return SpaceTypeToIndex(SpaceType::SPACE_TYPE_UNDEFINED);
370     }
371 
372     auto *vm = Thread::GetCurrent()->GetVM();
373     SpaceType objSpaceType =
374         PoolManager::GetMmapMemPool()->GetSpaceTypeForAddr(static_cast<void *>(obj->GetCoreType()));
375     if (objSpaceType == SpaceType::SPACE_TYPE_OBJECT && vm->GetGC()->IsGenerational()) {
376         if (vm->GetHeapManager()->IsObjectInYoungSpace(obj->GetCoreType())) {
377             const EtsInt youngSpace = 4;
378             return youngSpace;
379         }
380         const EtsInt tenuredSpace = 5;
381         return tenuredSpace;
382     }
383     return SpaceTypeToIndex(objSpaceType);
384 }
385 
StdGCPinObject(EtsObject * obj)386 extern "C" EtsVoid *StdGCPinObject(EtsObject *obj)
387 {
388     auto *coroutine = EtsCoroutine::GetCurrent();
389     ASSERT(coroutine != nullptr);
390     if (obj == nullptr) {
391         ThrowEtsException(coroutine, panda_file_items::class_descriptors::NULL_POINTER_EXCEPTION,
392                           "The value must be an object");
393         return EtsVoid::GetInstance();
394     }
395 
396     auto *vm = coroutine->GetVM();
397     auto *gc = vm->GetGC();
398     if (!gc->IsPinningSupported()) {
399         ThrowEtsException(coroutine, panda_file_items::class_descriptors::UNSUPPORTED_OPERATION_EXCEPTION,
400                           "Object pinning does not support with current gc");
401         return EtsVoid::GetInstance();
402     }
403     vm->GetHeapManager()->PinObject(obj->GetCoreType());
404     return EtsVoid::GetInstance();
405 }
406 
StdGCUnpinObject(EtsObject * obj)407 extern "C" void StdGCUnpinObject(EtsObject *obj)
408 {
409     if (obj == nullptr) {
410         auto *coroutine = EtsCoroutine::GetCurrent();
411         ASSERT(coroutine != nullptr);
412         ThrowEtsException(coroutine, panda_file_items::class_descriptors::NULL_POINTER_EXCEPTION,
413                           "The value must be an object");
414         return;
415     }
416 
417     auto *vm = Thread::GetCurrent()->GetVM();
418     vm->GetHeapManager()->UnpinObject(obj->GetCoreType());
419 }
420 
StdGCGetObjectAddress(EtsObject * obj)421 extern "C" EtsLong StdGCGetObjectAddress(EtsObject *obj)
422 {
423     return obj == nullptr ? 0 : reinterpret_cast<EtsLong>(obj->GetCoreType());
424 }
425 
426 // Function schedules GC before n-th allocation by setting counter to the specific GC trigger.
427 // Another call may reset the counter.  In this case the last counter will be used to trigger the GC.
StdGCScheduleGCAfterNthAlloc(EtsInt counter,EtsInt cause)428 extern "C" EtsVoid *StdGCScheduleGCAfterNthAlloc(EtsInt counter, EtsInt cause)
429 {
430     auto *coroutine = EtsCoroutine::GetCurrent();
431     ASSERT(coroutine != nullptr);
432 
433     if (counter < 0) {
434         ThrowEtsException(coroutine, panda_file_items::class_descriptors::ILLEGAL_ARGUMENT_EXCEPTION,
435                           "counter for allocation is negative");
436         return EtsVoid::GetInstance();
437     }
438     GCTaskCause reason = GCCauseFromInt(cause);
439     if (reason == GCTaskCause::INVALID_CAUSE) {
440         ThrowEtsException(coroutine, panda_file_items::class_descriptors::ILLEGAL_ARGUMENT_EXCEPTION,
441                           "Invalid GC cause");
442         return EtsVoid::GetInstance();
443     }
444 
445     auto *vm = coroutine->GetVM();
446     auto *gc = vm->GetGC();
447     if (!gc->CheckGCCause(reason)) {
448         PandaStringStream eMsg;
449         eMsg << mem::GCStringFromType(gc->GetType()) << " does not support " << reason << " cause";
450         ThrowEtsException(coroutine, panda_file_items::class_descriptors::ILLEGAL_ARGUMENT_EXCEPTION, eMsg.str());
451         return EtsVoid::GetInstance();
452     }
453     mem::GCTrigger *trigger = vm->GetGCTrigger();
454     if (trigger->GetType() != mem::GCTriggerType::ON_NTH_ALLOC) {
455         ThrowEtsException(coroutine, panda_file_items::class_descriptors::RUNTIME_EXCEPTION,
456                           "VM is running with unsupported GC trigger");
457         return EtsVoid::GetInstance();
458     }
459     EtsVoid *voidInstance = EtsVoid::GetInstance();
460     auto schedTrigger = reinterpret_cast<mem::SchedGCOnNthAllocTrigger *>(trigger);
461     schedTrigger->ScheduleGc(reason, counter);
462     return voidInstance;
463 }
464 
StdGetFreeHeapSize()465 extern "C" EtsLong StdGetFreeHeapSize()
466 {
467     const auto *coroutine = EtsCoroutine::GetCurrent();
468     ASSERT(coroutine != nullptr);
469     return static_cast<EtsLong>(coroutine->GetPandaVM()->GetHeapManager()->GetFreeMemory());
470 }
471 
StdGetUsedHeapSize()472 extern "C" EtsLong StdGetUsedHeapSize()
473 {
474     const auto *coroutine = EtsCoroutine::GetCurrent();
475     ASSERT(coroutine != nullptr);
476     return static_cast<EtsLong>(coroutine->GetPandaVM()->GetHeapManager()->GetTotalMemory());
477 }
478 
StdGetReservedHeapSize()479 extern "C" EtsLong StdGetReservedHeapSize()
480 {
481     const auto *coroutine = EtsCoroutine::GetCurrent();
482     ASSERT(coroutine != nullptr);
483     return static_cast<EtsLong>(coroutine->GetPandaVM()->GetHeapManager()->GetMaxMemory());
484 }
485 
StdGCRegisterNativeAllocation(EtsLong size)486 extern "C" void StdGCRegisterNativeAllocation(EtsLong size)
487 {
488     auto *coroutine = EtsCoroutine::GetCurrent();
489     ASSERT(coroutine != nullptr);
490     if (size < 0) {
491         ThrowEtsException(coroutine, panda_file_items::class_descriptors::NEGATIVE_ARRAY_SIZE_EXCEPTION,
492                           "The value must be non negative");
493         return;
494     }
495 
496     ScopedNativeCodeThread s(ManagedThread::GetCurrent());
497     coroutine->GetVM()->GetGC()->RegisterNativeAllocation(ClampToSizeT(size));
498 }
499 
StdGCRegisterNativeFree(EtsLong size)500 extern "C" void StdGCRegisterNativeFree(EtsLong size)
501 {
502     auto *coroutine = EtsCoroutine::GetCurrent();
503     ASSERT(coroutine != nullptr);
504     if (size < 0) {
505         ThrowEtsException(coroutine, panda_file_items::class_descriptors::NEGATIVE_ARRAY_SIZE_EXCEPTION,
506                           "The value must be non negative");
507         return;
508     }
509 
510     ScopedNativeCodeThread s(ManagedThread::GetCurrent());
511     coroutine->GetVM()->GetGC()->RegisterNativeFree(ClampToSizeT(size));
512 }
513 
514 }  // namespace panda::ets::intrinsics
515