BoehmGC.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. #include "il2cpp-config.h"
  2. #if IL2CPP_GC_BOEHM
  3. #include <stdint.h>
  4. #include "gc_wrapper.h"
  5. #include "GarbageCollector.h"
  6. #include "WriteBarrier.h"
  7. #include "WriteBarrierValidation.h"
  8. #include "os/Mutex.h"
  9. #include "vm/Array.h"
  10. #include "vm/Domain.h"
  11. #include "vm/Profiler.h"
  12. #include "utils/Il2CppHashMap.h"
  13. #include "utils/HashUtils.h"
  14. #include "il2cpp-object-internals.h"
  15. #include "Baselib.h"
  16. #include "Cpp/ReentrantLock.h"
  17. static bool s_GCInitialized = false;
  18. #if IL2CPP_ENABLE_DEFERRED_GC
  19. static bool s_PendingGC = false;
  20. #endif
  21. static void on_gc_event(GC_EventType eventType);
  22. #if IL2CPP_ENABLE_PROFILER
  23. using il2cpp::vm::Profiler;
  24. static void on_heap_resize(GC_word newSize);
  25. #endif
  26. #if !RUNTIME_TINY
  27. static GC_push_other_roots_proc default_push_other_roots;
  28. typedef Il2CppHashMap<char*, char*, il2cpp::utils::PassThroughHash<char*> > RootMap;
  29. static RootMap s_Roots;
  30. typedef Il2CppHashMap<void*, il2cpp::gc::GarbageCollector::GetDynamicRootDataProc, il2cpp::utils::PassThroughHash<void*> > DynamicRootMap;
  31. static DynamicRootMap s_DynamicRoots;
  32. static void push_other_roots(void);
  33. typedef struct ephemeron_node ephemeron_node;
  34. static ephemeron_node* ephemeron_list;
  35. static void
  36. clear_ephemerons(void);
  37. #if HYBRIDCLR_UNITY_VERSION >= 20210320
  38. static GC_ms_entry*
  39. push_ephemerons(GC_ms_entry* mark_stack_ptr, GC_ms_entry* mark_stack_limit);
  40. #else
  41. static void
  42. push_ephemerons(void);
  43. #endif
  44. #if !IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION
  45. #define ELEMENT_CHUNK_SIZE 256
  46. #define VECTOR_PROC_INDEX 6
  47. #define BYTES_PER_WORD (sizeof(GC_word))
  48. #include <gc_vector.h>
  49. GC_ms_entry* GC_gcj_vector_proc(GC_word* addr, GC_ms_entry* mark_stack_ptr,
  50. GC_ms_entry* mark_stack_limit, GC_word env)
  51. {
  52. Il2CppArraySize* a = NULL;
  53. if (env)
  54. {
  55. IL2CPP_ASSERT(env == 1);
  56. a = (Il2CppArraySize*)GC_base(addr);
  57. }
  58. else
  59. {
  60. IL2CPP_ASSERT(addr == GC_base(addr));
  61. a = (Il2CppArraySize*)addr;
  62. }
  63. if (!a->max_length)
  64. return mark_stack_ptr;
  65. il2cpp_array_size_t length = a->max_length;
  66. Il2CppClass* array_type = a->vtable->klass;
  67. Il2CppClass* element_type = array_type->element_class;
  68. GC_descr element_desc = (GC_descr)element_type->gc_desc;
  69. IL2CPP_ASSERT((element_desc & GC_DS_TAGS) == GC_DS_BITMAP);
  70. IL2CPP_ASSERT(element_type->byval_arg.valuetype);
  71. int words_per_element = array_type->element_size / BYTES_PER_WORD;
  72. GC_word* actual_start = (GC_word*)a->vector;
  73. /* start at first element or resume from last iteration */
  74. GC_word* start = env ? addr : actual_start;
  75. /* end at last element or max chunk size */
  76. GC_word* actual_end = actual_start + length * words_per_element;
  77. return GC_gcj_vector_mark_proc(mark_stack_ptr, mark_stack_limit, element_desc, start, actual_end, words_per_element);
  78. }
  79. #endif // !IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION
  80. #endif // !RUNTIME_TINY
  81. void
  82. il2cpp::gc::GarbageCollector::Initialize()
  83. {
  84. if (s_GCInitialized)
  85. return;
  86. #if IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION
  87. il2cpp::gc::WriteBarrierValidation::Setup();
  88. #endif
  89. // This tells the GC that we are not scanning dynamic library data segments and that
  90. // the GC tracked data structures need ot be manually pushed and marked.
  91. // Call this before GC_INIT since the initialization logic uses this value.
  92. GC_set_no_dls(1);
  93. #if !IL2CPP_DEVELOPMENT
  94. // Turn off GC logging and warnings for non-development builds
  95. GC_set_warn_proc(GC_ignore_warn_proc);
  96. #endif
  97. #if IL2CPP_ENABLE_WRITE_BARRIERS
  98. GC_enable_incremental();
  99. #if IL2CPP_INCREMENTAL_TIME_SLICE
  100. GC_set_time_limit(IL2CPP_INCREMENTAL_TIME_SLICE);
  101. #endif
  102. #endif
  103. #if !RUNTIME_TINY
  104. default_push_other_roots = GC_get_push_other_roots();
  105. GC_set_push_other_roots(push_other_roots);
  106. GC_set_mark_stack_empty(push_ephemerons);
  107. #endif // !RUNTIME_TINY
  108. GC_set_on_collection_event(&on_gc_event);
  109. #if IL2CPP_ENABLE_PROFILER
  110. GC_set_on_heap_resize(&on_heap_resize);
  111. #endif
  112. GC_INIT();
  113. #if defined(GC_THREADS)
  114. GC_set_finalize_on_demand(1);
  115. #if !RUNTIME_TINY
  116. GC_set_finalizer_notifier(&il2cpp::gc::GarbageCollector::NotifyFinalizers);
  117. #endif
  118. // We need to call this if we want to manually register threads, i.e. GC_register_my_thread
  119. #if !IL2CPP_TARGET_JAVASCRIPT
  120. GC_allow_register_threads();
  121. #endif
  122. #endif
  123. #ifdef GC_GCJ_SUPPORT
  124. GC_init_gcj_malloc(0, NULL);
  125. #endif
  126. #if !RUNTIME_TINY && !IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION
  127. GC_init_gcj_vector(VECTOR_PROC_INDEX, (void*)GC_gcj_vector_proc);
  128. #endif
  129. s_GCInitialized = true;
  130. }
  131. void il2cpp::gc::GarbageCollector::UninitializeGC()
  132. {
  133. #if IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION
  134. il2cpp::gc::WriteBarrierValidation::Run();
  135. #endif
  136. GC_deinit();
  137. #if IL2CPP_ENABLE_RELOAD
  138. s_GCInitialized = false;
  139. default_push_other_roots = NULL;
  140. s_Roots.clear();
  141. #endif
  142. }
  143. int32_t
  144. il2cpp::gc::GarbageCollector::GetCollectionCount(int32_t generation)
  145. {
  146. return (int32_t)GC_get_gc_no();
  147. }
  148. int32_t
  149. il2cpp::gc::GarbageCollector::GetMaxGeneration()
  150. {
  151. return 0;
  152. }
  153. void
  154. il2cpp::gc::GarbageCollector::Collect(int maxGeneration)
  155. {
  156. #if IL2CPP_ENABLE_DEFERRED_GC
  157. if (GC_is_disabled())
  158. s_PendingGC = true;
  159. #endif
  160. GC_gcollect();
  161. }
  162. int32_t
  163. il2cpp::gc::GarbageCollector::CollectALittle()
  164. {
  165. #if IL2CPP_ENABLE_DEFERRED_GC
  166. if (s_PendingGC)
  167. {
  168. s_PendingGC = false;
  169. GC_gcollect();
  170. return 0; // no more work to do
  171. }
  172. else
  173. {
  174. return GC_collect_a_little();
  175. }
  176. #else
  177. return GC_collect_a_little();
  178. #endif
  179. }
  180. void
  181. il2cpp::gc::GarbageCollector::StartIncrementalCollection()
  182. {
  183. GC_start_incremental_collection();
  184. }
  185. #if IL2CPP_ENABLE_WRITE_BARRIERS
  186. void
  187. il2cpp::gc::GarbageCollector::SetWriteBarrier(void **ptr)
  188. {
  189. GC_END_STUBBORN_CHANGE(ptr);
  190. }
  191. #endif
  192. int64_t
  193. il2cpp::gc::GarbageCollector::GetUsedHeapSize(void)
  194. {
  195. return GC_get_heap_size() - GC_get_free_bytes();
  196. }
  197. int64_t
  198. il2cpp::gc::GarbageCollector::GetAllocatedHeapSize(void)
  199. {
  200. return GC_get_heap_size();
  201. }
  202. void
  203. il2cpp::gc::GarbageCollector::Disable()
  204. {
  205. GC_disable();
  206. }
  207. void
  208. il2cpp::gc::GarbageCollector::Enable()
  209. {
  210. GC_enable();
  211. }
  212. bool
  213. il2cpp::gc::GarbageCollector::IsDisabled()
  214. {
  215. return GC_is_disabled();
  216. }
  217. static baselib::ReentrantLock s_GCSetModeLock;
  218. void
  219. il2cpp::gc::GarbageCollector::SetMode(Il2CppGCMode mode)
  220. {
  221. os::FastAutoLock lock(&s_GCSetModeLock);
  222. switch (mode)
  223. {
  224. case IL2CPP_GC_MODE_ENABLED:
  225. if (GC_is_disabled())
  226. GC_enable();
  227. GC_set_disable_automatic_collection(false);
  228. break;
  229. case IL2CPP_GC_MODE_DISABLED:
  230. if (!GC_is_disabled())
  231. GC_disable();
  232. break;
  233. case IL2CPP_GC_MODE_MANUAL:
  234. if (GC_is_disabled())
  235. GC_enable();
  236. GC_set_disable_automatic_collection(true);
  237. break;
  238. }
  239. }
  240. void
  241. il2cpp::gc::GarbageCollector::RegisterThread()
  242. {
  243. #if defined(GC_THREADS) && !IL2CPP_TARGET_JAVASCRIPT
  244. struct GC_stack_base sb;
  245. int res;
  246. res = GC_get_stack_base(&sb);
  247. if (res != GC_SUCCESS)
  248. {
  249. /* Can't determine the register stack bounds */
  250. IL2CPP_ASSERT(false && "GC_get_stack_base () failed, aborting.");
  251. /* Abort we can't scan the stack, so we can't use the GC */
  252. abort();
  253. }
  254. res = GC_register_my_thread(&sb);
  255. if ((res != GC_SUCCESS) && (res != GC_DUPLICATE))
  256. {
  257. IL2CPP_ASSERT(false && "GC_register_my_thread () failed.");
  258. /* Abort we can't use the GC on this thread, so we can't run managed code */
  259. abort();
  260. }
  261. #endif
  262. }
  263. bool
  264. il2cpp::gc::GarbageCollector::UnregisterThread()
  265. {
  266. #if defined(GC_THREADS) && !IL2CPP_TARGET_JAVASCRIPT
  267. int res;
  268. res = GC_unregister_my_thread();
  269. if (res != GC_SUCCESS)
  270. IL2CPP_ASSERT(false && "GC_unregister_my_thread () failed.");
  271. return res == GC_SUCCESS;
  272. #else
  273. return true;
  274. #endif
  275. }
  276. il2cpp::gc::GarbageCollector::FinalizerCallback il2cpp::gc::GarbageCollector::RegisterFinalizerWithCallback(Il2CppObject* obj, FinalizerCallback callback)
  277. {
  278. FinalizerCallback oldCallback;
  279. void* oldData;
  280. GC_REGISTER_FINALIZER_NO_ORDER((char*)obj, callback, NULL, &oldCallback, &oldData);
  281. IL2CPP_ASSERT(oldData == NULL);
  282. return oldCallback;
  283. }
  284. void
  285. il2cpp::gc::GarbageCollector::AddWeakLink(void **link_addr, Il2CppObject *obj, bool track)
  286. {
  287. /* libgc requires that we use HIDE_POINTER... */
  288. *link_addr = (void*)GC_HIDE_POINTER(obj);
  289. // need this since our strings are not real objects
  290. if (GC_is_heap_ptr(obj))
  291. GC_GENERAL_REGISTER_DISAPPEARING_LINK(link_addr, obj);
  292. }
  293. void
  294. il2cpp::gc::GarbageCollector::RemoveWeakLink(void **link_addr)
  295. {
  296. Il2CppObject* obj = GarbageCollector::GetWeakLink(link_addr);
  297. if (GC_is_heap_ptr(obj))
  298. GC_unregister_disappearing_link(link_addr);
  299. *link_addr = NULL;
  300. }
  301. static void*
  302. RevealLink(void* link_addr)
  303. {
  304. void **link_a = (void**)link_addr;
  305. return GC_REVEAL_POINTER(*link_a);
  306. }
  307. Il2CppObject*
  308. il2cpp::gc::GarbageCollector::GetWeakLink(void **link_addr)
  309. {
  310. Il2CppObject *obj = (Il2CppObject*)GC_call_with_alloc_lock(RevealLink, link_addr);
  311. if (obj == (Il2CppObject*)-1)
  312. return NULL;
  313. return obj;
  314. }
  315. void*
  316. il2cpp::gc::GarbageCollector::MakeDescriptorForObject(size_t *bitmap, int numbits)
  317. {
  318. #ifdef GC_GCJ_SUPPORT
  319. /* It seems there are issues when the bitmap doesn't fit: play it safe */
  320. if (numbits >= 30)
  321. return GC_NO_DESCRIPTOR;
  322. else
  323. {
  324. GC_descr desc = GC_make_descriptor((GC_bitmap)bitmap, numbits);
  325. // we should always have a GC_DS_BITMAP descriptor, as we:
  326. // 1) Always want a precise marker.
  327. // 2) Can never be GC_DS_LENGTH since we always have an object header
  328. // at the beginning of the allocation.
  329. IL2CPP_ASSERT((desc & GC_DS_TAGS) == GC_DS_BITMAP || (desc & GC_DS_TAGS) == (GC_descr)GC_NO_DESCRIPTOR);
  330. return (void*)desc;
  331. }
  332. #else
  333. return 0;
  334. #endif
  335. }
  336. void* il2cpp::gc::GarbageCollector::MakeDescriptorForString()
  337. {
  338. return GC_NO_DESCRIPTOR;
  339. }
  340. void* il2cpp::gc::GarbageCollector::MakeDescriptorForArray()
  341. {
  342. return GC_NO_DESCRIPTOR;
  343. }
  344. void il2cpp::gc::GarbageCollector::StopWorld()
  345. {
  346. GC_stop_world_external();
  347. }
  348. void il2cpp::gc::GarbageCollector::StartWorld()
  349. {
  350. GC_start_world_external();
  351. }
  352. #if RUNTIME_TINY
  353. void*
  354. il2cpp::gc::GarbageCollector::Allocate(size_t size)
  355. {
  356. return GC_MALLOC(size);
  357. }
  358. void*
  359. il2cpp::gc::GarbageCollector::AllocateObject(size_t size, void* type)
  360. {
  361. #if IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION
  362. return GC_gcj_malloc(size, type);
  363. #else
  364. return GC_MALLOC(size);
  365. #endif
  366. }
  367. #endif
  368. void*
  369. il2cpp::gc::GarbageCollector::AllocateFixed(size_t size, void *descr)
  370. {
  371. // Note that we changed the implementation from mono.
  372. // In our case, we expect that
  373. // a) This memory will never be moved
  374. // b) This memory will be scanned for references
  375. // c) This memory will remain 'alive' until explicitly freed
  376. // GC_MALLOC_UNCOLLECTABLE fulfills all these requirements
  377. // It does not accept a descriptor, but there was only one
  378. // or two places in mono that pass a descriptor to this routine
  379. // and we can or will support those use cases in a different manner.
  380. IL2CPP_ASSERT(!descr);
  381. return GC_MALLOC_UNCOLLECTABLE(size);
  382. }
  383. void
  384. il2cpp::gc::GarbageCollector::FreeFixed(void* addr)
  385. {
  386. GC_FREE(addr);
  387. }
  388. #if !RUNTIME_TINY
  389. int32_t
  390. il2cpp::gc::GarbageCollector::InvokeFinalizers()
  391. {
  392. #if IL2CPP_TINY
  393. return 0; // The Tiny profile does not have finalizers
  394. #else
  395. return (int32_t)GC_invoke_finalizers();
  396. #endif
  397. }
  398. bool
  399. il2cpp::gc::GarbageCollector::HasPendingFinalizers()
  400. {
  401. return GC_should_invoke_finalizers() != 0;
  402. }
  403. #endif
  404. int64_t
  405. il2cpp::gc::GarbageCollector::GetMaxTimeSliceNs()
  406. {
  407. return GC_get_time_limit_ns();
  408. }
  409. void
  410. il2cpp::gc::GarbageCollector::SetMaxTimeSliceNs(int64_t maxTimeSlice)
  411. {
  412. GC_set_time_limit_ns(maxTimeSlice);
  413. }
  414. bool
  415. il2cpp::gc::GarbageCollector::IsIncremental()
  416. {
  417. return GC_is_incremental_mode();
  418. }
  419. void on_gc_event(GC_EventType eventType)
  420. {
  421. #if !RUNTIME_TINY
  422. if (eventType == GC_EVENT_RECLAIM_START)
  423. {
  424. clear_ephemerons();
  425. }
  426. #endif
  427. #if IL2CPP_ENABLE_PROFILER
  428. Profiler::GCEvent((Il2CppGCEvent)eventType);
  429. #endif
  430. }
  431. #if IL2CPP_ENABLE_PROFILER
  432. void on_heap_resize(GC_word newSize)
  433. {
  434. Profiler::GCHeapResize((int64_t)newSize);
  435. }
  436. #endif // IL2CPP_ENABLE_PROFILER
  437. void il2cpp::gc::GarbageCollector::ForEachHeapSection(void* user_data, HeapSectionCallback callback)
  438. {
  439. GC_foreach_heap_section(user_data, callback);
  440. }
  441. size_t il2cpp::gc::GarbageCollector::GetSectionCount()
  442. {
  443. return GC_get_heap_section_count();
  444. }
  445. void* il2cpp::gc::GarbageCollector::CallWithAllocLockHeld(GCCallWithAllocLockCallback callback, void* user_data)
  446. {
  447. return GC_call_with_alloc_lock(callback, user_data);
  448. }
  449. typedef struct
  450. {
  451. char *start;
  452. char *end;
  453. } RootData;
  454. #if !RUNTIME_TINY
  455. static void*
  456. register_root(void* arg)
  457. {
  458. RootData* root_data = (RootData*)arg;
  459. s_Roots.insert(std::make_pair(root_data->start, root_data->end));
  460. return NULL;
  461. }
  462. void il2cpp::gc::GarbageCollector::RegisterRoot(char *start, size_t size)
  463. {
  464. RootData root_data;
  465. root_data.start = start;
  466. /* Boehm root processing requires one byte past end of region to be scanned */
  467. root_data.end = start + size + 1;
  468. CallWithAllocLockHeld(register_root, &root_data);
  469. }
  470. static void*
  471. deregister_root(void* arg)
  472. {
  473. s_Roots.erase((char*)arg);
  474. return NULL;
  475. }
  476. void il2cpp::gc::GarbageCollector::UnregisterRoot(char* start)
  477. {
  478. GC_call_with_alloc_lock(deregister_root, start);
  479. }
  480. struct DynamicRootData
  481. {
  482. void* root;
  483. il2cpp::gc::GarbageCollector::GetDynamicRootDataProc getRootDataFunc;
  484. };
  485. static void* register_dynamic_root(void* arg)
  486. {
  487. DynamicRootData* rootData = (DynamicRootData*)arg;
  488. IL2CPP_ASSERT(s_DynamicRoots.find(rootData->root) == s_DynamicRoots.end());
  489. s_DynamicRoots.add(rootData->root, rootData->getRootDataFunc);
  490. return NULL;
  491. }
  492. static void* deregister_dynamic_root(void* arg)
  493. {
  494. IL2CPP_ASSERT(s_DynamicRoots.find(arg) != s_DynamicRoots.end());
  495. s_DynamicRoots.erase(arg);
  496. return NULL;
  497. }
  498. void il2cpp::gc::GarbageCollector::RegisterDynamicRoot(void* root, GetDynamicRootDataProc getRootDataFunc)
  499. {
  500. DynamicRootData rootData = {root, getRootDataFunc};
  501. GC_call_with_alloc_lock(register_dynamic_root, &rootData);
  502. }
  503. void il2cpp::gc::GarbageCollector::UnregisterDynamicRoot(void* root)
  504. {
  505. GC_call_with_alloc_lock(deregister_dynamic_root, root);
  506. }
  507. static void
  508. push_other_roots(void)
  509. {
  510. for (RootMap::iterator iter = s_Roots.begin(); iter != s_Roots.end(); ++iter)
  511. GC_push_all(iter->first, iter->second);
  512. for (auto dynamicRootEntry : s_DynamicRoots)
  513. {
  514. std::pair<char*, size_t> dynamicRootData = dynamicRootEntry.second(dynamicRootEntry.first);
  515. if (dynamicRootData.first)
  516. {
  517. GC_push_all(dynamicRootData.first, dynamicRootData.first + dynamicRootData.second);
  518. }
  519. }
  520. GC_push_all(&ephemeron_list, &ephemeron_list + 1);
  521. if (default_push_other_roots)
  522. default_push_other_roots();
  523. }
  524. struct ephemeron_node
  525. {
  526. ephemeron_node* next;
  527. void* ephemeron_array_weak_link;
  528. };
  529. static void*
  530. ephemeron_array_add(void* arg)
  531. {
  532. ephemeron_node* item = (ephemeron_node*)arg;
  533. ephemeron_node* current = ephemeron_list;
  534. il2cpp::gc::WriteBarrier::GenericStore(&item->next, current);
  535. ephemeron_list = item;
  536. return NULL;
  537. }
  538. struct Ephemeron
  539. {
  540. Il2CppObject* key;
  541. Il2CppObject* value;
  542. };
  543. static void
  544. clear_ephemerons(void)
  545. {
  546. ephemeron_node* prev_node = NULL;
  547. ephemeron_node* current_node = NULL;
  548. /* iterate all registered Ephemeron[] */
  549. for (current_node = ephemeron_list; current_node; current_node = current_node->next)
  550. {
  551. Ephemeron* current_ephemeron, * array_end;
  552. Il2CppObject* tombstone = NULL;
  553. /* reveal weak link value*/
  554. Il2CppArray* array = (Il2CppArray*)GC_REVEAL_POINTER(current_node->ephemeron_array_weak_link);
  555. /* remove unmarked (non-reachable) arrays from the list */
  556. if (!GC_is_marked(array))
  557. {
  558. if (prev_node == NULL)
  559. il2cpp::gc::WriteBarrier::GenericStore(&ephemeron_list, current_node->next);
  560. else
  561. il2cpp::gc::WriteBarrier::GenericStore(&prev_node->next, current_node->next);
  562. continue;
  563. }
  564. prev_node = current_node;
  565. current_ephemeron = il2cpp_array_addr(array, Ephemeron, 0);
  566. array_end = current_ephemeron + array->max_length;
  567. tombstone = il2cpp::vm::Domain::GetCurrent()->ephemeron_tombstone;
  568. for (; current_ephemeron < array_end; ++current_ephemeron)
  569. {
  570. /* skip a null or tombstone (empty) key */
  571. if (!current_ephemeron->key || current_ephemeron->key == tombstone)
  572. continue;
  573. /* If the key is not marked, then set it to the tombstone and the value to NULL. */
  574. if (!GC_is_marked(current_ephemeron->key))
  575. {
  576. il2cpp::gc::WriteBarrier::GenericStore(&current_ephemeron->key, tombstone);
  577. current_ephemeron->value = NULL;
  578. }
  579. }
  580. }
  581. }
  582. #if HYBRIDCLR_UNITY_VERSION >= 20210320
  583. static GC_ms_entry*
  584. push_ephemerons(GC_ms_entry* mark_stack_ptr, GC_ms_entry* mark_stack_limit)
  585. #else
  586. static void
  587. push_ephemerons(void)
  588. #endif
  589. {
  590. ephemeron_node* prev_node = NULL;
  591. ephemeron_node* current_node = NULL;
  592. /* iterate all registered Ephemeron[] */
  593. for (current_node = ephemeron_list; current_node; current_node = current_node->next)
  594. {
  595. Ephemeron* current_ephemeron, * array_end;
  596. Il2CppObject* tombstone = NULL;
  597. /* reveal weak link value*/
  598. Il2CppArray* array = (Il2CppArray*)GC_REVEAL_POINTER(current_node->ephemeron_array_weak_link);
  599. /* unreferenced array */
  600. if (!GC_is_marked(array))
  601. {
  602. continue;
  603. }
  604. prev_node = current_node;
  605. current_ephemeron = il2cpp_array_addr(array, Ephemeron, 0);
  606. array_end = current_ephemeron + array->max_length;
  607. tombstone = il2cpp::vm::Domain::GetCurrent()->ephemeron_tombstone;
  608. for (; current_ephemeron < array_end; ++current_ephemeron)
  609. {
  610. /* skip a null or tombstone (empty) key */
  611. if (!current_ephemeron->key || current_ephemeron->key == tombstone)
  612. continue;
  613. /* If the key is not marked, then don't mark value. */
  614. if (!GC_is_marked(current_ephemeron->key))
  615. continue;
  616. #if HYBRIDCLR_UNITY_VERSION >= 20210320
  617. if (current_ephemeron->value)
  618. {
  619. mark_stack_ptr = GC_mark_and_push((void*)current_ephemeron->value, mark_stack_ptr, mark_stack_limit, (void**)&current_ephemeron->value);
  620. }
  621. #else
  622. if (current_ephemeron->value && !GC_is_marked(current_ephemeron->value))
  623. {
  624. /* the key is marked, so mark the value if needed */
  625. GC_push_all(&current_ephemeron->value, &current_ephemeron->value + 1);
  626. }
  627. #endif
  628. }
  629. }
  630. #if HYBRIDCLR_UNITY_VERSION >= 20210320
  631. return mark_stack_ptr;
  632. #endif
  633. }
  634. bool il2cpp::gc::GarbageCollector::EphemeronArrayAdd(Il2CppObject* obj)
  635. {
  636. ephemeron_node* item = (ephemeron_node*)GC_MALLOC(sizeof(ephemeron_node));
  637. memset(item, 0, sizeof(ephemeron_node));
  638. AddWeakLink(&item->ephemeron_array_weak_link, obj, false);
  639. GC_call_with_alloc_lock(ephemeron_array_add, item);
  640. return true;
  641. }
  642. #endif // !RUNTIME_TINY
  643. #endif