• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2023-2025 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 <cstring>
17 #include <string>
18 
19 #include "plugins/ets/runtime/interop_js/interop_common.h"
20 #include "plugins/ets/runtime/interop_js/ets_proxy/shared_reference_storage.h"
21 #include "plugins/ets/runtime/interop_js/interop_context.h"
22 #include "plugins/ets/runtime/interop_js/xgc/xgc.h"
23 
24 namespace ark::ets::interop::js::ets_proxy {
25 
26 class SharedReferenceSanity {
27 public:
Kill(SharedReference * ref)28     static ALWAYS_INLINE void Kill(SharedReference *ref)
29     {
30         ASSERT(ref->jsRef_ != nullptr);
31         ref->jsRef_ = nullptr;
32         ref->ctx_ = nullptr;
33         ref->flags_.ClearFlags();
34     }
35 
CheckAlive(SharedReference * ref)36     static ALWAYS_INLINE bool CheckAlive(SharedReference *ref)
37     {
38         return ref->jsRef_ != nullptr && !ref->flags_.IsEmpty();
39     }
40 };
41 
42 // NOTE(ipetrov, 20146): It's tempoary solution for SharedReferencesStorage. All napi calls should in native scope
43 class ScopedNativeCodeThreadIfNeeded {
44 public:
ScopedNativeCodeThreadIfNeeded(ManagedThread * thread)45     explicit ScopedNativeCodeThreadIfNeeded(ManagedThread *thread) : thread_(thread)
46     {
47         ASSERT(thread_ != nullptr);
48         if (thread_->IsInNativeCode()) {
49             needToEndNativeCode_ = false;
50         } else {
51             thread_->NativeCodeBegin();
52         }
53     }
54     NO_COPY_SEMANTIC(ScopedNativeCodeThreadIfNeeded);
55     NO_MOVE_SEMANTIC(ScopedNativeCodeThreadIfNeeded);
56 
~ScopedNativeCodeThreadIfNeeded()57     ~ScopedNativeCodeThreadIfNeeded()
58     {
59         if (needToEndNativeCode_) {
60             thread_->NativeCodeEnd();
61         }
62     }
63 
64 private:
65     ManagedThread *thread_ {nullptr};
66     bool needToEndNativeCode_ {true};
67 };
68 
69 SharedReferenceStorage *SharedReferenceStorage::sharedStorage_ = nullptr;
70 
71 /* static */
Create(PandaEtsVM * vm)72 PandaUniquePtr<SharedReferenceStorage> SharedReferenceStorage::Create(PandaEtsVM *vm)
73 {
74     if (sharedStorage_ != nullptr) {
75         return nullptr;
76     }
77     size_t realSize = SharedReferenceStorage::MAX_POOL_SIZE;
78 
79     void *data = os::mem::MapRWAnonymousRaw(realSize);
80     if (data == nullptr) {
81         INTEROP_LOG(FATAL) << "Cannot allocate MemPool";
82         return nullptr;
83     }
84     auto sharedStorage = MakePandaUnique<SharedReferenceStorage>(vm, data, realSize);
85     sharedStorage_ = sharedStorage.get();
86     vm->AddRootProvider(sharedStorage_);
87     return sharedStorage;
88 }
89 
~SharedReferenceStorage()90 SharedReferenceStorage::~SharedReferenceStorage()
91 {
92     vm_->RemoveRootProvider(this);
93     sharedStorage_ = nullptr;
94 }
95 
GetReference(EtsObject * etsObject) const96 SharedReference *SharedReferenceStorage::GetReference(EtsObject *etsObject) const
97 {
98     os::memory::ReadLockHolder lock(storageLock_);
99     ASSERT(SharedReference::HasReference(etsObject));
100     return GetItemByIndex(SharedReference::ExtractMaybeIndex(etsObject));
101 }
102 
NapiUnwrap(napi_env env,napi_value jsObject,void ** result)103 napi_status NapiUnwrap(napi_env env, napi_value jsObject, void **result)
104 {
105 #if defined(PANDA_JS_ETS_HYBRID_MODE) || defined(PANDA_TARGET_OHOS)
106     return napi_xref_unwrap(env, jsObject, result);
107 #else
108     return napi_unwrap(env, jsObject, result);
109 #endif
110 }
111 
112 /* static */
ExtractMaybeReference(napi_env env,napi_value jsObject)113 SharedReference *ExtractMaybeReference(napi_env env, napi_value jsObject)
114 {
115     void *data;
116     if (UNLIKELY(NapiUnwrap(env, jsObject, &data) != napi_ok)) {
117         return nullptr;
118     }
119     if (UNLIKELY(data == nullptr)) {
120         return nullptr;
121     }
122     // Atomic with acquire order reason: load visibility after shared reference initialization
123     return AtomicLoad(static_cast<SharedReference **>(data), std::memory_order_acquire);
124 }
125 
GetReference(napi_env env,napi_value jsObject) const126 SharedReference *SharedReferenceStorage::GetReference(napi_env env, napi_value jsObject) const
127 {
128     ScopedNativeCodeThread nativeScope(EtsCoroutine::GetCurrent());
129     void *data = ExtractMaybeReference(env, jsObject);
130     if (UNLIKELY(data == nullptr)) {
131         return nullptr;
132     }
133     return GetReference(data);
134 }
135 
GetReference(void * data) const136 SharedReference *SharedReferenceStorage::GetReference(void *data) const
137 {
138     os::memory::ReadLockHolder<os::memory::RWLock, DEBUG_BUILD> lock(storageLock_);
139     auto *sharedRef = static_cast<SharedReference *>(data);
140     ASSERT(IsValidItem(sharedRef));
141     ASSERT(SharedReferenceSanity::CheckAlive(sharedRef));
142     return sharedRef;
143 }
144 
HasReference(EtsObject * etsObject,napi_env env)145 bool SharedReferenceStorage::HasReference(EtsObject *etsObject, napi_env env)
146 {
147     os::memory::ReadLockHolder lock(storageLock_);
148     if (!SharedReference::HasReference(etsObject)) {
149         return false;
150     }
151     uint32_t index = SharedReference::ExtractMaybeIndex(etsObject);
152     do {
153         const SharedReference *currentRef = GetItemByIndex(index);
154         if (currentRef->ctx_->GetXGCVmAdaptor()->HasSameEnv(env)) {
155             return true;
156         }
157         index = currentRef->flags_.GetNextIndex();
158     } while (index != 0U);
159     return false;
160 }
161 
GetJsObject(EtsObject * etsObject,napi_env env) const162 napi_value SharedReferenceStorage::GetJsObject(EtsObject *etsObject, napi_env env) const
163 {
164     storageLock_.ReadLock();
165     const SharedReference *currentRef = GetItemByIndex(SharedReference::ExtractMaybeIndex(etsObject));
166     // CC-OFFNXT(G.CTL.03) false positive
167     do {
168         if (currentRef->ctx_->GetXGCVmAdaptor()->HasSameEnv(env)) {
169             auto ref = currentRef->jsRef_;
170             storageLock_.Unlock();
171             ScopedNativeCodeThreadIfNeeded s(EtsCoroutine::GetCurrent());
172             napi_value jsValue;
173             NAPI_CHECK_FATAL(napi_get_reference_value(env, ref, &jsValue));
174             return jsValue;
175         }
176         uint32_t index = currentRef->flags_.GetNextIndex();
177         if (index == 0U) {
178             // NOTE(MockMockBlack, #24062): to be replaced with a runtime exception
179             InteropFatal("No JS Object for SharedReference (" + std::to_string(reinterpret_cast<std::uintptr_t>(this)) +
180                          ") and napi_env: " + std::to_string(reinterpret_cast<std::uintptr_t>(env)));
181         }
182         currentRef = GetItemByIndex(index);
183     } while (true);
184 }
185 
HasReferenceWithCtx(SharedReference * ref,InteropCtx * ctx) const186 bool SharedReferenceStorage::HasReferenceWithCtx(SharedReference *ref, InteropCtx *ctx) const
187 {
188     uint32_t idx;
189     do {
190         if (ref->ctx_ == ctx) {
191             return true;
192         }
193         idx = ref->flags_.GetNextIndex();
194     } while (idx != 0U);
195     return false;
196 }
197 
DeleteSharedReferenceRefCallback(napi_env env,void * data,void * hint)198 static void DeleteSharedReferenceRefCallback([[maybe_unused]] napi_env env, void *data, [[maybe_unused]] void *hint)
199 {
200     delete static_cast<SharedReference **>(data);
201 }
TriggerXGCIfNeeded(InteropCtx * ctx)202 static void TriggerXGCIfNeeded([[maybe_unused]] InteropCtx *ctx)
203 {
204     // NOTE(ipetrov, XGC): XGC should be triggered only in hybrid mode. Need to remove when interop will work only in
205     // hybrid mode
206 #ifdef PANDA_JS_ETS_HYBRID_MODE
207     XGC::GetInstance()->TriggerGcIfNeeded(ctx->GetPandaEtsVM()->GetGC());
208 #endif  // PANDA_JS_ETS_HYBRID_MODE
209 }
210 
CreateXRef(InteropCtx * ctx,napi_value jsObject,napi_ref * result,const SharedReferenceStorage::PreInitJSObjectCallback & preInitCallback=nullptr)211 static SharedReference **CreateXRef(InteropCtx *ctx, napi_value jsObject, napi_ref *result,
212                                     const SharedReferenceStorage::PreInitJSObjectCallback &preInitCallback = nullptr)
213 {
214     auto *currentCoro = EtsCoroutine::GetCurrent();
215     ScopedNativeCodeThreadIfNeeded scope(currentCoro);
216     napi_env env = ctx->GetJSEnv();
217     // Deleter can be called after Runtime::Destroy, so InternalAllocator can not be used
218     auto **refRef = new SharedReference *(nullptr);
219     if (preInitCallback != nullptr) {
220         jsObject = preInitCallback(refRef);
221     }
222     napi_status status = napi_ok;
223 #if defined(PANDA_JS_ETS_HYBRID_MODE) || defined(PANDA_TARGET_OHOS)
224     status = napi_wrap_with_xref(env, jsObject, refRef, DeleteSharedReferenceRefCallback, result);
225 #else
226     status = napi_wrap(env, jsObject, refRef, DeleteSharedReferenceRefCallback, nullptr, nullptr);
227     if (status == napi_ok) {
228         status = napi_create_reference(env, jsObject, 1, result);
229     }
230 #endif
231     if (UNLIKELY(status != napi_ok)) {
232         DeleteSharedReferenceRefCallback(env, refRef, nullptr);
233         if (currentCoro->HasPendingException()) {
234             ctx->ForwardEtsException(currentCoro);
235         }
236         ASSERT(ctx->SanityJSExceptionPending());
237         return nullptr;
238     }
239     return refRef;
240 }
241 
242 template <SharedReference::InitFn REF_INIT>
CreateReference(InteropCtx * ctx,EtsHandle<EtsObject> etsObject,napi_ref jsRef)243 SharedReference *SharedReferenceStorage::CreateReference(InteropCtx *ctx, EtsHandle<EtsObject> etsObject,
244                                                          napi_ref jsRef)
245 {
246     SharedReference *sharedRef = AllocItem();
247     if (UNLIKELY(sharedRef == nullptr)) {
248         ctx->ThrowJSError(ctx->GetJSEnv(), "no free space for SharedReference");
249         return nullptr;
250     }
251     SharedReference *lastRefInChain = nullptr;
252     // If EtsObject has been already marked as interop object then add new created SharedReference for a new interop
253     // context to chain of references with this EtsObject
254     ASSERT(etsObject.GetPtr() != nullptr);
255     if (etsObject->HasInteropIndex()) {
256         lastRefInChain = GetItemByIndex(etsObject->GetInteropIndex());
257         ASSERT(!HasReferenceWithCtx(lastRefInChain, ctx));
258         uint32_t index = lastRefInChain->flags_.GetNextIndex();
259         while (index != 0U) {
260             lastRefInChain = GetItemByIndex(index);
261             index = lastRefInChain->flags_.GetNextIndex();
262         }
263     }
264     auto newRefIndex = GetIndexByItem(sharedRef);
265     (sharedRef->*REF_INIT)(ctx, etsObject.GetPtr(), jsRef, newRefIndex);
266     if (lastRefInChain != nullptr) {
267         lastRefInChain->flags_.SetNextIndex(newRefIndex);
268     }
269     // Ref allocated during XGC, so need to mark it on Remark to avoid removing
270     if (isXGCinProgress_) {
271         refsAllocatedDuringXGC_.insert(sharedRef);
272     }
273     LOG(DEBUG, ETS_INTEROP_JS) << "Alloc shared ref: " << sharedRef;
274     return sharedRef;
275 }
276 
277 template <SharedReference::InitFn REF_INIT>
CreateRefCommon(InteropCtx * ctx,EtsObject * etsObject,napi_value jsObject,const PreInitJSObjectCallback & callback)278 SharedReference *SharedReferenceStorage::CreateRefCommon(InteropCtx *ctx, EtsObject *etsObject, napi_value jsObject,
279                                                          const PreInitJSObjectCallback &callback)
280 {
281     auto *coro = EtsCoroutine::GetCurrent();
282     [[maybe_unused]] EtsHandleScope hScope(coro);
283     EtsHandle<EtsObject> hobject(coro, etsObject);
284     TriggerXGCIfNeeded(ctx);
285     napi_ref jsRef;
286     // Create XRef before SharedReferenceStorage lock to avoid deadlock situation with JS mutator lock in napi calls
287     SharedReference **refRef = CreateXRef(ctx, jsObject, &jsRef, callback);
288     if (refRef == nullptr) {
289         return nullptr;
290     }
291     os::memory::WriteLockHolder lock(storageLock_);
292     auto *sharedRef = CreateReference<REF_INIT>(ctx, hobject, jsRef);
293     // Atomic with release order reason: XGC thread should see all writes (initialization of SharedReference) before
294     // check initialization status
295     AtomicStore(refRef, sharedRef, std::memory_order_release);
296     return sharedRef;
297 }
298 
CreateETSObjectRef(InteropCtx * ctx,EtsObject * etsObject,napi_value jsObject,const PreInitJSObjectCallback & callback)299 SharedReference *SharedReferenceStorage::CreateETSObjectRef(InteropCtx *ctx, EtsObject *etsObject, napi_value jsObject,
300                                                             const PreInitJSObjectCallback &callback)
301 {
302     return CreateRefCommon<&SharedReference::InitETSObject>(ctx, etsObject, jsObject, callback);
303 }
304 
CreateJSObjectRef(InteropCtx * ctx,EtsObject * etsObject,napi_value jsObject)305 SharedReference *SharedReferenceStorage::CreateJSObjectRef(InteropCtx *ctx, EtsObject *etsObject, napi_value jsObject)
306 {
307     auto *coro = EtsCoroutine::GetCurrent();
308     [[maybe_unused]] EtsHandleScope hScope(coro);
309     EtsHandle<EtsObject> hobject(coro, etsObject);
310     TriggerXGCIfNeeded(ctx);
311     napi_ref jsRef;
312 #if defined(PANDA_JS_ETS_HYBRID_MODE)
313     NAPI_CHECK_FATAL(napi_create_xref(ctx->GetJSEnv(), jsObject, 1, &jsRef));
314 #else
315     NAPI_CHECK_FATAL(napi_create_reference(ctx->GetJSEnv(), jsObject, 1, &jsRef));
316 #endif
317     os::memory::WriteLockHolder lock(storageLock_);
318     auto *sharedRef = CreateReference<&SharedReference::InitJSObject>(ctx, hobject, jsRef);
319     return sharedRef;
320 }
321 
CreateJSObjectRefwithWrap(InteropCtx * ctx,EtsObject * etsObject,napi_value jsObject)322 SharedReference *SharedReferenceStorage::CreateJSObjectRefwithWrap(InteropCtx *ctx, EtsObject *etsObject,
323                                                                    napi_value jsObject)
324 {
325     return CreateRefCommon<&SharedReference::InitJSObject>(ctx, etsObject, jsObject);
326 }
327 
CreateHybridObjectRef(InteropCtx * ctx,EtsObject * etsObject,napi_value jsObject)328 SharedReference *SharedReferenceStorage::CreateHybridObjectRef(InteropCtx *ctx, EtsObject *etsObject,
329                                                                napi_value jsObject)
330 {
331     return CreateRefCommon<&SharedReference::InitHybridObject>(ctx, etsObject, jsObject);
332 }
333 
RemoveReference(SharedReference * sharedRef)334 void SharedReferenceStorage::RemoveReference(SharedReference *sharedRef)
335 {
336     ASSERT(Size() > 0U);
337     LOG(DEBUG, ETS_INTEROP_JS) << "Remove shared ref: " << sharedRef;
338     FreeItem(sharedRef);
339     SharedReferenceSanity::Kill(sharedRef);
340 }
341 
DeleteJSRefAndRemoveReference(SharedReference * sharedRef)342 void SharedReferenceStorage::DeleteJSRefAndRemoveReference(SharedReference *sharedRef)
343 {
344     NAPI_CHECK_FATAL(napi_delete_reference(sharedRef->ctx_->GetJSEnv(), sharedRef->jsRef_));
345     RemoveReference(sharedRef);
346 }
347 
DeleteReferenceFromChain(EtsObject * etsObject,SharedReference * prevRef,SharedReference * removingRef,uint32_t nextIndex)348 void SharedReferenceStorage::DeleteReferenceFromChain(EtsObject *etsObject, SharedReference *prevRef,
349                                                       SharedReference *removingRef, uint32_t nextIndex)
350 {
351     DeleteJSRefAndRemoveReference(removingRef);
352     // The current reference is head of chain
353     if (prevRef == nullptr) {
354         if (nextIndex == 0U) {
355             // Chain contains only one reference, so no more references for this EtsObject
356             etsObject->DropInteropIndex();
357         } else {
358             // Update interop index to actual head
359             etsObject->SetInteropIndex(nextIndex);
360         }
361     } else {
362         prevRef->flags_.SetNextIndex(nextIndex);
363     }
364 }
365 
DeleteReference(SharedReference * sharedRef,const std::function<bool (const SharedReference * sharedRef)> & deletePredicate)366 bool SharedReferenceStorage::DeleteReference(
367     SharedReference *sharedRef, const std::function<bool(const SharedReference *sharedRef)> &deletePredicate)
368 {
369     ASSERT(sharedRef != nullptr);
370     ASSERT(!sharedRef->IsEmpty());
371     auto *etsObject = sharedRef->GetEtsObject();
372     // EtsObject contains interop index for the first reference in chain
373     uint32_t nextIndex = etsObject->GetInteropIndex();
374     ASSERT(nextIndex != 0U);
375     SharedReference *prevRef = nullptr;
376     do {
377         auto *currentRef = GetItemByIndex(nextIndex);
378         nextIndex = currentRef->flags_.GetNextIndex();
379         if (deletePredicate(currentRef)) {
380             DeleteReferenceFromChain(etsObject, prevRef, currentRef, nextIndex);
381             return true;
382         }
383         prevRef = currentRef;
384     } while (nextIndex != 0U);
385     return false;
386 }
387 
DeleteAllReferencesWithCtx(const InteropCtx * ctx)388 void SharedReferenceStorage::DeleteAllReferencesWithCtx(const InteropCtx *ctx)
389 {
390     os::memory::WriteLockHolder lock(storageLock_);
391     const std::function<bool(const SharedReference *)> contextPredicate = [ctx](const SharedReference *ref) {
392         return ctx == ref->ctx_;
393     };
394     const size_t capacity = Capacity();
395     for (size_t i = 1U; i < capacity; ++i) {
396         SharedReference *ref = GetItemByIndex(i);
397         if (ref->IsEmpty()) {
398             continue;
399         }
400         if (contextPredicate(ref)) {
401             [[maybe_unused]] bool isDeleted = DeleteReference(ref, contextPredicate);
402             ASSERT(isDeleted == true);
403         }
404     }
405 }
406 
DeleteUnmarkedReferences(SharedReference * sharedRef)407 void SharedReferenceStorage::DeleteUnmarkedReferences(SharedReference *sharedRef)
408 {
409     ASSERT(sharedRef != nullptr);
410     ASSERT(!sharedRef->IsEmpty());
411     EtsObject *etsObject = sharedRef->GetEtsObject();
412     // Get head of refs chain
413     auto currentIndex = etsObject->GetInteropIndex();
414     SharedReference *currentRef = GetItemByIndex(currentIndex);
415     auto nextIndex = currentRef->flags_.GetNextIndex();
416     // if nextIndex is 0, then refs chain contains only 1 element, so need to also drop interop index from EtsObject
417     if (LIKELY(nextIndex == 0U)) {
418         ASSERT(sharedRef == currentRef);
419         DeleteJSRefAndRemoveReference(currentRef);
420         etsObject->DropInteropIndex();
421         return;
422     }
423     SharedReference *headRef = currentRef;
424     // -- Remove all unmarked refs except head: START --
425     SharedReference *prevRef = currentRef;
426     currentRef = GetItemByIndex(nextIndex);
427     nextIndex = currentRef->flags_.GetNextIndex();
428     while (nextIndex != 0U) {
429         if (!currentRef->IsMarked()) {
430             DeleteJSRefAndRemoveReference(currentRef);
431             prevRef->flags_.SetNextIndex(nextIndex);
432         } else {
433             prevRef = currentRef;
434         }
435         currentRef = GetItemByIndex(nextIndex);
436         nextIndex = currentRef->flags_.GetNextIndex();
437     }
438     if (!currentRef->IsMarked()) {
439         DeleteJSRefAndRemoveReference(currentRef);
440         prevRef->flags_.SetNextIndex(0U);
441     }
442     // -- Remove all unmarked refs except head: FINISH --
443     // Check mark state for headRef, we need to exchange or drop interop index in the EtsObject header
444     if (!headRef->IsMarked()) {
445         nextIndex = headRef->flags_.GetNextIndex();
446         DeleteJSRefAndRemoveReference(headRef);
447         if (nextIndex == 0U) {
448             // Head reference is alone reference in the chain, so need to drop interop index from EtsObject header
449             etsObject->DropInteropIndex();
450             return;
451         }
452         // All unmarked references were removed from the chain, so reference after head should marked
453         ASSERT(GetItemByIndex(nextIndex)->IsMarked());
454         // Update interop index to actual head
455         etsObject->SetInteropIndex(nextIndex);
456     }
457 }
458 
NotifyXGCStarted()459 void SharedReferenceStorage::NotifyXGCStarted()
460 {
461     os::memory::WriteLockHolder lock(storageLock_);
462     isXGCinProgress_ = true;
463 }
464 
NotifyXGCFinished()465 void SharedReferenceStorage::NotifyXGCFinished()
466 {
467     os::memory::WriteLockHolder lock(storageLock_);
468     isXGCinProgress_ = false;
469 }
470 
VisitRoots(const GCRootVisitor & visitor)471 void SharedReferenceStorage::VisitRoots(const GCRootVisitor &visitor)
472 {
473     // No need lock, because we visit roots on pause and we wait XGC ConcurrentSweep for local GCs
474     size_t capacity = Capacity();
475     for (size_t i = 1U; i < capacity; ++i) {
476         SharedReference *ref = GetItemByIndex(i);
477         if (!ref->IsEmpty() && ref->flags_.GetNextIndex() == 0U) {
478             visitor(mem::GCRoot {mem::RootType::ROOT_VM, ref->GetEtsObject()->GetCoreType()});
479         }
480     }
481 }
482 
UpdateRefs(const GCRootUpdater & gcRootUpdater)483 void SharedReferenceStorage::UpdateRefs(const GCRootUpdater &gcRootUpdater)
484 {
485     // No need lock, because we visit roots on pause and we wait XGC ConcurrentSweep for local GCs
486     size_t capacity = Capacity();
487     for (size_t i = 1U; i < capacity; ++i) {
488         SharedReference *ref = GetItemByIndex(i);
489         if (ref->IsEmpty()) {
490             continue;
491         }
492         ObjectHeader *obj = ref->GetEtsObject()->GetCoreType();
493         if (gcRootUpdater(&obj)) {
494             ref->SetETSObject(EtsObject::FromCoreType(obj));
495         }
496     }
497 }
498 
SweepUnmarkedRefs()499 void SharedReferenceStorage::SweepUnmarkedRefs()
500 {
501     os::memory::WriteLockHolder lock(storageLock_);
502     ASSERT_PRINT(refsAllocatedDuringXGC_.empty(),
503                  "All references allocted during XGC should be proceesed on ReMark phase, unprocessed refs: "
504                      << refsAllocatedDuringXGC_.size());
505     size_t capacity = Capacity();
506     for (size_t i = 1U; i < capacity; ++i) {
507         SharedReference *ref = GetItemByIndex(i);
508         if (ref->IsEmpty()) {
509             continue;
510         }
511         // If the reference is unmarked, then we immediately remove all unmarked references from related chain
512         if (!ref->IsMarked()) {
513             DeleteUnmarkedReferences(ref);
514         }
515     }
516 }
517 
UnmarkAll()518 void SharedReferenceStorage::UnmarkAll()
519 {
520     os::memory::WriteLockHolder lock(storageLock_);
521     size_t capacity = Capacity();
522     for (size_t i = 1U; i < capacity; ++i) {
523         auto *ref = GetItemByIndex(i);
524         if (!ref->IsEmpty()) {
525             ref->Unmark();
526         }
527     }
528 }
529 
CheckAlive(void * data)530 bool SharedReferenceStorage::CheckAlive(void *data)
531 {
532     auto *sharedRef = reinterpret_cast<SharedReference *>(data);
533     os::memory::ReadLockHolder lock(storageLock_);
534     return IsValidItem(sharedRef) && SharedReferenceSanity::CheckAlive(sharedRef);
535 }
536 
537 }  // namespace ark::ets::interop::js::ets_proxy
538