Skip to content

Commit aad0dca

Browse files
committed
critnib: add reference counter
Signed-off-by: Lukasz Dorau <[email protected]>
1 parent 78a16e1 commit aad0dca

File tree

6 files changed

+477
-109
lines changed

6 files changed

+477
-109
lines changed

src/critnib/critnib.c

Lines changed: 163 additions & 29 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,11 +300,7 @@ 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);
293-
}
294-
295-
utils_atomic_store_release_ptr((void **)&k->value, c->deleted_leaf);
303+
utils_atomic_store_release_ptr(&k->value, c->deleted_leaf);
296304
utils_atomic_store_release_ptr((void **)&c->deleted_leaf, k);
297305
}
298306

@@ -305,8 +313,18 @@ static struct critnib_leaf *alloc_leaf(struct critnib *__restrict c) {
305313
}
306314

307315
struct critnib_leaf *k = c->deleted_leaf;
308-
309316
c->deleted_leaf = k->value;
317+
318+
uint64_t ref_count = 0;
319+
void *to_be_freed = NULL;
320+
utils_atomic_load_acquire_u64(&k->ref_count, &ref_count);
321+
utils_atomic_load_acquire_ptr(&k->to_be_freed, &to_be_freed);
322+
if (ref_count && to_be_freed) {
323+
LOG_DEBUG("WARNING: reusing probably non-freed leaf: key = %p, value = "
324+
"%p, ref_count = %llu",
325+
(void *)k->key, to_be_freed, (unsigned long long)ref_count);
326+
}
327+
310328
utils_annotate_memory_new(k, sizeof(*k));
311329

312330
return k;
@@ -334,8 +352,12 @@ int critnib_insert(struct critnib *c, word key, void *value, int update) {
334352

335353
utils_annotate_memory_no_check(k, sizeof(struct critnib_leaf));
336354

355+
utils_atomic_store_release_ptr(&k->to_be_freed, 0);
337356
utils_atomic_store_release_ptr((void **)&k->key, (void *)key);
338-
utils_atomic_store_release_ptr((void **)&k->value, value);
357+
utils_atomic_store_release_ptr(&k->value, value);
358+
359+
// mark the leaf as valid (ref_count == 1)
360+
utils_atomic_store_release_u64(&k->ref_count, 1ULL);
339361

340362
struct critnib_node *kn = (void *)((word)k | 1);
341363

@@ -370,10 +392,6 @@ int critnib_insert(struct critnib *c, word key, void *value, int update) {
370392
word at = path ^ key;
371393
if (!at) {
372394
ASSERT(is_leaf(n));
373-
if (to_leaf(kn)->value == value) {
374-
// do not free the value
375-
to_leaf(kn)->value = NULL;
376-
}
377395
free_leaf(c, to_leaf(kn));
378396

379397
if (update) {
@@ -392,11 +410,10 @@ int critnib_insert(struct critnib *c, word key, void *value, int update) {
392410
struct critnib_node *m = alloc_node(c);
393411
if (!m) {
394412
free_leaf(c, to_leaf(kn));
395-
396413
utils_mutex_unlock(&c->mutex);
397-
398414
return ENOMEM;
399415
}
416+
400417
utils_annotate_memory_no_check(m, sizeof(struct critnib_node));
401418

402419
for (int i = 0; i < SLNODES; i++) {
@@ -418,19 +435,36 @@ int critnib_insert(struct critnib *c, word key, void *value, int update) {
418435
/*
419436
* critnib_remove -- delete a key from the critnib structure, return its value
420437
*/
421-
void *critnib_remove(struct critnib *c, word key) {
438+
void *critnib_remove(struct critnib *c, word key, void **ref) {
422439
struct critnib_leaf *k;
423440
void *value = NULL;
424441

442+
if (!ref) {
443+
return NULL;
444+
}
445+
425446
utils_mutex_lock(&c->mutex);
426447

427448
struct critnib_node *n = c->root;
428449
if (!n) {
429450
goto not_found;
430451
}
431452

432-
word del =
433-
(utils_atomic_increment_u64(&c->remove_count) - 1) % DELETED_LIFE;
453+
word del;
454+
int i_del = 0;
455+
uint64_t ref_count = 0;
456+
do {
457+
del = (utils_atomic_increment_u64(&c->remove_count) - 1) % DELETED_LIFE;
458+
if (++i_del == (DELETED_LIFE + 1)) {
459+
break;
460+
}
461+
462+
k = c->pending_del_leaves[del];
463+
if (k) {
464+
utils_atomic_load_acquire_u64(&k->ref_count, &ref_count);
465+
}
466+
} while (k && (ref_count > 0));
467+
434468
free_node(c, c->pending_del_nodes[del]);
435469
free_leaf(c, c->pending_del_leaves[del]);
436470
c->pending_del_nodes[del] = NULL;
@@ -491,13 +525,55 @@ void *critnib_remove(struct critnib *c, word key) {
491525

492526
del_leaf:
493527
value = k->value;
528+
utils_atomic_store_release_ptr(&k->to_be_freed, value);
529+
utils_atomic_store_release_ptr(&k->value, NULL);
494530
c->pending_del_leaves[del] = k;
531+
*ref = k;
495532

496533
not_found:
497534
utils_mutex_unlock(&c->mutex);
498535
return value;
499536
}
500537

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

515-
do {
516-
struct critnib_node *n;
593+
if (!ref) {
594+
return NULL;
595+
}
517596

597+
do {
518598
utils_atomic_load_acquire_u64(&c->remove_count, &wrs1);
519599
utils_atomic_load_acquire_ptr((void **)&c->root, (void **)&n);
520600

@@ -524,16 +604,31 @@ void *critnib_get(struct critnib *c, word key) {
524604
* going wrong way if our path is missing, but that's ok...
525605
*/
526606
while (n && !is_leaf(n)) {
607+
sh_t sh = n->shift;
527608
utils_atomic_load_acquire_ptr(
528-
(void **)&n->child[slice_index(key, n->shift)], (void **)&n);
609+
(void **)&n->child[slice_index(key, sh)], (void **)&n);
529610
}
530611

531612
/* ... as we check it at the end. */
532-
struct critnib_leaf *k = to_leaf(n);
613+
k = to_leaf(n);
533614
res = (n && k->key == key) ? k->value : NULL;
534615
utils_atomic_load_acquire_u64(&c->remove_count, &wrs2);
535616
} while (wrs1 + DELETED_LIFE <= wrs2);
536617

618+
if (res) {
619+
uint64_t ref_count;
620+
utils_atomic_load_acquire_u64(&k->ref_count, &ref_count);
621+
if (ref_count == 0) {
622+
return NULL;
623+
}
624+
if (utils_atomic_increment_u64(&k->ref_count) == 1) {
625+
utils_atomic_decrement_u64(&k->ref_count);
626+
return NULL;
627+
}
628+
629+
*ref = k;
630+
}
631+
537632
return res;
538633
}
539634

@@ -645,19 +740,42 @@ static struct critnib_leaf *find_le(struct critnib_node *__restrict n,
645740
*
646741
* Same guarantees as critnib_get().
647742
*/
648-
void *critnib_find_le(struct critnib *c, word key) {
743+
void *critnib_find_le(struct critnib *c, word key, void **ref) {
744+
struct critnib_leaf *k;
649745
uint64_t wrs1, wrs2;
650746
void *res;
651747

748+
if (!ref) {
749+
return NULL;
750+
}
751+
652752
do {
653753
utils_atomic_load_acquire_u64(&c->remove_count, &wrs1);
654754
struct critnib_node *n; /* avoid a subtle TOCTOU */
655755
utils_atomic_load_acquire_ptr((void **)&c->root, (void **)&n);
656-
struct critnib_leaf *k = n ? find_le(n, key) : NULL;
657-
res = k ? k->value : NULL;
756+
k = n ? find_le(n, key) : NULL;
757+
if (k) {
758+
utils_atomic_load_acquire_ptr(&k->value, &res);
759+
} else {
760+
res = NULL;
761+
}
658762
utils_atomic_load_acquire_u64(&c->remove_count, &wrs2);
659763
} while (wrs1 + DELETED_LIFE <= wrs2);
660764

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

@@ -743,12 +861,16 @@ static struct critnib_leaf *find_ge(struct critnib_node *__restrict n,
743861
* critnib_find -- parametrized query, returns 1 if found
744862
*/
745863
int critnib_find(struct critnib *c, uintptr_t key, enum find_dir_t dir,
746-
uintptr_t *rkey, void **rvalue) {
864+
uintptr_t *rkey, void **rvalue, void **ref) {
747865
uint64_t wrs1, wrs2;
748866
struct critnib_leaf *k;
749867
uintptr_t _rkey = (uintptr_t)0x0;
750868
void **_rvalue = NULL;
751869

870+
if (!ref) {
871+
return 0;
872+
}
873+
752874
/* <42 ≡ ≤41 */
753875
if (dir < -1) {
754876
if (!key) {
@@ -790,6 +912,18 @@ int critnib_find(struct critnib *c, uintptr_t key, enum find_dir_t dir,
790912
} while (wrs1 + DELETED_LIFE <= wrs2);
791913

792914
if (k) {
915+
uint64_t ref_count;
916+
utils_atomic_load_acquire_u64(&k->ref_count, &ref_count);
917+
if (ref_count == 0) {
918+
return 0;
919+
}
920+
if (utils_atomic_increment_u64(&k->ref_count) == 1) {
921+
utils_atomic_decrement_u64(&k->ref_count);
922+
return 0;
923+
}
924+
925+
*ref = k;
926+
793927
if (rkey) {
794928
*rkey = _rkey;
795929
}

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
}

0 commit comments

Comments
 (0)