123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- #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 <memory>
-
- namespace il2cpp
- {
- namespace gc
- {
- #if IL2CPP_SIZEOF_VOID_P == 4
- #define HANDLE_COUNT 992
- #define HANDLE_DATA_ALIGNMENT 4096
- #else
- #define HANDLE_COUNT 992
- #define HANDLE_DATA_ALIGNMENT 8192
- #endif
-
- typedef struct HandleData HandleData;
- struct HandleData
- {
- HandleData *next; //immutable
- HandleData *next_free; // next free
- uint32_t *bitmap;
- uint32_t in_use;
- uint32_t size;
- uint8_t type;
- uint32_t slot_hint : 24; /* starting slot for search in bitmap */
- void* entries[HANDLE_COUNT];
- };
-
-
- static HandleData* gc_handles[HANDLE_PINNED + 1];
- static HandleData* gc_handles_free[HANDLE_PINNED + 1];
-
- inline bool HandleTypeIsWeak(GCHandleType type)
- {
- return type == GCHandleType::HANDLE_WEAK || type == GCHandleType::HANDLE_WEAK_TRACK;
- }
-
- #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
-
- static bool
- slot_occupied(HandleData* handles, uint32_t slot)
- {
- return handles->bitmap[slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
- }
-
- static void
- vacate_slot(HandleData* handles, uint32_t slot)
- {
- handles->in_use--;
- handles->bitmap[slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
-
- if (handles->in_use == (handles->size - 1))
- {
- uint8_t type = handles->type;
- HandleData* first = gc_handles_free[type];
- handles->next_free = first;
- gc_handles_free[type] = handles;
- }
- }
-
- static void
- occupy_slot(HandleData* handles, uint32_t slot)
- {
- handles->in_use++;
- handles->bitmap[slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
-
- if (handles->in_use == handles->size)
- {
- uint8_t type = handles->type;
- IL2CPP_ASSERT(handles == gc_handles_free[type]);
- gc_handles_free[type] = gc_handles_free[type]->next_free;
- }
- }
-
- 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 HandleData*
- handle_data_alloc_entries(int type)
- {
- IL2CPP_ASSERT(sizeof(HandleData) < HANDLE_DATA_ALIGNMENT);
- IL2CPP_ASSERT(HANDLE_COUNT % BITMAP_SIZE == 0);
- HandleData* handles = (HandleData*)utils::Memory::AlignedMalloc(sizeof(HandleData), HANDLE_DATA_ALIGNMENT);
- memset(handles, 0, sizeof(HandleData));
- handles->type = type;
- handles->size = HANDLE_COUNT;
- if (!HandleTypeIsWeak((GCHandleType)handles->type))
- {
- GarbageCollector::RegisterRoot((char*)&handles->entries[0], HANDLE_COUNT * sizeof(void*));
- }
- handles->bitmap = (uint32_t*)utils::Memory::Calloc(sizeof(char), handles->size / CHAR_BIT);
-
- return handles;
- }
-
- static int32_t
- handle_data_next_unset(HandleData* handles)
- {
- uint32_t slot;
- for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot)
- {
- if (handles->bitmap[slot] == 0xffffffff)
- continue;
- handles->slot_hint = slot;
- return find_first_unset(handles->bitmap[slot]);
- }
- return -1;
- }
-
- static int32_t
- handle_data_first_unset(HandleData* handles)
- {
- uint32_t slot;
- for (slot = 0; slot < handles->slot_hint; ++slot)
- {
- if (handles->bitmap[slot] == 0xffffffff)
- continue;
- handles->slot_hint = slot;
- return find_first_unset(handles->bitmap[slot]);
- }
- return -1;
- }
-
- static int32_t
- handle_data_find_slot(HandleData* handles)
- {
- int32_t slot = 0;
- int32_t i = handle_data_next_unset(handles);
- if (i == -1 && handles->slot_hint != 0)
- i = handle_data_first_unset(handles);
-
- IL2CPP_ASSERT(i != -1);
-
- slot = handles->slot_hint * BITMAP_SIZE + i;
- return slot;
- }
-
- static Il2CppGCHandle
- handle_tag_weak(Il2CppGCHandle handle)
- {
- return (Il2CppGCHandle)((uintptr_t)handle | (uintptr_t)1);
- }
-
- static Il2CppGCHandle
- handle_untag_weak(Il2CppGCHandle handle)
- {
- return (Il2CppGCHandle)((uintptr_t)handle & ~(uintptr_t)1);
- }
-
- static uintptr_t AlignDownTo(uintptr_t size, uintptr_t align)
- {
- return size & ~(align - 1);
- }
-
- static HandleData*
- get_handle_data_from_handle(Il2CppGCHandle handle)
- {
- HandleData* handles = (HandleData*)AlignDownTo((uintptr_t)handle, HANDLE_DATA_ALIGNMENT);
- return handles;
- }
-
- static HandleData*
- handle_lookup(Il2CppGCHandle handle, uint32_t* slot)
- {
- HandleData* handles = get_handle_data_from_handle(handle);
- if (slot)
- *slot = (uint32_t)(ptrdiff_t)((void**)handle_untag_weak(handle) - &handles->entries[0]);
- return handles;
- }
-
- static baselib::ReentrantLock g_HandlesMutex;
-
- #define lock_handles(handles) g_HandlesMutex.Acquire ()
- #define unlock_handles(handles) g_HandlesMutex.Release ()
-
- static Il2CppGCHandle
- alloc_handle(GCHandleType type, Il2CppObject *obj, bool track)
- {
- int32_t slot = 0;
- Il2CppGCHandle res = 0;
- HandleData* handles = gc_handles[type];
- lock_handles(handles);
- handles = gc_handles_free[type];
- if (!handles)
- {
- handles = handle_data_alloc_entries(type);
- handles->next = gc_handles[type];
- gc_handles[type] = handles;
-
- handles->next_free = gc_handles_free[type];
- gc_handles_free[type] = handles;
- }
- slot = handle_data_find_slot(handles);
- occupy_slot(handles, slot);
-
- handles->entries[slot] = NULL;
- if (handles->type <= HANDLE_WEAK_TRACK)
- {
- if (obj)
- GarbageCollector::AddWeakLink(&(handles->entries[slot]), obj, track);
- }
- else
- {
- handles->entries[slot] = obj;
- GarbageCollector::SetWriteBarrier(handles->entries + slot);
- }
-
- //mono_perfcounters->gc_num_handles++;
- unlock_handles(handles);
-
- res = (Il2CppGCHandle) & handles->entries[slot];
- if (HandleTypeIsWeak((GCHandleType)handles->type))
- {
- /*
- * Use lowest bit as an optimization to indicate weak GC handle.
- * This allows client code to simply dereference strong GCHandle
- * when the bit is not set.
- */
- res = handle_tag_weak(res);
- }
- return res;
- }
-
- Il2CppGCHandle GCHandle::New(Il2CppObject *obj, bool pinned)
- {
- return alloc_handle(pinned ? HANDLE_PINNED : HANDLE_NORMAL, obj, false);
- }
-
- utils::Expected<Il2CppGCHandle> GCHandle::NewWeakref(Il2CppObject *obj, bool track_resurrection)
- {
- Il2CppGCHandle handle = alloc_handle(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 (Il2CppGCHandle)handle;
- }
-
- GCHandleType GCHandle::GetHandleType(Il2CppGCHandle gchandle)
- {
- HandleData* handles = handle_lookup(gchandle, NULL);
- return (GCHandleType)handles->type;
- }
-
- Il2CppObject* GCHandle::GetTarget(Il2CppGCHandle gchandle)
- {
- uint32_t slot = 0;
- HandleData* handles = handle_lookup(gchandle, &slot);
- Il2CppObject *obj = NULL;
-
- if (handles->type >= HANDLE_TYPE_MAX)
- return NULL;
-
- lock_handles(handles);
- if (slot < handles->size && slot_occupied(handles, slot))
- {
- 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(Il2CppGCHandle gchandle, Il2CppObject *obj)
- {
- uint32_t slot = 0;
- HandleData* handles = handle_lookup(gchandle, &slot);
- Il2CppObject *old_obj = NULL;
-
-
- IL2CPP_ASSERT(handles->type < HANDLE_TYPE_MAX);
- lock_handles(handles);
- if (slot < handles->size && slot_occupied(handles, slot))
- {
- 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);
- }
-
- void GCHandle::Free(Il2CppGCHandle gchandle)
- {
- if (!gchandle)
- return;
-
- uint32_t slot = 0;
- HandleData* handles = handle_lookup(gchandle, &slot);
- if (handles->type >= HANDLE_TYPE_MAX)
- return;
-
- lock_handles(handles);
- if (slot < handles->size && slot_occupied(handles, slot))
- {
- if (HandleTypeIsWeak((GCHandleType)handles->type))
- {
- if (handles->entries[slot])
- GarbageCollector::RemoveWeakLink(&handles->entries[slot] /*, handles->type == HANDLE_WEAK_TRACK*/);
- }
- else
- {
- handles->entries[slot] = NULL;
- }
- vacate_slot(handles, slot);
- }
- else
- {
- /* print a warning? */
- }
-
- unlock_handles(handles);
- }
-
- utils::Expected<Il2CppGCHandle> GCHandle::GetTargetHandle(Il2CppObject * obj, Il2CppGCHandle 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]];
-
- while (handles != NULL)
- {
- for (uint32_t i = 0; i < handles->size; i++)
- {
- if (handles->entries[i] != NULL)
- callback(static_cast<Il2CppObject*>(handles->entries[i]), context);
- }
-
- handles = handles->next;
- }
- }
- unlock_handles(handles);
- }
- } /* gc */
- } /* il2cpp */
|