#include "il2cpp-config.h" #include "gc/GCHandle.h" #include "il2cpp-object-internals.h" #include "GarbageCollector.h" #include "os/Mutex.h" #include "utils/Memory.h" #include namespace il2cpp { namespace gc { typedef struct { uint32_t *bitmap; void* *entries; uint32_t size; uint8_t type; uint32_t slot_hint : 24;/* starting slot for search */ /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */ /* we alloc this only for weak refs, since we can get the domain directly in the other cases */ uint16_t *domain_ids; } HandleData; /* weak and weak-track arrays will be allocated in malloc memory */ static HandleData gc_handles[] = { {NULL, NULL, 0, HANDLE_WEAK, 0}, {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0}, {NULL, NULL, 0, HANDLE_NORMAL, 0}, {NULL, NULL, 0, HANDLE_PINNED, 0} }; static int find_first_unset(uint32_t bitmap) { int i; for (i = 0; i < 32; ++i) { if (!(bitmap & (1 << i))) return i; } return -1; } static baselib::ReentrantLock g_HandlesMutex; #define lock_handles(handles) g_HandlesMutex.Acquire () #define unlock_handles(handles) g_HandlesMutex.Release () static uint32_t alloc_handle(HandleData *handles, Il2CppObject *obj, bool track) { uint32_t slot; int i; lock_handles(handles); if (!handles->size) { handles->size = 32; if (handles->type > HANDLE_WEAK_TRACK) { handles->entries = (void**)GarbageCollector::AllocateFixed(sizeof(void*) * handles->size, NULL); } else { handles->entries = (void**)IL2CPP_MALLOC_ZERO(sizeof(void*) * handles->size, IL2CPP_MEM_GC_HANDLE); handles->domain_ids = (uint16_t*)IL2CPP_MALLOC_ZERO(sizeof(uint16_t) * handles->size, IL2CPP_MEM_GC_HANDLE); } handles->bitmap = (uint32_t*)IL2CPP_MALLOC_ZERO(handles->size / 8, IL2CPP_MEM_GC_HANDLE); } i = -1; for (slot = handles->slot_hint; slot < handles->size / 32; ++slot) { if (handles->bitmap[slot] != 0xffffffff) { i = find_first_unset(handles->bitmap[slot]); handles->slot_hint = slot; break; } } if (i == -1 && handles->slot_hint != 0) { for (slot = 0; slot < handles->slot_hint; ++slot) { if (handles->bitmap[slot] != 0xffffffff) { i = find_first_unset(handles->bitmap[slot]); handles->slot_hint = slot; break; } } } if (i == -1) { uint32_t *new_bitmap; uint32_t new_size = handles->size * 2; /* always double: we memset to 0 based on this below */ /* resize and copy the bitmap */ new_bitmap = (uint32_t*)IL2CPP_MALLOC_ZERO(new_size / 8, IL2CPP_MEM_GC_HANDLE); memcpy(new_bitmap, handles->bitmap, handles->size / 8); IL2CPP_FREE(handles->bitmap, IL2CPP_MEM_GC_HANDLE); handles->bitmap = new_bitmap; /* resize and copy the entries */ if (handles->type > HANDLE_WEAK_TRACK) { void* *entries; entries = (void**)GarbageCollector::AllocateFixed(sizeof(void*) * new_size, NULL); memcpy(entries, handles->entries, sizeof(void*) * handles->size); GarbageCollector::SetWriteBarrier(entries, sizeof(void*) * handles->size); void** previous_entries = handles->entries; handles->entries = entries; GarbageCollector::FreeFixed(previous_entries); } else { void* *entries; uint16_t *domain_ids; domain_ids = (uint16_t*)IL2CPP_MALLOC_ZERO(sizeof(uint16_t) * new_size, IL2CPP_MEM_GC_HANDLE); entries = (void**)IL2CPP_MALLOC(sizeof(void*) * new_size, IL2CPP_MEM_GC_HANDLE); /* we disable GC because we could lose some disappearing link updates */ GarbageCollector::Disable(); memcpy(entries, handles->entries, sizeof(void*) * handles->size); memset(entries + handles->size, 0, sizeof(void*) * handles->size); memcpy(domain_ids, handles->domain_ids, sizeof(uint16_t) * handles->size); for (i = 0; i < (int32_t)handles->size; ++i) { Il2CppObject *obj = GarbageCollector::GetWeakLink(&(handles->entries[i])); if (handles->entries[i]) GarbageCollector::RemoveWeakLink(&(handles->entries[i])); /*g_print ("reg/unreg entry %d of type %d at %p to object %p (%p), was: %p\n", i, handles->type, &(entries [i]), obj, entries [i], handles->entries [i]);*/ if (obj) { GarbageCollector::AddWeakLink(&(entries[i]), obj, track); } } IL2CPP_FREE(handles->entries, IL2CPP_MEM_GC_HANDLE); IL2CPP_FREE(handles->domain_ids, IL2CPP_MEM_GC_HANDLE); handles->entries = entries; handles->domain_ids = domain_ids; GarbageCollector::Enable(); } /* set i and slot to the next free position */ i = 0; slot = (handles->size + 1) / 32; handles->slot_hint = handles->size + 1; handles->size = new_size; } handles->bitmap[slot] |= 1 << i; slot = slot * 32 + i; handles->entries[slot] = obj; GarbageCollector::SetWriteBarrier(handles->entries + slot); if (handles->type <= HANDLE_WEAK_TRACK) { if (obj) GarbageCollector::AddWeakLink(&(handles->entries[slot]), obj, track); } //mono_perfcounters->gc_num_handles++; unlock_handles(handles); /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/ return (slot << 3) | (handles->type + 1); } uint32_t GCHandle::New(Il2CppObject *obj, bool pinned) { return alloc_handle(&gc_handles[pinned ? HANDLE_PINNED : HANDLE_NORMAL], obj, false); } utils::Expected GCHandle::NewWeakref(Il2CppObject *obj, bool track_resurrection) { uint32_t handle = alloc_handle(&gc_handles[track_resurrection ? HANDLE_WEAK_TRACK : HANDLE_WEAK], obj, track_resurrection); #ifndef HAVE_SGEN_GC if (track_resurrection) return utils::Il2CppError(utils::NotSupported, "IL2CPP does not support resurrection for weak references. Pass the trackResurrection with a value of false."); #endif return handle; } GCHandleType GCHandle::GetHandleType(uint32_t gchandle) { return static_cast((gchandle & 7) - 1); } static inline uint32_t GetHandleSlot(uint32_t gchandle) { return gchandle >> 3; } Il2CppObject* GCHandle::GetTarget(uint32_t gchandle) { uint32_t slot = GetHandleSlot(gchandle); uint32_t type = GetHandleType(gchandle); HandleData *handles = &gc_handles[type]; Il2CppObject *obj = NULL; if (type > 3) return NULL; lock_handles(handles); if (slot < handles->size && (handles->bitmap[slot / 32] & (1 << (slot % 32)))) { if (handles->type <= HANDLE_WEAK_TRACK) { obj = GarbageCollector::GetWeakLink(&handles->entries[slot]); } else { obj = (Il2CppObject*)handles->entries[slot]; } } else { /* print a warning? */ } unlock_handles(handles); /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/ return obj; } static void il2cpp_gchandle_set_target(uint32_t gchandle, Il2CppObject *obj) { uint32_t slot = GetHandleSlot(gchandle); uint32_t type = GCHandle::GetHandleType(gchandle); HandleData *handles = &gc_handles[type]; Il2CppObject *old_obj = NULL; if (type > 3) return; lock_handles(handles); if (slot < handles->size && (handles->bitmap[slot / 32] & (1 << (slot % 32)))) { if (handles->type <= HANDLE_WEAK_TRACK) { old_obj = (Il2CppObject*)handles->entries[slot]; if (handles->entries[slot]) GarbageCollector::RemoveWeakLink(&handles->entries[slot]); if (obj) GarbageCollector::AddWeakLink(&handles->entries[slot], obj, handles->type == HANDLE_WEAK_TRACK); } else { handles->entries[slot] = obj; } } else { /* print a warning? */ } unlock_handles(handles); #ifndef HAVE_SGEN_GC if (type == HANDLE_WEAK_TRACK) IL2CPP_NOT_IMPLEMENTED(il2cpp_gchandle_set_target); #endif } void GCHandle::Free(uint32_t gchandle) { uint32_t slot = GetHandleSlot(gchandle); uint32_t type = GetHandleType(gchandle); HandleData *handles = &gc_handles[type]; if (type > 3) return; #ifndef HAVE_SGEN_GC if (type == HANDLE_WEAK_TRACK) IL2CPP_NOT_IMPLEMENTED(GCHandle::Free); #endif lock_handles(handles); if (slot < handles->size && (handles->bitmap[slot / 32] & (1 << (slot % 32)))) { if (handles->type <= HANDLE_WEAK_TRACK) { if (handles->entries[slot]) GarbageCollector::RemoveWeakLink(&handles->entries[slot]); } else { handles->entries[slot] = NULL; } handles->bitmap[slot / 32] &= ~(1 << (slot % 32)); } else { /* print a warning? */ } //mono_perfcounters->gc_num_handles--; /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/ unlock_handles(handles); } utils::Expected GCHandle::GetTargetHandle(Il2CppObject * obj, int32_t handle, int32_t type) { if (type == -1) { il2cpp_gchandle_set_target(handle, obj); /* the handle doesn't change */ return handle; } switch (type) { case HANDLE_WEAK: return NewWeakref(obj, false); case HANDLE_WEAK_TRACK: return NewWeakref(obj, true); case HANDLE_NORMAL: return New(obj, false); case HANDLE_PINNED: return New(obj, true); default: IL2CPP_ASSERT(0); } return 0; } void GCHandle::WalkStrongGCHandleTargets(WalkGCHandleTargetsCallback callback, void* context) { lock_handles(handles); const GCHandleType types[] = { HANDLE_NORMAL, HANDLE_PINNED }; for (int gcHandleTypeIndex = 0; gcHandleTypeIndex < 2; gcHandleTypeIndex++) { const HandleData& handles = gc_handles[types[gcHandleTypeIndex]]; for (uint32_t i = 0; i < handles.size; i++) { if (handles.entries[i] != NULL) callback(static_cast(handles.entries[i]), context); } } unlock_handles(handles); } } /* gc */ } /* il2cpp */