BoehmGC.cpp 19 KB

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