Skip to content

Commit f0a0881

Browse files
committed
[WIP!] critnib: add refcount WIP!
Signed-off-by: Lukasz Dorau <[email protected]>
1 parent 436a201 commit f0a0881

File tree

6 files changed

+465
-98
lines changed

6 files changed

+465
-98
lines changed

src/critnib/critnib.c

Lines changed: 151 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ struct critnib_node {
116116
struct critnib_leaf {
117117
word key;
118118
void *value;
119+
void *to_be_freed;
120+
uint64_t ref_count;
119121
};
120122

121123
struct critnib {
@@ -194,7 +196,12 @@ struct critnib *critnib_new(free_leaf_t cb_free_leaf, void *leaf_allocator) {
194196
static void delete_node(struct critnib *c, struct critnib_node *__restrict n) {
195197
if (is_leaf(n)) {
196198
if (c->cb_free_leaf && to_leaf(n)) {
197-
c->cb_free_leaf(c->leaf_allocator, (void *)to_leaf(n)->value);
199+
if (to_leaf(n)->value) {
200+
c->cb_free_leaf(c->leaf_allocator, (void *)to_leaf(n)->value);
201+
} else if (to_leaf(n)->to_be_freed) {
202+
c->cb_free_leaf(c->leaf_allocator,
203+
(void *)to_leaf(n)->to_be_freed);
204+
}
198205
}
199206
umf_ba_global_free(to_leaf(n));
200207
} else {
@@ -233,8 +240,13 @@ void critnib_delete(struct critnib *c) {
233240
for (int i = 0; i < DELETED_LIFE; i++) {
234241
umf_ba_global_free(c->pending_del_nodes[i]);
235242
if (c->cb_free_leaf && c->pending_del_leaves[i]) {
236-
c->cb_free_leaf(c->leaf_allocator,
237-
(void *)c->pending_del_leaves[i]->value);
243+
if (c->pending_del_leaves[i]->value) {
244+
c->cb_free_leaf(c->leaf_allocator,
245+
(void *)c->pending_del_leaves[i]->value);
246+
} else if (c->pending_del_leaves[i]->to_be_freed) {
247+
c->cb_free_leaf(c->leaf_allocator,
248+
(void *)c->pending_del_leaves[i]->to_be_freed);
249+
}
238250
}
239251
umf_ba_global_free(c->pending_del_leaves[i]);
240252
}
@@ -288,8 +300,17 @@ static void free_leaf(struct critnib *__restrict c,
288300
return;
289301
}
290302

291-
if (c->cb_free_leaf && k && k->value) {
292-
c->cb_free_leaf(c->leaf_allocator, (void *)k->value);
303+
if (c->cb_free_leaf && k) {
304+
void *to_be_freed = NULL;
305+
utils_atomic_load_acquire_ptr(&k->to_be_freed, &to_be_freed);
306+
if (to_be_freed) {
307+
if (k->ref_count) {
308+
LOG_DEBUG("FREEING: ref_count = %llu, ptr = %p, value = %p",
309+
(unsigned long long)k->ref_count, (void *)k->key,
310+
to_be_freed);
311+
}
312+
c->cb_free_leaf(c->leaf_allocator, to_be_freed);
313+
}
293314
}
294315

295316
utils_atomic_store_release_ptr((void **)&k->value, c->deleted_leaf);
@@ -334,8 +355,11 @@ int critnib_insert(struct critnib *c, word key, void *value, int update) {
334355

335356
utils_annotate_memory_no_check(k, sizeof(struct critnib_leaf));
336357

358+
utils_atomic_store_release_ptr(&k->to_be_freed, 0);
337359
utils_atomic_store_release_ptr((void **)&k->key, (void *)key);
338360
utils_atomic_store_release_ptr((void **)&k->value, value);
361+
// mark the leaf as valid (ref_count == 1)
362+
utils_atomic_store_release_u64(&k->ref_count, 1ULL);
339363

340364
struct critnib_node *kn = (void *)((word)k | 1);
341365

@@ -370,9 +394,9 @@ int critnib_insert(struct critnib *c, word key, void *value, int update) {
370394
word at = path ^ key;
371395
if (!at) {
372396
ASSERT(is_leaf(n));
373-
if (to_leaf(kn)->value == value) {
397+
if (to_leaf(kn)->to_be_freed == value) {
374398
// do not free the value
375-
to_leaf(kn)->value = NULL;
399+
to_leaf(kn)->to_be_freed = NULL;
376400
}
377401
free_leaf(c, to_leaf(kn));
378402

@@ -418,19 +442,35 @@ int critnib_insert(struct critnib *c, word key, void *value, int update) {
418442
/*
419443
* critnib_remove -- delete a key from the critnib structure, return its value
420444
*/
421-
void *critnib_remove(struct critnib *c, word key) {
445+
void *critnib_remove(struct critnib *c, word key, void **ref) {
422446
struct critnib_leaf *k;
423447
void *value = NULL;
424448

449+
if (!ref) {
450+
return NULL;
451+
}
452+
425453
utils_mutex_lock(&c->mutex);
426454

427455
struct critnib_node *n = c->root;
428456
if (!n) {
429457
goto not_found;
430458
}
431459

432-
word del =
433-
(utils_atomic_increment_u64(&c->remove_count) - 1) % DELETED_LIFE;
460+
word del;
461+
int i_del = 0;
462+
uint64_t ref_count = 0;
463+
do {
464+
del = (utils_atomic_increment_u64(&c->remove_count) - 1) % DELETED_LIFE;
465+
k = c->pending_del_leaves[del];
466+
if (i_del++ == DELETED_LIFE) {
467+
break;
468+
}
469+
if (k) {
470+
utils_atomic_load_acquire_u64(&k->ref_count, &ref_count);
471+
}
472+
} while (k && (ref_count > 0));
473+
434474
free_node(c, c->pending_del_nodes[del]);
435475
free_leaf(c, c->pending_del_leaves[del]);
436476
c->pending_del_nodes[del] = NULL;
@@ -491,13 +531,53 @@ void *critnib_remove(struct critnib *c, word key) {
491531

492532
del_leaf:
493533
value = k->value;
534+
*ref = k;
535+
utils_atomic_store_release_ptr(&k->to_be_freed, value);
536+
utils_atomic_store_release_ptr((void **)&k->value, NULL);
494537
c->pending_del_leaves[del] = k;
495538

496539
not_found:
497540
utils_mutex_unlock(&c->mutex);
498541
return value;
499542
}
500543

544+
/*
545+
* critnib_release -- release a reference to a key
546+
*/
547+
int critnib_release(struct critnib *c, void *ref) {
548+
if (!c || !ref) {
549+
return -1;
550+
}
551+
552+
struct critnib_leaf *k = (struct critnib_leaf *)ref;
553+
554+
uint64_t ref_count;
555+
utils_atomic_load_acquire_u64(&k->ref_count, &ref_count);
556+
557+
// check if the reference count is overflowed
558+
assert((ref_count & (1ULL << 63)) == 0);
559+
560+
/* decrement the reference count */
561+
if (ref_count && (utils_atomic_decrement_u64(&k->ref_count) == 0)) {
562+
void *to_be_freed = NULL;
563+
utils_atomic_load_acquire_ptr(&k->to_be_freed, &to_be_freed);
564+
if (to_be_freed) {
565+
utils_atomic_store_release_ptr(&k->to_be_freed, NULL);
566+
if (to_be_freed && c->cb_free_leaf) {
567+
c->cb_free_leaf(c->leaf_allocator, to_be_freed);
568+
}
569+
}
570+
}
571+
572+
#ifndef NDEBUG
573+
// check if the reference count is overflowed
574+
utils_atomic_load_acquire_u64(&k->ref_count, &ref_count);
575+
assert((ref_count & (1ULL << 63)) == 0);
576+
#endif
577+
578+
return 0;
579+
}
580+
501581
/*
502582
* critnib_get -- query for a key ("==" match), returns value or NULL
503583
*
@@ -508,13 +588,17 @@ void *critnib_remove(struct critnib *c, word key) {
508588
* Counterintuitively, it's pointless to return the most current answer,
509589
* we need only one that was valid at any point after the call started.
510590
*/
511-
void *critnib_get(struct critnib *c, word key) {
591+
void *critnib_get(struct critnib *c, word key, void **ref) {
592+
struct critnib_leaf *k;
593+
struct critnib_node *n;
512594
uint64_t wrs1, wrs2;
513-
void *res;
595+
void *res = NULL;
514596

515-
do {
516-
struct critnib_node *n;
597+
if (!ref) {
598+
return NULL;
599+
}
517600

601+
do {
518602
utils_atomic_load_acquire_u64(&c->remove_count, &wrs1);
519603
utils_atomic_load_acquire_ptr((void **)&c->root, (void **)&n);
520604

@@ -529,11 +613,25 @@ void *critnib_get(struct critnib *c, word key) {
529613
}
530614

531615
/* ... as we check it at the end. */
532-
struct critnib_leaf *k = to_leaf(n);
616+
k = to_leaf(n);
533617
res = (n && k->key == key) ? k->value : NULL;
534618
utils_atomic_load_acquire_u64(&c->remove_count, &wrs2);
535619
} while (wrs1 + DELETED_LIFE <= wrs2);
536620

621+
if (res) {
622+
uint64_t ref_count;
623+
utils_atomic_load_acquire_u64(&k->ref_count, &ref_count);
624+
if (ref_count == 0) {
625+
return NULL;
626+
}
627+
if (utils_atomic_increment_u64(&k->ref_count) == 1) {
628+
utils_atomic_decrement_u64(&k->ref_count);
629+
return NULL;
630+
}
631+
632+
*ref = k;
633+
}
634+
537635
return res;
538636
}
539637

@@ -645,19 +743,38 @@ static struct critnib_leaf *find_le(struct critnib_node *__restrict n,
645743
*
646744
* Same guarantees as critnib_get().
647745
*/
648-
void *critnib_find_le(struct critnib *c, word key) {
746+
void *critnib_find_le(struct critnib *c, word key, void **ref) {
747+
struct critnib_leaf *k;
649748
uint64_t wrs1, wrs2;
650749
void *res;
651750

751+
if (!ref) {
752+
return NULL;
753+
}
754+
652755
do {
653756
utils_atomic_load_acquire_u64(&c->remove_count, &wrs1);
654757
struct critnib_node *n; /* avoid a subtle TOCTOU */
655758
utils_atomic_load_acquire_ptr((void **)&c->root, (void **)&n);
656-
struct critnib_leaf *k = n ? find_le(n, key) : NULL;
759+
k = n ? find_le(n, key) : NULL;
657760
res = k ? k->value : NULL;
658761
utils_atomic_load_acquire_u64(&c->remove_count, &wrs2);
659762
} while (wrs1 + DELETED_LIFE <= wrs2);
660763

764+
if (res) {
765+
uint64_t ref_count;
766+
utils_atomic_load_acquire_u64(&k->ref_count, &ref_count);
767+
if (ref_count == 0) {
768+
return NULL;
769+
}
770+
if (utils_atomic_increment_u64(&k->ref_count) == 1) {
771+
utils_atomic_decrement_u64(&k->ref_count);
772+
return NULL;
773+
}
774+
775+
*ref = k;
776+
}
777+
661778
return res;
662779
}
663780

@@ -743,12 +860,16 @@ static struct critnib_leaf *find_ge(struct critnib_node *__restrict n,
743860
* critnib_find -- parametrized query, returns 1 if found
744861
*/
745862
int critnib_find(struct critnib *c, uintptr_t key, enum find_dir_t dir,
746-
uintptr_t *rkey, void **rvalue) {
863+
uintptr_t *rkey, void **rvalue, void **ref) {
747864
uint64_t wrs1, wrs2;
748865
struct critnib_leaf *k;
749866
uintptr_t _rkey = (uintptr_t)0x0;
750867
void **_rvalue = NULL;
751868

869+
if (!ref) {
870+
return 0;
871+
}
872+
752873
/* <42 ≡ ≤41 */
753874
if (dir < -1) {
754875
if (!key) {
@@ -790,6 +911,18 @@ int critnib_find(struct critnib *c, uintptr_t key, enum find_dir_t dir,
790911
} while (wrs1 + DELETED_LIFE <= wrs2);
791912

792913
if (k) {
914+
uint64_t ref_count;
915+
utils_atomic_load_acquire_u64(&k->ref_count, &ref_count);
916+
if (ref_count == 0) {
917+
return 0;
918+
}
919+
if (utils_atomic_increment_u64(&k->ref_count) == 1) {
920+
utils_atomic_decrement_u64(&k->ref_count);
921+
return 0;
922+
}
923+
924+
*ref = k;
925+
793926
if (rkey) {
794927
*rkey = _rkey;
795928
}

src/critnib/critnib.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,15 @@ critnib *critnib_new(free_leaf_t cb_free_leaf, void *leaf_allocator);
3232
void critnib_delete(critnib *c);
3333

3434
int critnib_insert(critnib *c, uintptr_t key, void *value, int update);
35-
void *critnib_remove(critnib *c, uintptr_t key);
36-
void *critnib_get(critnib *c, uintptr_t key);
37-
void *critnib_find_le(critnib *c, uintptr_t key);
35+
void *critnib_remove(critnib *c, uintptr_t key, void **ref);
36+
void *critnib_get(critnib *c, uintptr_t key, void **ref);
37+
void *critnib_find_le(critnib *c, uintptr_t key, void **ref);
3838
int critnib_find(critnib *c, uintptr_t key, enum find_dir_t dir,
39-
uintptr_t *rkey, void **rvalue);
39+
uintptr_t *rkey, void **rvalue, void **ref);
4040
void critnib_iter(critnib *c, uintptr_t min, uintptr_t max,
4141
int (*func)(uintptr_t key, void *value, void *privdata),
4242
void *privdata);
43+
int critnib_release(struct critnib *c, void *ref);
4344

4445
#ifdef __cplusplus
4546
}

src/pool/pool_disjoint.c

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,11 @@ static umf_result_t pool_unregister_slab(disjoint_pool_t *pool, slab_t *slab) {
231231
// TODO ASSERT_IS_ALIGNED((uintptr_t)slab_addr, bucket->size);
232232
LOG_DEBUG("slab: %p, start: %p", (void *)slab, slab_addr);
233233

234-
critnib_remove(slabs, (uintptr_t)slab_addr);
234+
void *ref_slabs = NULL;
235+
critnib_remove(slabs, (uintptr_t)slab_addr, &ref_slabs);
236+
if (ref_slabs) {
237+
critnib_release(slabs, ref_slabs);
238+
}
235239

236240
return UMF_RESULT_SUCCESS;
237241
}
@@ -812,8 +816,9 @@ size_t disjoint_pool_malloc_usable_size(void *pool, const void *ptr) {
812816
}
813817

814818
// check if given pointer is allocated inside any Disjoint Pool slab
815-
slab_t *slab =
816-
(slab_t *)critnib_find_le(disjoint_pool->known_slabs, (uintptr_t)ptr);
819+
void *ref_slab = NULL;
820+
slab_t *slab = (slab_t *)critnib_find_le(disjoint_pool->known_slabs,
821+
(uintptr_t)ptr, &ref_slab);
817822
if (slab == NULL || ptr >= slab_get_end(slab)) {
818823
// memory comes directly from the provider
819824
umf_alloc_info_t allocInfo = {NULL, 0, NULL};
@@ -831,7 +836,12 @@ size_t disjoint_pool_malloc_usable_size(void *pool, const void *ptr) {
831836

832837
ptrdiff_t diff = (ptrdiff_t)ptr - (ptrdiff_t)unaligned_ptr;
833838

834-
return slab->bucket->size - diff;
839+
size_t size = slab->bucket->size - diff;
840+
841+
assert(ref_slab);
842+
critnib_release(disjoint_pool->known_slabs, ref_slab);
843+
844+
return size;
835845
}
836846

837847
umf_result_t disjoint_pool_free(void *pool, void *ptr) {
@@ -841,8 +851,9 @@ umf_result_t disjoint_pool_free(void *pool, void *ptr) {
841851
}
842852

843853
// check if given pointer is allocated inside any Disjoint Pool slab
844-
slab_t *slab =
845-
(slab_t *)critnib_find_le(disjoint_pool->known_slabs, (uintptr_t)ptr);
854+
void *ref_slab = NULL;
855+
slab_t *slab = (slab_t *)critnib_find_le(disjoint_pool->known_slabs,
856+
(uintptr_t)ptr, &ref_slab);
846857

847858
if (slab == NULL || ptr >= slab_get_end(slab)) {
848859

@@ -889,6 +900,9 @@ umf_result_t disjoint_pool_free(void *pool, void *ptr) {
889900
utils_annotate_memory_inaccessible(unaligned_ptr, bucket->size);
890901
bucket_free_chunk(bucket, unaligned_ptr, slab, &to_pool);
891902

903+
assert(ref_slab);
904+
critnib_release(disjoint_pool->known_slabs, ref_slab);
905+
892906
if (disjoint_pool->params.pool_trace > 1) {
893907
bucket->free_count++;
894908
}

0 commit comments

Comments
 (0)