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