343 lines
12 KiB
C++
343 lines
12 KiB
C++
#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
|
|
{
|
|
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);
|
|
handles->domain_ids = (uint16_t*)IL2CPP_MALLOC_ZERO(sizeof(uint16_t) * handles->size);
|
|
}
|
|
handles->bitmap = (uint32_t*)IL2CPP_MALLOC_ZERO(handles->size / 8);
|
|
}
|
|
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);
|
|
memcpy(new_bitmap, handles->bitmap, handles->size / 8);
|
|
IL2CPP_FREE(handles->bitmap);
|
|
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);
|
|
entries = (void**)IL2CPP_MALLOC(sizeof(void*) * new_size);
|
|
/* 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_FREE(handles->domain_ids);
|
|
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<uint32_t> 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<GCHandleType>((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<uint32_t> 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<Il2CppObject*>(handles.entries[i]), context);
|
|
}
|
|
}
|
|
unlock_handles(handles);
|
|
}
|
|
} /* gc */
|
|
} /* il2cpp */
|