No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GCHandle.cpp 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. #include "il2cpp-config.h"
  2. #include "gc/GCHandle.h"
  3. #include "il2cpp-object-internals.h"
  4. #include "GarbageCollector.h"
  5. #include "os/Mutex.h"
  6. #include "utils/Memory.h"
  7. #include <memory>
  8. namespace il2cpp
  9. {
  10. namespace gc
  11. {
  12. #if IL2CPP_SIZEOF_VOID_P == 4
  13. #define HANDLE_COUNT 992
  14. #define HANDLE_DATA_ALIGNMENT 4096
  15. #else
  16. #define HANDLE_COUNT 992
  17. #define HANDLE_DATA_ALIGNMENT 8192
  18. #endif
  19. typedef struct HandleData HandleData;
  20. struct HandleData
  21. {
  22. HandleData *next; //immutable
  23. HandleData *next_free; // next free
  24. uint32_t *bitmap;
  25. uint32_t in_use;
  26. uint32_t size;
  27. uint8_t type;
  28. uint32_t slot_hint : 24; /* starting slot for search in bitmap */
  29. void* entries[HANDLE_COUNT];
  30. };
  31. static HandleData* gc_handles[HANDLE_PINNED + 1];
  32. static HandleData* gc_handles_free[HANDLE_PINNED + 1];
  33. inline bool HandleTypeIsWeak(GCHandleType type)
  34. {
  35. return type == GCHandleType::HANDLE_WEAK || type == GCHandleType::HANDLE_WEAK_TRACK;
  36. }
  37. #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
  38. static bool
  39. slot_occupied(HandleData* handles, uint32_t slot)
  40. {
  41. return handles->bitmap[slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
  42. }
  43. static void
  44. vacate_slot(HandleData* handles, uint32_t slot)
  45. {
  46. handles->in_use--;
  47. handles->bitmap[slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
  48. if (handles->in_use == (handles->size - 1))
  49. {
  50. uint8_t type = handles->type;
  51. HandleData* first = gc_handles_free[type];
  52. handles->next_free = first;
  53. gc_handles_free[type] = handles;
  54. }
  55. }
  56. static void
  57. occupy_slot(HandleData* handles, uint32_t slot)
  58. {
  59. handles->in_use++;
  60. handles->bitmap[slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
  61. if (handles->in_use == handles->size)
  62. {
  63. uint8_t type = handles->type;
  64. IL2CPP_ASSERT(handles == gc_handles_free[type]);
  65. gc_handles_free[type] = gc_handles_free[type]->next_free;
  66. }
  67. }
  68. static int
  69. find_first_unset(uint32_t bitmap)
  70. {
  71. int i;
  72. for (i = 0; i < 32; ++i)
  73. {
  74. if (!(bitmap & (1 << i)))
  75. return i;
  76. }
  77. return -1;
  78. }
  79. static HandleData*
  80. handle_data_alloc_entries(int type)
  81. {
  82. IL2CPP_ASSERT(sizeof(HandleData) < HANDLE_DATA_ALIGNMENT);
  83. IL2CPP_ASSERT(HANDLE_COUNT % BITMAP_SIZE == 0);
  84. HandleData* handles = (HandleData*)utils::Memory::AlignedMalloc(sizeof(HandleData), HANDLE_DATA_ALIGNMENT);
  85. memset(handles, 0, sizeof(HandleData));
  86. handles->type = type;
  87. handles->size = HANDLE_COUNT;
  88. if (!HandleTypeIsWeak((GCHandleType)handles->type))
  89. {
  90. GarbageCollector::RegisterRoot((char*)&handles->entries[0], HANDLE_COUNT * sizeof(void*));
  91. }
  92. handles->bitmap = (uint32_t*)utils::Memory::Calloc(sizeof(char), handles->size / CHAR_BIT);
  93. return handles;
  94. }
  95. static int32_t
  96. handle_data_next_unset(HandleData* handles)
  97. {
  98. uint32_t slot;
  99. for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot)
  100. {
  101. if (handles->bitmap[slot] == 0xffffffff)
  102. continue;
  103. handles->slot_hint = slot;
  104. return find_first_unset(handles->bitmap[slot]);
  105. }
  106. return -1;
  107. }
  108. static int32_t
  109. handle_data_first_unset(HandleData* handles)
  110. {
  111. uint32_t slot;
  112. for (slot = 0; slot < handles->slot_hint; ++slot)
  113. {
  114. if (handles->bitmap[slot] == 0xffffffff)
  115. continue;
  116. handles->slot_hint = slot;
  117. return find_first_unset(handles->bitmap[slot]);
  118. }
  119. return -1;
  120. }
  121. static int32_t
  122. handle_data_find_slot(HandleData* handles)
  123. {
  124. int32_t slot = 0;
  125. int32_t i = handle_data_next_unset(handles);
  126. if (i == -1 && handles->slot_hint != 0)
  127. i = handle_data_first_unset(handles);
  128. IL2CPP_ASSERT(i != -1);
  129. slot = handles->slot_hint * BITMAP_SIZE + i;
  130. return slot;
  131. }
  132. static Il2CppGCHandle
  133. handle_tag_weak(Il2CppGCHandle handle)
  134. {
  135. return (Il2CppGCHandle)((uintptr_t)handle | (uintptr_t)1);
  136. }
  137. static Il2CppGCHandle
  138. handle_untag_weak(Il2CppGCHandle handle)
  139. {
  140. return (Il2CppGCHandle)((uintptr_t)handle & ~(uintptr_t)1);
  141. }
  142. static uintptr_t AlignDownTo(uintptr_t size, uintptr_t align)
  143. {
  144. return size & ~(align - 1);
  145. }
  146. static HandleData*
  147. get_handle_data_from_handle(Il2CppGCHandle handle)
  148. {
  149. HandleData* handles = (HandleData*)AlignDownTo((uintptr_t)handle, HANDLE_DATA_ALIGNMENT);
  150. return handles;
  151. }
  152. static HandleData*
  153. handle_lookup(Il2CppGCHandle handle, uint32_t* slot)
  154. {
  155. HandleData* handles = get_handle_data_from_handle(handle);
  156. if (slot)
  157. *slot = (uint32_t)(ptrdiff_t)((void**)handle_untag_weak(handle) - &handles->entries[0]);
  158. return handles;
  159. }
  160. static baselib::ReentrantLock g_HandlesMutex;
  161. #define lock_handles(handles) g_HandlesMutex.Acquire ()
  162. #define unlock_handles(handles) g_HandlesMutex.Release ()
  163. static Il2CppGCHandle
  164. alloc_handle(GCHandleType type, Il2CppObject *obj, bool track)
  165. {
  166. int32_t slot = 0;
  167. Il2CppGCHandle res = 0;
  168. HandleData* handles = gc_handles[type];
  169. lock_handles(handles);
  170. handles = gc_handles_free[type];
  171. if (!handles)
  172. {
  173. handles = handle_data_alloc_entries(type);
  174. handles->next = gc_handles[type];
  175. gc_handles[type] = handles;
  176. handles->next_free = gc_handles_free[type];
  177. gc_handles_free[type] = handles;
  178. }
  179. slot = handle_data_find_slot(handles);
  180. occupy_slot(handles, slot);
  181. handles->entries[slot] = NULL;
  182. if (handles->type <= HANDLE_WEAK_TRACK)
  183. {
  184. if (obj)
  185. GarbageCollector::AddWeakLink(&(handles->entries[slot]), obj, track);
  186. }
  187. else
  188. {
  189. handles->entries[slot] = obj;
  190. GarbageCollector::SetWriteBarrier(handles->entries + slot);
  191. }
  192. //mono_perfcounters->gc_num_handles++;
  193. unlock_handles(handles);
  194. res = (Il2CppGCHandle) & handles->entries[slot];
  195. if (HandleTypeIsWeak((GCHandleType)handles->type))
  196. {
  197. /*
  198. * Use lowest bit as an optimization to indicate weak GC handle.
  199. * This allows client code to simply dereference strong GCHandle
  200. * when the bit is not set.
  201. */
  202. res = handle_tag_weak(res);
  203. }
  204. return res;
  205. }
  206. Il2CppGCHandle GCHandle::New(Il2CppObject *obj, bool pinned)
  207. {
  208. return alloc_handle(pinned ? HANDLE_PINNED : HANDLE_NORMAL, obj, false);
  209. }
  210. utils::Expected<Il2CppGCHandle> GCHandle::NewWeakref(Il2CppObject *obj, bool track_resurrection)
  211. {
  212. Il2CppGCHandle handle = alloc_handle(track_resurrection ? HANDLE_WEAK_TRACK : HANDLE_WEAK, obj, track_resurrection);
  213. #ifndef HAVE_SGEN_GC
  214. if (track_resurrection)
  215. return utils::Il2CppError(utils::NotSupported, "IL2CPP does not support resurrection for weak references. Pass the trackResurrection with a value of false.");
  216. #endif
  217. return (Il2CppGCHandle)handle;
  218. }
  219. GCHandleType GCHandle::GetHandleType(Il2CppGCHandle gchandle)
  220. {
  221. HandleData* handles = handle_lookup(gchandle, NULL);
  222. return (GCHandleType)handles->type;
  223. }
  224. Il2CppObject* GCHandle::GetTarget(Il2CppGCHandle gchandle)
  225. {
  226. uint32_t slot = 0;
  227. HandleData* handles = handle_lookup(gchandle, &slot);
  228. Il2CppObject *obj = NULL;
  229. if (handles->type >= HANDLE_TYPE_MAX)
  230. return NULL;
  231. lock_handles(handles);
  232. if (slot < handles->size && slot_occupied(handles, slot))
  233. {
  234. if (handles->type <= HANDLE_WEAK_TRACK)
  235. {
  236. obj = GarbageCollector::GetWeakLink(&handles->entries[slot]);
  237. }
  238. else
  239. {
  240. obj = (Il2CppObject*)handles->entries[slot];
  241. }
  242. }
  243. else
  244. {
  245. /* print a warning? */
  246. }
  247. unlock_handles(handles);
  248. /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
  249. return obj;
  250. }
  251. static void
  252. il2cpp_gchandle_set_target(Il2CppGCHandle gchandle, Il2CppObject *obj)
  253. {
  254. uint32_t slot = 0;
  255. HandleData* handles = handle_lookup(gchandle, &slot);
  256. Il2CppObject *old_obj = NULL;
  257. IL2CPP_ASSERT(handles->type < HANDLE_TYPE_MAX);
  258. lock_handles(handles);
  259. if (slot < handles->size && slot_occupied(handles, slot))
  260. {
  261. if (handles->type <= HANDLE_WEAK_TRACK)
  262. {
  263. old_obj = (Il2CppObject*)handles->entries[slot];
  264. if (handles->entries[slot])
  265. GarbageCollector::RemoveWeakLink(&handles->entries[slot]);
  266. if (obj)
  267. GarbageCollector::AddWeakLink(&handles->entries[slot], obj, handles->type == HANDLE_WEAK_TRACK);
  268. }
  269. else
  270. {
  271. handles->entries[slot] = obj;
  272. }
  273. }
  274. else
  275. {
  276. /* print a warning? */
  277. }
  278. unlock_handles(handles);
  279. }
  280. void GCHandle::Free(Il2CppGCHandle gchandle)
  281. {
  282. if (!gchandle)
  283. return;
  284. uint32_t slot = 0;
  285. HandleData* handles = handle_lookup(gchandle, &slot);
  286. if (handles->type >= HANDLE_TYPE_MAX)
  287. return;
  288. lock_handles(handles);
  289. if (slot < handles->size && slot_occupied(handles, slot))
  290. {
  291. if (HandleTypeIsWeak((GCHandleType)handles->type))
  292. {
  293. if (handles->entries[slot])
  294. GarbageCollector::RemoveWeakLink(&handles->entries[slot] /*, handles->type == HANDLE_WEAK_TRACK*/);
  295. }
  296. else
  297. {
  298. handles->entries[slot] = NULL;
  299. }
  300. vacate_slot(handles, slot);
  301. }
  302. else
  303. {
  304. /* print a warning? */
  305. }
  306. unlock_handles(handles);
  307. }
  308. utils::Expected<Il2CppGCHandle> GCHandle::GetTargetHandle(Il2CppObject * obj, Il2CppGCHandle handle, int32_t type)
  309. {
  310. if (type == -1)
  311. {
  312. il2cpp_gchandle_set_target(handle, obj);
  313. /* the handle doesn't change */
  314. return handle;
  315. }
  316. switch (type)
  317. {
  318. case HANDLE_WEAK:
  319. return NewWeakref(obj, false);
  320. case HANDLE_WEAK_TRACK:
  321. return NewWeakref(obj, true);
  322. case HANDLE_NORMAL:
  323. return New(obj, false);
  324. case HANDLE_PINNED:
  325. return New(obj, true);
  326. default:
  327. IL2CPP_ASSERT(0);
  328. }
  329. return 0;
  330. }
  331. void GCHandle::WalkStrongGCHandleTargets(WalkGCHandleTargetsCallback callback, void* context)
  332. {
  333. lock_handles(handles);
  334. const GCHandleType types[] = { HANDLE_NORMAL, HANDLE_PINNED };
  335. for (int gcHandleTypeIndex = 0; gcHandleTypeIndex < 2; gcHandleTypeIndex++)
  336. {
  337. const HandleData* handles = gc_handles[types[gcHandleTypeIndex]];
  338. while (handles != NULL)
  339. {
  340. for (uint32_t i = 0; i < handles->size; i++)
  341. {
  342. if (handles->entries[i] != NULL)
  343. callback(static_cast<Il2CppObject*>(handles->entries[i]), context);
  344. }
  345. handles = handles->next;
  346. }
  347. }
  348. unlock_handles(handles);
  349. }
  350. } /* gc */
  351. } /* il2cpp */