123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766 |
- #include "il2cpp-config.h"
- #if IL2CPP_GC_BOEHM
- #include <stdint.h>
- #include "gc_wrapper.h"
- #include "GarbageCollector.h"
- #include "WriteBarrier.h"
- #include "WriteBarrierValidation.h"
- #include "os/Mutex.h"
- #include "vm/Array.h"
- #include "vm/Domain.h"
- #include "vm/Profiler.h"
- #include "utils/Il2CppHashMap.h"
- #include "utils/HashUtils.h"
- #include "il2cpp-object-internals.h"
- #include "Baselib.h"
- #include "Cpp/ReentrantLock.h"
- static bool s_GCInitialized = false;
- #if IL2CPP_ENABLE_DEFERRED_GC
- static bool s_PendingGC = false;
- #endif
- static void on_gc_event(GC_EventType eventType);
- #if IL2CPP_ENABLE_PROFILER
- using il2cpp::vm::Profiler;
- static void on_heap_resize(GC_word newSize);
- #endif
- #if !RUNTIME_TINY
- static GC_push_other_roots_proc default_push_other_roots;
- typedef Il2CppHashMap<char*, char*, il2cpp::utils::PassThroughHash<char*> > RootMap;
- static RootMap s_Roots;
- typedef Il2CppHashMap<void*, il2cpp::gc::GarbageCollector::GetDynamicRootDataProc, il2cpp::utils::PassThroughHash<void*> > DynamicRootMap;
- static DynamicRootMap s_DynamicRoots;
- static void push_other_roots(void);
- typedef struct ephemeron_node ephemeron_node;
- static ephemeron_node* ephemeron_list;
- static void
- clear_ephemerons(void);
- #if HYBRIDCLR_UNITY_VERSION >= 20210320
- static GC_ms_entry*
- push_ephemerons(GC_ms_entry* mark_stack_ptr, GC_ms_entry* mark_stack_limit);
- #else
- static void
- push_ephemerons(void);
- #endif
- #if !IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION
- #define ELEMENT_CHUNK_SIZE 256
- #define VECTOR_PROC_INDEX 6
- #define BYTES_PER_WORD (sizeof(GC_word))
- #include <gc_vector.h>
- GC_ms_entry* GC_gcj_vector_proc(GC_word* addr, GC_ms_entry* mark_stack_ptr,
- GC_ms_entry* mark_stack_limit, GC_word env)
- {
- Il2CppArraySize* a = NULL;
- if (env)
- {
- IL2CPP_ASSERT(env == 1);
- a = (Il2CppArraySize*)GC_base(addr);
- }
- else
- {
- IL2CPP_ASSERT(addr == GC_base(addr));
- a = (Il2CppArraySize*)addr;
- }
- if (!a->max_length)
- return mark_stack_ptr;
- il2cpp_array_size_t length = a->max_length;
- Il2CppClass* array_type = a->vtable->klass;
- Il2CppClass* element_type = array_type->element_class;
- GC_descr element_desc = (GC_descr)element_type->gc_desc;
- IL2CPP_ASSERT((element_desc & GC_DS_TAGS) == GC_DS_BITMAP);
- IL2CPP_ASSERT(element_type->byval_arg.valuetype);
- int words_per_element = array_type->element_size / BYTES_PER_WORD;
- GC_word* actual_start = (GC_word*)a->vector;
- /* start at first element or resume from last iteration */
- GC_word* start = env ? addr : actual_start;
- /* end at last element or max chunk size */
- GC_word* actual_end = actual_start + length * words_per_element;
- return GC_gcj_vector_mark_proc(mark_stack_ptr, mark_stack_limit, element_desc, start, actual_end, words_per_element);
- }
- #endif // !IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION
- #endif // !RUNTIME_TINY
- void
- il2cpp::gc::GarbageCollector::Initialize()
- {
- if (s_GCInitialized)
- return;
- #if IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION
- il2cpp::gc::WriteBarrierValidation::Setup();
- #endif
- // This tells the GC that we are not scanning dynamic library data segments and that
- // the GC tracked data structures need ot be manually pushed and marked.
- // Call this before GC_INIT since the initialization logic uses this value.
- GC_set_no_dls(1);
- #if !IL2CPP_DEVELOPMENT
- // Turn off GC logging and warnings for non-development builds
- GC_set_warn_proc(GC_ignore_warn_proc);
- #endif
- #if IL2CPP_ENABLE_WRITE_BARRIERS
- GC_enable_incremental();
- #if IL2CPP_INCREMENTAL_TIME_SLICE
- GC_set_time_limit(IL2CPP_INCREMENTAL_TIME_SLICE);
- #endif
- #endif
- #if !RUNTIME_TINY
- default_push_other_roots = GC_get_push_other_roots();
- GC_set_push_other_roots(push_other_roots);
- GC_set_mark_stack_empty(push_ephemerons);
- #endif // !RUNTIME_TINY
- GC_set_on_collection_event(&on_gc_event);
- #if IL2CPP_ENABLE_PROFILER
- GC_set_on_heap_resize(&on_heap_resize);
- #endif
- GC_INIT();
- #if defined(GC_THREADS)
- GC_set_finalize_on_demand(1);
- #if !RUNTIME_TINY
- GC_set_finalizer_notifier(&il2cpp::gc::GarbageCollector::NotifyFinalizers);
- #endif
- // We need to call this if we want to manually register threads, i.e. GC_register_my_thread
- #if !IL2CPP_TARGET_JAVASCRIPT
- GC_allow_register_threads();
- #endif
- #endif
- #ifdef GC_GCJ_SUPPORT
- GC_init_gcj_malloc(0, NULL);
- #endif
- #if !RUNTIME_TINY && !IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION
- GC_init_gcj_vector(VECTOR_PROC_INDEX, (void*)GC_gcj_vector_proc);
- #endif
- s_GCInitialized = true;
- }
- void il2cpp::gc::GarbageCollector::UninitializeGC()
- {
- #if IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION
- il2cpp::gc::WriteBarrierValidation::Run();
- #endif
- GC_deinit();
- #if IL2CPP_ENABLE_RELOAD
- s_GCInitialized = false;
- default_push_other_roots = NULL;
- s_Roots.clear();
- #endif
- }
- int32_t
- il2cpp::gc::GarbageCollector::GetCollectionCount(int32_t generation)
- {
- return (int32_t)GC_get_gc_no();
- }
- int32_t
- il2cpp::gc::GarbageCollector::GetMaxGeneration()
- {
- return 0;
- }
- void
- il2cpp::gc::GarbageCollector::Collect(int maxGeneration)
- {
- #if IL2CPP_ENABLE_DEFERRED_GC
- if (GC_is_disabled())
- s_PendingGC = true;
- #endif
- GC_gcollect();
- }
- int32_t
- il2cpp::gc::GarbageCollector::CollectALittle()
- {
- #if IL2CPP_ENABLE_DEFERRED_GC
- if (s_PendingGC)
- {
- s_PendingGC = false;
- GC_gcollect();
- return 0; // no more work to do
- }
- else
- {
- return GC_collect_a_little();
- }
- #else
- return GC_collect_a_little();
- #endif
- }
- void
- il2cpp::gc::GarbageCollector::StartIncrementalCollection()
- {
- GC_start_incremental_collection();
- }
- #if IL2CPP_ENABLE_WRITE_BARRIERS
- void
- il2cpp::gc::GarbageCollector::SetWriteBarrier(void **ptr)
- {
- GC_END_STUBBORN_CHANGE(ptr);
- }
- #endif
- int64_t
- il2cpp::gc::GarbageCollector::GetUsedHeapSize(void)
- {
- return GC_get_heap_size() - GC_get_free_bytes();
- }
- int64_t
- il2cpp::gc::GarbageCollector::GetAllocatedHeapSize(void)
- {
- return GC_get_heap_size();
- }
- void
- il2cpp::gc::GarbageCollector::Disable()
- {
- GC_disable();
- }
- void
- il2cpp::gc::GarbageCollector::Enable()
- {
- GC_enable();
- }
- bool
- il2cpp::gc::GarbageCollector::IsDisabled()
- {
- return GC_is_disabled();
- }
- static baselib::ReentrantLock s_GCSetModeLock;
- void
- il2cpp::gc::GarbageCollector::SetMode(Il2CppGCMode mode)
- {
- os::FastAutoLock lock(&s_GCSetModeLock);
- switch (mode)
- {
- case IL2CPP_GC_MODE_ENABLED:
- if (GC_is_disabled())
- GC_enable();
- GC_set_disable_automatic_collection(false);
- break;
- case IL2CPP_GC_MODE_DISABLED:
- if (!GC_is_disabled())
- GC_disable();
- break;
- case IL2CPP_GC_MODE_MANUAL:
- if (GC_is_disabled())
- GC_enable();
- GC_set_disable_automatic_collection(true);
- break;
- }
- }
- void
- il2cpp::gc::GarbageCollector::RegisterThread()
- {
- #if defined(GC_THREADS) && !IL2CPP_TARGET_JAVASCRIPT
- struct GC_stack_base sb;
- int res;
- res = GC_get_stack_base(&sb);
- if (res != GC_SUCCESS)
- {
- /* Can't determine the register stack bounds */
- IL2CPP_ASSERT(false && "GC_get_stack_base () failed, aborting.");
- /* Abort we can't scan the stack, so we can't use the GC */
- abort();
- }
- res = GC_register_my_thread(&sb);
- if ((res != GC_SUCCESS) && (res != GC_DUPLICATE))
- {
- IL2CPP_ASSERT(false && "GC_register_my_thread () failed.");
- /* Abort we can't use the GC on this thread, so we can't run managed code */
- abort();
- }
- #endif
- }
- bool
- il2cpp::gc::GarbageCollector::UnregisterThread()
- {
- #if defined(GC_THREADS) && !IL2CPP_TARGET_JAVASCRIPT
- int res;
- res = GC_unregister_my_thread();
- if (res != GC_SUCCESS)
- IL2CPP_ASSERT(false && "GC_unregister_my_thread () failed.");
- return res == GC_SUCCESS;
- #else
- return true;
- #endif
- }
- il2cpp::gc::GarbageCollector::FinalizerCallback il2cpp::gc::GarbageCollector::RegisterFinalizerWithCallback(Il2CppObject* obj, FinalizerCallback callback)
- {
- FinalizerCallback oldCallback;
- void* oldData;
- GC_REGISTER_FINALIZER_NO_ORDER((char*)obj, callback, NULL, &oldCallback, &oldData);
- IL2CPP_ASSERT(oldData == NULL);
- return oldCallback;
- }
- void
- il2cpp::gc::GarbageCollector::AddWeakLink(void **link_addr, Il2CppObject *obj, bool track)
- {
- /* libgc requires that we use HIDE_POINTER... */
- *link_addr = (void*)GC_HIDE_POINTER(obj);
- // need this since our strings are not real objects
- if (GC_is_heap_ptr(obj))
- GC_GENERAL_REGISTER_DISAPPEARING_LINK(link_addr, obj);
- }
- void
- il2cpp::gc::GarbageCollector::RemoveWeakLink(void **link_addr)
- {
- Il2CppObject* obj = GarbageCollector::GetWeakLink(link_addr);
- if (GC_is_heap_ptr(obj))
- GC_unregister_disappearing_link(link_addr);
- *link_addr = NULL;
- }
- static void*
- RevealLink(void* link_addr)
- {
- void **link_a = (void**)link_addr;
- return GC_REVEAL_POINTER(*link_a);
- }
- Il2CppObject*
- il2cpp::gc::GarbageCollector::GetWeakLink(void **link_addr)
- {
- Il2CppObject *obj = (Il2CppObject*)GC_call_with_alloc_lock(RevealLink, link_addr);
- if (obj == (Il2CppObject*)-1)
- return NULL;
- return obj;
- }
- void*
- il2cpp::gc::GarbageCollector::MakeDescriptorForObject(size_t *bitmap, int numbits)
- {
- #ifdef GC_GCJ_SUPPORT
- /* It seems there are issues when the bitmap doesn't fit: play it safe */
- if (numbits >= 30)
- return GC_NO_DESCRIPTOR;
- else
- {
- GC_descr desc = GC_make_descriptor((GC_bitmap)bitmap, numbits);
- // we should always have a GC_DS_BITMAP descriptor, as we:
- // 1) Always want a precise marker.
- // 2) Can never be GC_DS_LENGTH since we always have an object header
- // at the beginning of the allocation.
- IL2CPP_ASSERT((desc & GC_DS_TAGS) == GC_DS_BITMAP || (desc & GC_DS_TAGS) == (GC_descr)GC_NO_DESCRIPTOR);
- return (void*)desc;
- }
- #else
- return 0;
- #endif
- }
- void* il2cpp::gc::GarbageCollector::MakeDescriptorForString()
- {
- return GC_NO_DESCRIPTOR;
- }
- void* il2cpp::gc::GarbageCollector::MakeDescriptorForArray()
- {
- return GC_NO_DESCRIPTOR;
- }
- void il2cpp::gc::GarbageCollector::StopWorld()
- {
- GC_stop_world_external();
- }
- void il2cpp::gc::GarbageCollector::StartWorld()
- {
- GC_start_world_external();
- }
- #if RUNTIME_TINY
- void*
- il2cpp::gc::GarbageCollector::Allocate(size_t size)
- {
- return GC_MALLOC(size);
- }
- void*
- il2cpp::gc::GarbageCollector::AllocateObject(size_t size, void* type)
- {
- #if IL2CPP_ENABLE_WRITE_BARRIER_VALIDATION
- return GC_gcj_malloc(size, type);
- #else
- return GC_MALLOC(size);
- #endif
- }
- #endif
- void*
- il2cpp::gc::GarbageCollector::AllocateFixed(size_t size, void *descr)
- {
- // Note that we changed the implementation from mono.
- // In our case, we expect that
- // a) This memory will never be moved
- // b) This memory will be scanned for references
- // c) This memory will remain 'alive' until explicitly freed
- // GC_MALLOC_UNCOLLECTABLE fulfills all these requirements
- // It does not accept a descriptor, but there was only one
- // or two places in mono that pass a descriptor to this routine
- // and we can or will support those use cases in a different manner.
- IL2CPP_ASSERT(!descr);
- return GC_MALLOC_UNCOLLECTABLE(size);
- }
- void
- il2cpp::gc::GarbageCollector::FreeFixed(void* addr)
- {
- GC_FREE(addr);
- }
- #if !RUNTIME_TINY
- int32_t
- il2cpp::gc::GarbageCollector::InvokeFinalizers()
- {
- #if IL2CPP_TINY
- return 0; // The Tiny profile does not have finalizers
- #else
- return (int32_t)GC_invoke_finalizers();
- #endif
- }
- bool
- il2cpp::gc::GarbageCollector::HasPendingFinalizers()
- {
- return GC_should_invoke_finalizers() != 0;
- }
- #endif
- int64_t
- il2cpp::gc::GarbageCollector::GetMaxTimeSliceNs()
- {
- return GC_get_time_limit_ns();
- }
- void
- il2cpp::gc::GarbageCollector::SetMaxTimeSliceNs(int64_t maxTimeSlice)
- {
- GC_set_time_limit_ns(maxTimeSlice);
- }
- bool
- il2cpp::gc::GarbageCollector::IsIncremental()
- {
- return GC_is_incremental_mode();
- }
- void on_gc_event(GC_EventType eventType)
- {
- #if !RUNTIME_TINY
- if (eventType == GC_EVENT_RECLAIM_START)
- {
- clear_ephemerons();
- }
- #endif
- #if IL2CPP_ENABLE_PROFILER
- Profiler::GCEvent((Il2CppGCEvent)eventType);
- #endif
- }
- #if IL2CPP_ENABLE_PROFILER
- void on_heap_resize(GC_word newSize)
- {
- Profiler::GCHeapResize((int64_t)newSize);
- }
- #endif // IL2CPP_ENABLE_PROFILER
- void il2cpp::gc::GarbageCollector::ForEachHeapSection(void* user_data, HeapSectionCallback callback)
- {
- GC_foreach_heap_section(user_data, callback);
- }
- size_t il2cpp::gc::GarbageCollector::GetSectionCount()
- {
- return GC_get_heap_section_count();
- }
- void* il2cpp::gc::GarbageCollector::CallWithAllocLockHeld(GCCallWithAllocLockCallback callback, void* user_data)
- {
- return GC_call_with_alloc_lock(callback, user_data);
- }
- typedef struct
- {
- char *start;
- char *end;
- } RootData;
- #if !RUNTIME_TINY
- static void*
- register_root(void* arg)
- {
- RootData* root_data = (RootData*)arg;
- s_Roots.insert(std::make_pair(root_data->start, root_data->end));
- return NULL;
- }
- void il2cpp::gc::GarbageCollector::RegisterRoot(char *start, size_t size)
- {
- RootData root_data;
- root_data.start = start;
- /* Boehm root processing requires one byte past end of region to be scanned */
- root_data.end = start + size + 1;
- CallWithAllocLockHeld(register_root, &root_data);
- }
- static void*
- deregister_root(void* arg)
- {
- s_Roots.erase((char*)arg);
- return NULL;
- }
- void il2cpp::gc::GarbageCollector::UnregisterRoot(char* start)
- {
- GC_call_with_alloc_lock(deregister_root, start);
- }
- struct DynamicRootData
- {
- void* root;
- il2cpp::gc::GarbageCollector::GetDynamicRootDataProc getRootDataFunc;
- };
- static void* register_dynamic_root(void* arg)
- {
- DynamicRootData* rootData = (DynamicRootData*)arg;
- IL2CPP_ASSERT(s_DynamicRoots.find(rootData->root) == s_DynamicRoots.end());
- s_DynamicRoots.add(rootData->root, rootData->getRootDataFunc);
-
- return NULL;
- }
- static void* deregister_dynamic_root(void* arg)
- {
- IL2CPP_ASSERT(s_DynamicRoots.find(arg) != s_DynamicRoots.end());
- s_DynamicRoots.erase(arg);
- return NULL;
- }
- void il2cpp::gc::GarbageCollector::RegisterDynamicRoot(void* root, GetDynamicRootDataProc getRootDataFunc)
- {
- DynamicRootData rootData = {root, getRootDataFunc};
- GC_call_with_alloc_lock(register_dynamic_root, &rootData);
- }
- void il2cpp::gc::GarbageCollector::UnregisterDynamicRoot(void* root)
- {
- GC_call_with_alloc_lock(deregister_dynamic_root, root);
- }
- static void
- push_other_roots(void)
- {
- for (RootMap::iterator iter = s_Roots.begin(); iter != s_Roots.end(); ++iter)
- GC_push_all(iter->first, iter->second);
- for (auto dynamicRootEntry : s_DynamicRoots)
- {
- std::pair<char*, size_t> dynamicRootData = dynamicRootEntry.second(dynamicRootEntry.first);
- if (dynamicRootData.first)
- {
- GC_push_all(dynamicRootData.first, dynamicRootData.first + dynamicRootData.second);
- }
- }
- GC_push_all(&ephemeron_list, &ephemeron_list + 1);
- if (default_push_other_roots)
- default_push_other_roots();
- }
- struct ephemeron_node
- {
- ephemeron_node* next;
- void* ephemeron_array_weak_link;
- };
- static void*
- ephemeron_array_add(void* arg)
- {
- ephemeron_node* item = (ephemeron_node*)arg;
- ephemeron_node* current = ephemeron_list;
- il2cpp::gc::WriteBarrier::GenericStore(&item->next, current);
- ephemeron_list = item;
- return NULL;
- }
- struct Ephemeron
- {
- Il2CppObject* key;
- Il2CppObject* value;
- };
- static void
- clear_ephemerons(void)
- {
- ephemeron_node* prev_node = NULL;
- ephemeron_node* current_node = NULL;
- /* iterate all registered Ephemeron[] */
- for (current_node = ephemeron_list; current_node; current_node = current_node->next)
- {
- Ephemeron* current_ephemeron, * array_end;
- Il2CppObject* tombstone = NULL;
- /* reveal weak link value*/
- Il2CppArray* array = (Il2CppArray*)GC_REVEAL_POINTER(current_node->ephemeron_array_weak_link);
- /* remove unmarked (non-reachable) arrays from the list */
- if (!GC_is_marked(array))
- {
- if (prev_node == NULL)
- il2cpp::gc::WriteBarrier::GenericStore(&ephemeron_list, current_node->next);
- else
- il2cpp::gc::WriteBarrier::GenericStore(&prev_node->next, current_node->next);
- continue;
- }
- prev_node = current_node;
- current_ephemeron = il2cpp_array_addr(array, Ephemeron, 0);
- array_end = current_ephemeron + array->max_length;
- tombstone = il2cpp::vm::Domain::GetCurrent()->ephemeron_tombstone;
- for (; current_ephemeron < array_end; ++current_ephemeron)
- {
- /* skip a null or tombstone (empty) key */
- if (!current_ephemeron->key || current_ephemeron->key == tombstone)
- continue;
- /* If the key is not marked, then set it to the tombstone and the value to NULL. */
- if (!GC_is_marked(current_ephemeron->key))
- {
- il2cpp::gc::WriteBarrier::GenericStore(¤t_ephemeron->key, tombstone);
- current_ephemeron->value = NULL;
- }
- }
- }
- }
- #if HYBRIDCLR_UNITY_VERSION >= 20210320
- static GC_ms_entry*
- push_ephemerons(GC_ms_entry* mark_stack_ptr, GC_ms_entry* mark_stack_limit)
- #else
- static void
- push_ephemerons(void)
- #endif
- {
- ephemeron_node* prev_node = NULL;
- ephemeron_node* current_node = NULL;
- /* iterate all registered Ephemeron[] */
- for (current_node = ephemeron_list; current_node; current_node = current_node->next)
- {
- Ephemeron* current_ephemeron, * array_end;
- Il2CppObject* tombstone = NULL;
- /* reveal weak link value*/
- Il2CppArray* array = (Il2CppArray*)GC_REVEAL_POINTER(current_node->ephemeron_array_weak_link);
- /* unreferenced array */
- if (!GC_is_marked(array))
- {
- continue;
- }
- prev_node = current_node;
- current_ephemeron = il2cpp_array_addr(array, Ephemeron, 0);
- array_end = current_ephemeron + array->max_length;
- tombstone = il2cpp::vm::Domain::GetCurrent()->ephemeron_tombstone;
- for (; current_ephemeron < array_end; ++current_ephemeron)
- {
- /* skip a null or tombstone (empty) key */
- if (!current_ephemeron->key || current_ephemeron->key == tombstone)
- continue;
- /* If the key is not marked, then don't mark value. */
- if (!GC_is_marked(current_ephemeron->key))
- continue;
- #if HYBRIDCLR_UNITY_VERSION >= 20210320
- if (current_ephemeron->value)
- {
- mark_stack_ptr = GC_mark_and_push((void*)current_ephemeron->value, mark_stack_ptr, mark_stack_limit, (void**)¤t_ephemeron->value);
- }
- #else
- if (current_ephemeron->value && !GC_is_marked(current_ephemeron->value))
- {
- /* the key is marked, so mark the value if needed */
- GC_push_all(¤t_ephemeron->value, ¤t_ephemeron->value + 1);
- }
- #endif
- }
- }
- #if HYBRIDCLR_UNITY_VERSION >= 20210320
- return mark_stack_ptr;
- #endif
- }
- bool il2cpp::gc::GarbageCollector::EphemeronArrayAdd(Il2CppObject* obj)
- {
- ephemeron_node* item = (ephemeron_node*)GC_MALLOC(sizeof(ephemeron_node));
- memset(item, 0, sizeof(ephemeron_node));
- AddWeakLink(&item->ephemeron_array_weak_link, obj, false);
- GC_call_with_alloc_lock(ephemeron_array_add, item);
- return true;
- }
- #endif // !RUNTIME_TINY
- #endif
|