@@ -116,6 +116,8 @@ struct critnib_node {
116116struct critnib_leaf {
117117 word key ;
118118 void * value ;
119+ void * to_be_freed ;
120+ uint64_t ref_count ;
119121};
120122
121123struct critnib {
@@ -194,7 +196,12 @@ struct critnib *critnib_new(free_leaf_t cb_free_leaf, void *leaf_allocator) {
194196static 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,8 @@ 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 && k -> to_be_freed ) {
304+ c -> cb_free_leaf (c -> leaf_allocator , (void * )k -> to_be_freed );
293305 }
294306
295307 utils_atomic_store_release_ptr ((void * * )& k -> value , c -> deleted_leaf );
@@ -334,8 +346,11 @@ int critnib_insert(struct critnib *c, word key, void *value, int update) {
334346
335347 utils_annotate_memory_no_check (k , sizeof (struct critnib_leaf ));
336348
349+ utils_atomic_store_release_ptr (& k -> to_be_freed , 0 );
337350 utils_atomic_store_release_ptr ((void * * )& k -> key , (void * )key );
338351 utils_atomic_store_release_ptr ((void * * )& k -> value , value );
352+ // mark the leaf as valid (ref_count == 1)
353+ utils_atomic_store_release_u64 (& k -> ref_count , 1ULL );
339354
340355 struct critnib_node * kn = (void * )((word )k | 1 );
341356
@@ -370,9 +385,9 @@ int critnib_insert(struct critnib *c, word key, void *value, int update) {
370385 word at = path ^ key ;
371386 if (!at ) {
372387 ASSERT (is_leaf (n ));
373- if (to_leaf (kn )-> value == value ) {
388+ if (to_leaf (kn )-> to_be_freed == value ) {
374389 // do not free the value
375- to_leaf (kn )-> value = NULL ;
390+ to_leaf (kn )-> to_be_freed = NULL ;
376391 }
377392 free_leaf (c , to_leaf (kn ));
378393
@@ -418,19 +433,31 @@ int critnib_insert(struct critnib *c, word key, void *value, int update) {
418433/*
419434 * critnib_remove -- delete a key from the critnib structure, return its value
420435 */
421- void * critnib_remove (struct critnib * c , word key ) {
436+ void * critnib_remove (struct critnib * c , word key , void * * ref ) {
422437 struct critnib_leaf * k ;
423438 void * value = NULL ;
424439
440+ if (!ref ) {
441+ return NULL ;
442+ }
443+
425444 utils_mutex_lock (& c -> mutex );
426445
427446 struct critnib_node * n = c -> root ;
428447 if (!n ) {
429448 goto not_found ;
430449 }
431450
432- word del =
433- (utils_atomic_increment_u64 (& c -> remove_count ) - 1 ) % DELETED_LIFE ;
451+ word del ;
452+ int i_del = 0 ;
453+ do {
454+ del = (utils_atomic_increment_u64 (& c -> remove_count ) - 1 ) % DELETED_LIFE ;
455+ k = c -> pending_del_leaves [del ];
456+ if (i_del ++ == DELETED_LIFE ) {
457+ break ;
458+ }
459+ } while (k && (k -> ref_count > 0 ));
460+
434461 free_node (c , c -> pending_del_nodes [del ]);
435462 free_leaf (c , c -> pending_del_leaves [del ]);
436463 c -> pending_del_nodes [del ] = NULL ;
@@ -491,13 +518,40 @@ void *critnib_remove(struct critnib *c, word key) {
491518
492519del_leaf :
493520 value = k -> value ;
521+ * ref = k ;
522+ utils_atomic_store_release_ptr (& k -> to_be_freed , value );
523+ utils_atomic_store_release_ptr ((void * * )& k -> value , NULL );
494524 c -> pending_del_leaves [del ] = k ;
495525
496526not_found :
497527 utils_mutex_unlock (& c -> mutex );
498528 return value ;
499529}
500530
531+ /*
532+ * critnib_release -- release a reference to a key
533+ */
534+ int critnib_release (struct critnib * c , void * ref ) {
535+ if (!c || !ref ) {
536+ return -1 ;
537+ }
538+
539+ struct critnib_leaf * k = (struct critnib_leaf * )ref ;
540+
541+ /* decrement the reference count */
542+ if (k -> ref_count && (utils_atomic_decrement_u64 (& k -> ref_count ) == 0 ) &&
543+ k -> to_be_freed ) {
544+ void * value = k -> to_be_freed ;
545+ utils_atomic_store_release_ptr (& k -> to_be_freed , NULL );
546+ utils_atomic_store_release_u64 (& k -> key , 0 );
547+ if (value && c -> cb_free_leaf ) {
548+ c -> cb_free_leaf (c -> leaf_allocator , value );
549+ }
550+ }
551+
552+ return 0 ;
553+ }
554+
501555/*
502556 * critnib_get -- query for a key ("==" match), returns value or NULL
503557 *
@@ -508,13 +562,17 @@ void *critnib_remove(struct critnib *c, word key) {
508562 * Counterintuitively, it's pointless to return the most current answer,
509563 * we need only one that was valid at any point after the call started.
510564 */
511- void * critnib_get (struct critnib * c , word key ) {
565+ void * critnib_get (struct critnib * c , word key , void * * ref ) {
566+ struct critnib_leaf * k ;
567+ struct critnib_node * n ;
512568 uint64_t wrs1 , wrs2 ;
513- void * res ;
569+ void * res = NULL ;
514570
515- do {
516- struct critnib_node * n ;
571+ if (!ref ) {
572+ return NULL ;
573+ }
517574
575+ do {
518576 utils_atomic_load_acquire_u64 (& c -> remove_count , & wrs1 );
519577 utils_atomic_load_acquire_ptr ((void * * )& c -> root , (void * * )& n );
520578
@@ -529,11 +587,25 @@ void *critnib_get(struct critnib *c, word key) {
529587 }
530588
531589 /* ... as we check it at the end. */
532- struct critnib_leaf * k = to_leaf (n );
590+ k = to_leaf (n );
533591 res = (n && k -> key == key ) ? k -> value : NULL ;
534592 utils_atomic_load_acquire_u64 (& c -> remove_count , & wrs2 );
535593 } while (wrs1 + DELETED_LIFE <= wrs2 );
536594
595+ if (res ) {
596+ uint64_t ref_count ;
597+ utils_atomic_load_acquire_u64 (& k -> ref_count , & ref_count );
598+ if (ref_count == 0 ) {
599+ return NULL ;
600+ }
601+ if (utils_atomic_increment_u64 (& k -> ref_count ) == 1 ) {
602+ utils_atomic_decrement_u64 (& k -> ref_count );
603+ return NULL ;
604+ }
605+
606+ * ref = k ;
607+ }
608+
537609 return res ;
538610}
539611
@@ -645,19 +717,38 @@ static struct critnib_leaf *find_le(struct critnib_node *__restrict n,
645717 *
646718 * Same guarantees as critnib_get().
647719 */
648- void * critnib_find_le (struct critnib * c , word key ) {
720+ void * critnib_find_le (struct critnib * c , word key , void * * ref ) {
721+ struct critnib_leaf * k ;
649722 uint64_t wrs1 , wrs2 ;
650723 void * res ;
651724
725+ if (!ref ) {
726+ return NULL ;
727+ }
728+
652729 do {
653730 utils_atomic_load_acquire_u64 (& c -> remove_count , & wrs1 );
654731 struct critnib_node * n ; /* avoid a subtle TOCTOU */
655732 utils_atomic_load_acquire_ptr ((void * * )& c -> root , (void * * )& n );
656- struct critnib_leaf * k = n ? find_le (n , key ) : NULL ;
733+ k = n ? find_le (n , key ) : NULL ;
657734 res = k ? k -> value : NULL ;
658735 utils_atomic_load_acquire_u64 (& c -> remove_count , & wrs2 );
659736 } while (wrs1 + DELETED_LIFE <= wrs2 );
660737
738+ if (res ) {
739+ uint64_t ref_count ;
740+ utils_atomic_load_acquire_u64 (& k -> ref_count , & ref_count );
741+ if (ref_count == 0 ) {
742+ return NULL ;
743+ }
744+ if (utils_atomic_increment_u64 (& k -> ref_count ) == 1 ) {
745+ utils_atomic_decrement_u64 (& k -> ref_count );
746+ return NULL ;
747+ }
748+
749+ * ref = k ;
750+ }
751+
661752 return res ;
662753}
663754
@@ -743,12 +834,16 @@ static struct critnib_leaf *find_ge(struct critnib_node *__restrict n,
743834 * critnib_find -- parametrized query, returns 1 if found
744835 */
745836int critnib_find (struct critnib * c , uintptr_t key , enum find_dir_t dir ,
746- uintptr_t * rkey , void * * rvalue ) {
837+ uintptr_t * rkey , void * * rvalue , void * * ref ) {
747838 uint64_t wrs1 , wrs2 ;
748839 struct critnib_leaf * k ;
749840 uintptr_t _rkey = (uintptr_t )0x0 ;
750841 void * * _rvalue = NULL ;
751842
843+ if (!ref ) {
844+ return 0 ;
845+ }
846+
752847 /* <42 ≡ ≤41 */
753848 if (dir < -1 ) {
754849 if (!key ) {
@@ -790,6 +885,18 @@ int critnib_find(struct critnib *c, uintptr_t key, enum find_dir_t dir,
790885 } while (wrs1 + DELETED_LIFE <= wrs2 );
791886
792887 if (k ) {
888+ uint64_t ref_count ;
889+ utils_atomic_load_acquire_u64 (& k -> ref_count , & ref_count );
890+ if (ref_count == 0 ) {
891+ return 0 ;
892+ }
893+ if (utils_atomic_increment_u64 (& k -> ref_count ) == 1 ) {
894+ utils_atomic_decrement_u64 (& k -> ref_count );
895+ return 0 ;
896+ }
897+
898+ * ref = k ;
899+
793900 if (rkey ) {
794901 * rkey = _rkey ;
795902 }
0 commit comments