ZK_Framework/HybridCLRData/LocalIl2CppData-WindowsEditor/il2cpp/libil2cpp/gc/GCHandle.cpp

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 */