+static inline block_t *pop_free_list (tl_t *tl, int scale) {
+#ifndef RECYCLE_PAGES
+ block_t **free_list = &tl->free_list[scale];
+#else //RECYCLE_PAGES
+ size_class_t *sc = &tl->size_class[scale];
+ if (EXPECT_FALSE(sc->active_page == NULL))
+ return NULL;
+ block_t **free_list = &sc->active_page->free_list;
+#endif//RECYCLE_PAGES
+ block_t *b = *free_list;
+ if (EXPECT_FALSE(b == NULL))
+ return NULL;
+ ASSERT(get_header(b)->scale == scale);
+ *free_list = b->next;
+ return b;
+}
+
+// Allocate a block of memory at least size <n>. Blocks are binned in powers-of-two. Round up <n> to
+// the nearest power of two.
+//
+// First check the current thread's free list for an available block. If there are no blocks on the
+// free list, pull items off of the current thread's incoming block queues and push them onto the
+// free list. If we didn't get an appropriate size block off of the block queues then allocate a new
+// page, break it up into blocks and push them onto the free list.
+void *nbd_malloc (size_t n) {
+ // the scale is the log base 2 of <n>, rounded up
+ int b_scale = (sizeof(void *) * __CHAR_BIT__) - __builtin_clzl((n) - 1);
+ TRACE("m1", "nbd_malloc: size %llu (scale %llu)", n, b_scale);
+
+ if (EXPECT_FALSE(b_scale < MIN_SCALE)) { b_scale = MIN_SCALE; }
+ if (EXPECT_FALSE(b_scale > MAX_SCALE)) { return NULL; }
+
+ tl_t *tl = &tl_[GET_THREAD_INDEX()]; // thread-local data
+
+ block_t *b = pop_free_list(tl, b_scale);
+ if (b != NULL) {
+ TRACE("m1", "nbd_malloc: returning block %p", b, 0);
+ return b;
+ assert(b);
+ }
+
+ // The free list is empty so process blocks freed from other threads and then check again.
+ process_incoming_blocks(tl);
+ b = pop_free_list(tl, b_scale);
+ if (b != NULL) {
+ TRACE("m1", "nbd_malloc: returning block %p", b, 0);
+ return b;
+ assert(b);
+ }
+
+#ifdef RECYCLE_PAGES
+ // The current active page is completely allocated. Make the oldest partially allocated page
+ // the new active page.
+ size_class_t *sc = &tl->size_class[b_scale];
+ if (sc->oldest_partial != NULL) {
+ sc->active_page = sc->oldest_partial;
+ sc->oldest_partial = sc->oldest_partial->next;
+ sc->oldest_partial->prev = NULL;
+ b = pop_free_list(tl, b_scale);
+ ASSERT(b != NULL);
+ TRACE("m1", "nbd_malloc: returning block %p", b, 0);
+ return b;
+ assert(b);