暫無描述
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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