lock-free-alloc.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. /**
  2. * \file
  3. * Lock free allocator.
  4. *
  5. * (C) Copyright 2011 Novell, Inc
  6. *
  7. * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  8. */
  9. /*
  10. * This is a simplified version of the lock-free allocator described in
  11. *
  12. * Scalable Lock-Free Dynamic Memory Allocation
  13. * Maged M. Michael, PLDI 2004
  14. *
  15. * I could not get Michael's allocator working bug free under heavy
  16. * stress tests. The paper doesn't provide correctness proof and after
  17. * failing to formalize the ownership of descriptors I devised this
  18. * simpler allocator.
  19. *
  20. * Allocation within superblocks proceeds exactly like in Michael's
  21. * allocator. The simplification is that a thread has to "acquire" a
  22. * descriptor before it can allocate from its superblock. While it owns
  23. * the descriptor no other thread can acquire and hence allocate from
  24. * it. A consequence of this is that the ABA problem cannot occur, so
  25. * we don't need the tag field and don't have to use 64 bit CAS.
  26. *
  27. * Descriptors are stored in two locations: The partial queue and the
  28. * active field. They can only be in at most one of those at one time.
  29. * If a thread wants to allocate, it needs to get a descriptor. It
  30. * tries the active descriptor first, CASing it to NULL. If that
  31. * doesn't work, it gets a descriptor out of the partial queue. Once it
  32. * has the descriptor it owns it because it is not referenced anymore.
  33. * It allocates a slot and then gives the descriptor back (unless it is
  34. * FULL).
  35. *
  36. * Note that it is still possible that a slot is freed while an
  37. * allocation is in progress from the same superblock. Ownership in
  38. * this case is not complicated, though. If the block was FULL and the
  39. * free set it to PARTIAL, the free now owns the block (because FULL
  40. * blocks are not referenced from partial and active) and has to give it
  41. * back. If the block was PARTIAL then the free doesn't own the block
  42. * (because it's either still referenced, or an alloc owns it). A
  43. * special case of this is that it has changed from PARTIAL to EMPTY and
  44. * now needs to be retired. Technically, the free wouldn't have to do
  45. * anything in this case because the first thing an alloc does when it
  46. * gets ownership of a descriptor is to check whether it is EMPTY and
  47. * retire it if that is the case. As an optimization, our free does try
  48. * to acquire the descriptor (by CASing the active field, which, if it
  49. * is lucky, points to that descriptor) and if it can do so, retire it.
  50. * If it can't, it tries to retire other descriptors from the partial
  51. * queue, so that we can be sure that even if no more allocations
  52. * happen, descriptors are still retired. This is analogous to what
  53. * Michael's allocator does.
  54. *
  55. * Another difference to Michael's allocator is not related to
  56. * concurrency, however: We don't point from slots to descriptors.
  57. * Instead we allocate superblocks aligned and point from the start of
  58. * the superblock to the descriptor, so we only need one word of
  59. * metadata per superblock.
  60. *
  61. * FIXME: Having more than one allocator per size class is probably
  62. * buggy because it was never tested.
  63. */
  64. #include <glib.h>
  65. #include <stdlib.h>
  66. #include <mono/utils/atomic.h>
  67. #ifdef SGEN_WITHOUT_MONO
  68. #include <mono/sgen/sgen-gc.h>
  69. #include <mono/sgen/sgen-client.h>
  70. #else
  71. #include <mono/utils/mono-mmap.h>
  72. #endif
  73. #include <mono/utils/mono-membar.h>
  74. #include <mono/utils/hazard-pointer.h>
  75. #include <mono/utils/lock-free-queue.h>
  76. #include <mono/utils/lock-free-alloc.h>
  77. //#define DESC_AVAIL_DUMMY
  78. enum {
  79. STATE_FULL,
  80. STATE_PARTIAL,
  81. STATE_EMPTY
  82. };
  83. typedef union {
  84. gint32 value;
  85. struct {
  86. guint32 avail : 15;
  87. guint32 count : 15;
  88. guint32 state : 2;
  89. } data;
  90. } Anchor;
  91. typedef struct _MonoLockFreeAllocDescriptor Descriptor;
  92. struct _MonoLockFreeAllocDescriptor {
  93. MonoLockFreeQueueNode node;
  94. MonoLockFreeAllocator *heap;
  95. volatile Anchor anchor;
  96. unsigned int slot_size;
  97. unsigned int block_size;
  98. unsigned int max_count;
  99. gpointer sb;
  100. #ifndef DESC_AVAIL_DUMMY
  101. Descriptor * volatile next;
  102. #endif
  103. gboolean in_use; /* used for debugging only */
  104. };
  105. #define NUM_DESC_BATCH 64
  106. static MONO_ALWAYS_INLINE gpointer
  107. sb_header_for_addr (gpointer addr, size_t block_size)
  108. {
  109. return (gpointer)(((size_t)addr) & (~(block_size - 1)));
  110. }
  111. /* Taken from SGen */
  112. static unsigned long
  113. prot_flags_for_activate (int activate)
  114. {
  115. unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
  116. return prot_flags | MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
  117. }
  118. static gpointer
  119. alloc_sb (Descriptor *desc)
  120. {
  121. static int pagesize = -1;
  122. gpointer sb_header;
  123. if (pagesize == -1)
  124. pagesize = mono_pagesize ();
  125. sb_header = desc->block_size == pagesize ?
  126. mono_valloc (NULL, desc->block_size, prot_flags_for_activate (TRUE), desc->heap->account_type) :
  127. mono_valloc_aligned (desc->block_size, desc->block_size, prot_flags_for_activate (TRUE), desc->heap->account_type);
  128. g_assertf (sb_header, "Failed to allocate memory for the lock free allocator");
  129. g_assert (sb_header == sb_header_for_addr (sb_header, desc->block_size));
  130. *(Descriptor**)sb_header = desc;
  131. //g_print ("sb %p for %p\n", sb_header, desc);
  132. return (char*)sb_header + LOCK_FREE_ALLOC_SB_HEADER_SIZE;
  133. }
  134. static void
  135. free_sb (gpointer sb, size_t block_size, MonoMemAccountType type)
  136. {
  137. gpointer sb_header = sb_header_for_addr (sb, block_size);
  138. g_assert ((char*)sb_header + LOCK_FREE_ALLOC_SB_HEADER_SIZE == sb);
  139. mono_vfree (sb_header, block_size, type);
  140. //g_print ("free sb %p\n", sb_header);
  141. }
  142. #ifndef DESC_AVAIL_DUMMY
  143. static Descriptor * volatile desc_avail;
  144. static Descriptor*
  145. desc_alloc (MonoMemAccountType type)
  146. {
  147. MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
  148. Descriptor *desc;
  149. for (;;) {
  150. gboolean success;
  151. desc = (Descriptor *) mono_get_hazardous_pointer ((volatile gpointer *)&desc_avail, hp, 1);
  152. if (desc) {
  153. Descriptor *next = desc->next;
  154. success = (mono_atomic_cas_ptr ((volatile gpointer *)&desc_avail, next, desc) == desc);
  155. } else {
  156. size_t desc_size = sizeof (Descriptor);
  157. Descriptor *d;
  158. int i;
  159. desc = (Descriptor *) mono_valloc (NULL, desc_size * NUM_DESC_BATCH, prot_flags_for_activate (TRUE), type);
  160. g_assertf (desc, "Failed to allocate memory for the lock free allocator");
  161. /* Organize into linked list. */
  162. d = desc;
  163. for (i = 0; i < NUM_DESC_BATCH; ++i) {
  164. Descriptor *next = (i == (NUM_DESC_BATCH - 1)) ? NULL : (Descriptor*)((char*)desc + ((i + 1) * desc_size));
  165. d->next = next;
  166. mono_lock_free_queue_node_init (&d->node, TRUE);
  167. d = next;
  168. }
  169. mono_memory_write_barrier ();
  170. success = (mono_atomic_cas_ptr ((volatile gpointer *)&desc_avail, desc->next, NULL) == NULL);
  171. if (!success)
  172. mono_vfree (desc, desc_size * NUM_DESC_BATCH, type);
  173. }
  174. mono_hazard_pointer_clear (hp, 1);
  175. if (success)
  176. break;
  177. }
  178. g_assert (!desc->in_use);
  179. desc->in_use = TRUE;
  180. return desc;
  181. }
  182. static void
  183. desc_enqueue_avail (gpointer _desc)
  184. {
  185. Descriptor *desc = (Descriptor *) _desc;
  186. Descriptor *old_head;
  187. g_assert (desc->anchor.data.state == STATE_EMPTY);
  188. g_assert (!desc->in_use);
  189. do {
  190. old_head = desc_avail;
  191. desc->next = old_head;
  192. mono_memory_write_barrier ();
  193. } while (mono_atomic_cas_ptr ((volatile gpointer *)&desc_avail, desc, old_head) != old_head);
  194. }
  195. static void
  196. desc_retire (Descriptor *desc)
  197. {
  198. g_assert (desc->anchor.data.state == STATE_EMPTY);
  199. g_assert (desc->in_use);
  200. desc->in_use = FALSE;
  201. free_sb (desc->sb, desc->block_size, desc->heap->account_type);
  202. mono_thread_hazardous_try_free (desc, desc_enqueue_avail);
  203. }
  204. #else
  205. MonoLockFreeQueue available_descs;
  206. static Descriptor*
  207. desc_alloc (MonoMemAccountType type)
  208. {
  209. Descriptor *desc = (Descriptor*)mono_lock_free_queue_dequeue (&available_descs);
  210. if (desc)
  211. return desc;
  212. return g_calloc (1, sizeof (Descriptor));
  213. }
  214. static void
  215. desc_retire (Descriptor *desc)
  216. {
  217. free_sb (desc->sb, desc->block_size, desc->heap->account_type);
  218. mono_lock_free_queue_enqueue (&available_descs, &desc->node);
  219. }
  220. #endif
  221. static Descriptor*
  222. list_get_partial (MonoLockFreeAllocSizeClass *sc)
  223. {
  224. for (;;) {
  225. Descriptor *desc = (Descriptor*) mono_lock_free_queue_dequeue (&sc->partial);
  226. if (!desc)
  227. return NULL;
  228. if (desc->anchor.data.state != STATE_EMPTY)
  229. return desc;
  230. desc_retire (desc);
  231. }
  232. }
  233. static void
  234. desc_put_partial (gpointer _desc)
  235. {
  236. Descriptor *desc = (Descriptor *) _desc;
  237. g_assert (desc->anchor.data.state != STATE_FULL);
  238. mono_lock_free_queue_node_unpoison (&desc->node);
  239. mono_lock_free_queue_enqueue (&desc->heap->sc->partial, &desc->node);
  240. }
  241. static void
  242. list_put_partial (Descriptor *desc)
  243. {
  244. g_assert (desc->anchor.data.state != STATE_FULL);
  245. mono_thread_hazardous_try_free (desc, desc_put_partial);
  246. }
  247. static void
  248. list_remove_empty_desc (MonoLockFreeAllocSizeClass *sc)
  249. {
  250. int num_non_empty = 0;
  251. for (;;) {
  252. Descriptor *desc = (Descriptor*) mono_lock_free_queue_dequeue (&sc->partial);
  253. if (!desc)
  254. return;
  255. /*
  256. * We don't need to read atomically because we're the
  257. * only thread that references this descriptor.
  258. */
  259. if (desc->anchor.data.state == STATE_EMPTY) {
  260. desc_retire (desc);
  261. } else {
  262. g_assert (desc->heap->sc == sc);
  263. mono_thread_hazardous_try_free (desc, desc_put_partial);
  264. if (++num_non_empty >= 2)
  265. return;
  266. }
  267. }
  268. }
  269. static Descriptor*
  270. heap_get_partial (MonoLockFreeAllocator *heap)
  271. {
  272. return list_get_partial (heap->sc);
  273. }
  274. static void
  275. heap_put_partial (Descriptor *desc)
  276. {
  277. list_put_partial (desc);
  278. }
  279. static gboolean
  280. set_anchor (Descriptor *desc, Anchor old_anchor, Anchor new_anchor)
  281. {
  282. if (old_anchor.data.state == STATE_EMPTY)
  283. g_assert (new_anchor.data.state == STATE_EMPTY);
  284. return mono_atomic_cas_i32 (&desc->anchor.value, new_anchor.value, old_anchor.value) == old_anchor.value;
  285. }
  286. static gpointer
  287. alloc_from_active_or_partial (MonoLockFreeAllocator *heap)
  288. {
  289. Descriptor *desc;
  290. Anchor old_anchor, new_anchor;
  291. gpointer addr;
  292. retry:
  293. desc = heap->active;
  294. if (desc) {
  295. if (mono_atomic_cas_ptr ((volatile gpointer *)&heap->active, NULL, desc) != desc)
  296. goto retry;
  297. } else {
  298. desc = heap_get_partial (heap);
  299. if (!desc)
  300. return NULL;
  301. }
  302. /* Now we own the desc. */
  303. do {
  304. unsigned int next;
  305. new_anchor.value = old_anchor.value = ((volatile Anchor*)&desc->anchor)->value;
  306. if (old_anchor.data.state == STATE_EMPTY) {
  307. /* We must free it because we own it. */
  308. desc_retire (desc);
  309. goto retry;
  310. }
  311. g_assert (old_anchor.data.state == STATE_PARTIAL);
  312. g_assert (old_anchor.data.count > 0);
  313. addr = (char*)desc->sb + old_anchor.data.avail * desc->slot_size;
  314. mono_memory_read_barrier ();
  315. next = *(unsigned int*)addr;
  316. g_assert (next < LOCK_FREE_ALLOC_SB_USABLE_SIZE (desc->block_size) / desc->slot_size);
  317. new_anchor.data.avail = next;
  318. --new_anchor.data.count;
  319. if (new_anchor.data.count == 0)
  320. new_anchor.data.state = STATE_FULL;
  321. } while (!set_anchor (desc, old_anchor, new_anchor));
  322. /* If the desc is partial we have to give it back. */
  323. if (new_anchor.data.state == STATE_PARTIAL) {
  324. if (mono_atomic_cas_ptr ((volatile gpointer *)&heap->active, desc, NULL) != NULL)
  325. heap_put_partial (desc);
  326. }
  327. return addr;
  328. }
  329. static gpointer
  330. alloc_from_new_sb (MonoLockFreeAllocator *heap)
  331. {
  332. unsigned int slot_size, block_size, count, i;
  333. Descriptor *desc = desc_alloc (heap->account_type);
  334. slot_size = desc->slot_size = heap->sc->slot_size;
  335. block_size = desc->block_size = heap->sc->block_size;
  336. count = LOCK_FREE_ALLOC_SB_USABLE_SIZE (block_size) / slot_size;
  337. desc->heap = heap;
  338. /*
  339. * Setting avail to 1 because 0 is the block we're allocating
  340. * right away.
  341. */
  342. desc->anchor.data.avail = 1;
  343. desc->slot_size = heap->sc->slot_size;
  344. desc->max_count = count;
  345. desc->anchor.data.count = desc->max_count - 1;
  346. desc->anchor.data.state = STATE_PARTIAL;
  347. desc->sb = alloc_sb (desc);
  348. /* Organize blocks into linked list. */
  349. for (i = 1; i < count - 1; ++i)
  350. *(unsigned int*)((char*)desc->sb + i * slot_size) = i + 1;
  351. *(unsigned int*)((char*)desc->sb + (count - 1) * slot_size) = 0;
  352. mono_memory_write_barrier ();
  353. /* Make it active or free it again. */
  354. if (mono_atomic_cas_ptr ((volatile gpointer *)&heap->active, desc, NULL) == NULL) {
  355. return desc->sb;
  356. } else {
  357. desc->anchor.data.state = STATE_EMPTY;
  358. desc_retire (desc);
  359. return NULL;
  360. }
  361. }
  362. gpointer
  363. mono_lock_free_alloc (MonoLockFreeAllocator *heap)
  364. {
  365. gpointer addr;
  366. for (;;) {
  367. addr = alloc_from_active_or_partial (heap);
  368. if (addr)
  369. break;
  370. addr = alloc_from_new_sb (heap);
  371. if (addr)
  372. break;
  373. }
  374. return addr;
  375. }
  376. void
  377. mono_lock_free_free (gpointer ptr, size_t block_size)
  378. {
  379. Anchor old_anchor, new_anchor;
  380. Descriptor *desc;
  381. gpointer sb;
  382. MonoLockFreeAllocator *heap = NULL;
  383. desc = *(Descriptor**) sb_header_for_addr (ptr, block_size);
  384. g_assert (block_size == desc->block_size);
  385. sb = desc->sb;
  386. do {
  387. new_anchor.value = old_anchor.value = ((volatile Anchor*)&desc->anchor)->value;
  388. *(unsigned int*)ptr = old_anchor.data.avail;
  389. new_anchor.data.avail = ((char*)ptr - (char*)sb) / desc->slot_size;
  390. g_assert (new_anchor.data.avail < LOCK_FREE_ALLOC_SB_USABLE_SIZE (block_size) / desc->slot_size);
  391. if (old_anchor.data.state == STATE_FULL)
  392. new_anchor.data.state = STATE_PARTIAL;
  393. if (++new_anchor.data.count == desc->max_count) {
  394. heap = desc->heap;
  395. new_anchor.data.state = STATE_EMPTY;
  396. }
  397. } while (!set_anchor (desc, old_anchor, new_anchor));
  398. if (new_anchor.data.state == STATE_EMPTY) {
  399. g_assert (old_anchor.data.state != STATE_EMPTY);
  400. if (mono_atomic_cas_ptr ((volatile gpointer *)&heap->active, NULL, desc) == desc) {
  401. /*
  402. * We own desc, check if it's still empty, in which case we retire it.
  403. * If it's partial we need to put it back either on the active slot or
  404. * on the partial list.
  405. */
  406. if (desc->anchor.data.state == STATE_EMPTY) {
  407. desc_retire (desc);
  408. } else if (desc->anchor.data.state == STATE_PARTIAL) {
  409. if (mono_atomic_cas_ptr ((volatile gpointer *)&heap->active, desc, NULL) != NULL)
  410. heap_put_partial (desc);
  411. }
  412. } else {
  413. /*
  414. * Somebody else must free it, so we do some
  415. * freeing for others.
  416. */
  417. list_remove_empty_desc (heap->sc);
  418. }
  419. } else if (old_anchor.data.state == STATE_FULL) {
  420. /*
  421. * Nobody owned it, now we do, so we need to give it
  422. * back.
  423. */
  424. g_assert (new_anchor.data.state == STATE_PARTIAL);
  425. if (mono_atomic_cas_ptr ((volatile gpointer *)&desc->heap->active, desc, NULL) != NULL)
  426. heap_put_partial (desc);
  427. }
  428. }
  429. #define g_assert_OR_PRINT(c, format, ...) do { \
  430. if (!(c)) { \
  431. if (print) \
  432. g_print ((format), ## __VA_ARGS__); \
  433. else \
  434. g_assert (FALSE); \
  435. } \
  436. } while (0)
  437. static void
  438. descriptor_check_consistency (Descriptor *desc, gboolean print)
  439. {
  440. int count = desc->anchor.data.count;
  441. int max_count = LOCK_FREE_ALLOC_SB_USABLE_SIZE (desc->block_size) / desc->slot_size;
  442. gboolean* linked = g_newa (gboolean, max_count);
  443. int i, last;
  444. unsigned int index;
  445. #ifndef DESC_AVAIL_DUMMY
  446. Descriptor *avail;
  447. for (avail = desc_avail; avail; avail = avail->next)
  448. g_assert_OR_PRINT (desc != avail, "descriptor is in the available list\n");
  449. #endif
  450. g_assert_OR_PRINT (desc->slot_size == desc->heap->sc->slot_size, "slot size doesn't match size class\n");
  451. if (print)
  452. g_print ("descriptor %p is ", desc);
  453. switch (desc->anchor.data.state) {
  454. case STATE_FULL:
  455. if (print)
  456. g_print ("full\n");
  457. g_assert_OR_PRINT (count == 0, "count is not zero: %d\n", count);
  458. break;
  459. case STATE_PARTIAL:
  460. if (print)
  461. g_print ("partial\n");
  462. g_assert_OR_PRINT (count < max_count, "count too high: is %d but must be below %d\n", count, max_count);
  463. break;
  464. case STATE_EMPTY:
  465. if (print)
  466. g_print ("empty\n");
  467. g_assert_OR_PRINT (count == max_count, "count is wrong: is %d but should be %d\n", count, max_count);
  468. break;
  469. default:
  470. g_assert_OR_PRINT (FALSE, "invalid state\n");
  471. }
  472. for (i = 0; i < max_count; ++i)
  473. linked [i] = FALSE;
  474. index = desc->anchor.data.avail;
  475. last = -1;
  476. for (i = 0; i < count; ++i) {
  477. gpointer addr = (char*)desc->sb + index * desc->slot_size;
  478. g_assert_OR_PRINT (index >= 0 && index < max_count,
  479. "index %d for %dth available slot, linked from %d, not in range [0 .. %d)\n",
  480. index, i, last, max_count);
  481. g_assert_OR_PRINT (!linked [index], "%dth available slot %d linked twice\n", i, index);
  482. if (linked [index])
  483. break;
  484. linked [index] = TRUE;
  485. last = index;
  486. index = *(unsigned int*)addr;
  487. }
  488. }
  489. gboolean
  490. mono_lock_free_allocator_check_consistency (MonoLockFreeAllocator *heap)
  491. {
  492. Descriptor *active = heap->active;
  493. Descriptor *desc;
  494. if (active) {
  495. g_assert (active->anchor.data.state == STATE_PARTIAL);
  496. descriptor_check_consistency (active, FALSE);
  497. }
  498. while ((desc = (Descriptor*)mono_lock_free_queue_dequeue (&heap->sc->partial))) {
  499. g_assert (desc->anchor.data.state == STATE_PARTIAL || desc->anchor.data.state == STATE_EMPTY);
  500. descriptor_check_consistency (desc, FALSE);
  501. }
  502. return TRUE;
  503. }
  504. void
  505. mono_lock_free_allocator_init_size_class (MonoLockFreeAllocSizeClass *sc, unsigned int slot_size, unsigned int block_size)
  506. {
  507. g_assert (block_size > 0);
  508. g_assert ((block_size & (block_size - 1)) == 0); /* check if power of 2 */
  509. g_assert (slot_size * 2 <= LOCK_FREE_ALLOC_SB_USABLE_SIZE (block_size));
  510. mono_lock_free_queue_init (&sc->partial);
  511. sc->slot_size = slot_size;
  512. sc->block_size = block_size;
  513. }
  514. void
  515. mono_lock_free_allocator_init_allocator (MonoLockFreeAllocator *heap, MonoLockFreeAllocSizeClass *sc, MonoMemAccountType account_type)
  516. {
  517. heap->sc = sc;
  518. heap->active = NULL;
  519. heap->account_type = account_type;
  520. }