@@ -116,6 +116,8 @@ struct critnib_node {
116
116
struct critnib_leaf {
117
117
word key ;
118
118
void * value ;
119
+ void * to_be_freed ;
120
+ uint64_t ref_count ;
119
121
};
120
122
121
123
struct critnib {
@@ -194,7 +196,12 @@ struct critnib *critnib_new(free_leaf_t cb_free_leaf, void *leaf_allocator) {
194
196
static void delete_node (struct critnib * c , struct critnib_node * __restrict n ) {
195
197
if (is_leaf (n )) {
196
198
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
+ }
198
205
}
199
206
umf_ba_global_free (to_leaf (n ));
200
207
} else {
@@ -233,8 +240,13 @@ void critnib_delete(struct critnib *c) {
233
240
for (int i = 0 ; i < DELETED_LIFE ; i ++ ) {
234
241
umf_ba_global_free (c -> pending_del_nodes [i ]);
235
242
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
+ }
238
250
}
239
251
umf_ba_global_free (c -> pending_del_leaves [i ]);
240
252
}
@@ -288,8 +300,8 @@ static void free_leaf(struct critnib *__restrict c,
288
300
return ;
289
301
}
290
302
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 );
293
305
}
294
306
295
307
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) {
334
346
335
347
utils_annotate_memory_no_check (k , sizeof (struct critnib_leaf ));
336
348
349
+ utils_atomic_store_release_ptr (& k -> to_be_freed , 0 );
337
350
utils_atomic_store_release_ptr ((void * * )& k -> key , (void * )key );
338
351
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 );
339
354
340
355
struct critnib_node * kn = (void * )((word )k | 1 );
341
356
@@ -370,9 +385,9 @@ int critnib_insert(struct critnib *c, word key, void *value, int update) {
370
385
word at = path ^ key ;
371
386
if (!at ) {
372
387
ASSERT (is_leaf (n ));
373
- if (to_leaf (kn )-> value == value ) {
388
+ if (to_leaf (kn )-> to_be_freed == value ) {
374
389
// do not free the value
375
- to_leaf (kn )-> value = NULL ;
390
+ to_leaf (kn )-> to_be_freed = NULL ;
376
391
}
377
392
free_leaf (c , to_leaf (kn ));
378
393
@@ -418,19 +433,31 @@ int critnib_insert(struct critnib *c, word key, void *value, int update) {
418
433
/*
419
434
* critnib_remove -- delete a key from the critnib structure, return its value
420
435
*/
421
- void * critnib_remove (struct critnib * c , word key ) {
436
+ void * critnib_remove (struct critnib * c , word key , void * * ref ) {
422
437
struct critnib_leaf * k ;
423
438
void * value = NULL ;
424
439
440
+ if (!ref ) {
441
+ return NULL ;
442
+ }
443
+
425
444
utils_mutex_lock (& c -> mutex );
426
445
427
446
struct critnib_node * n = c -> root ;
428
447
if (!n ) {
429
448
goto not_found ;
430
449
}
431
450
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
+
434
461
free_node (c , c -> pending_del_nodes [del ]);
435
462
free_leaf (c , c -> pending_del_leaves [del ]);
436
463
c -> pending_del_nodes [del ] = NULL ;
@@ -491,13 +518,40 @@ void *critnib_remove(struct critnib *c, word key) {
491
518
492
519
del_leaf :
493
520
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 );
494
524
c -> pending_del_leaves [del ] = k ;
495
525
496
526
not_found :
497
527
utils_mutex_unlock (& c -> mutex );
498
528
return value ;
499
529
}
500
530
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
+
501
555
/*
502
556
* critnib_get -- query for a key ("==" match), returns value or NULL
503
557
*
@@ -508,13 +562,17 @@ void *critnib_remove(struct critnib *c, word key) {
508
562
* Counterintuitively, it's pointless to return the most current answer,
509
563
* we need only one that was valid at any point after the call started.
510
564
*/
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 ;
512
568
uint64_t wrs1 , wrs2 ;
513
- void * res ;
569
+ void * res = NULL ;
514
570
515
- do {
516
- struct critnib_node * n ;
571
+ if (!ref ) {
572
+ return NULL ;
573
+ }
517
574
575
+ do {
518
576
utils_atomic_load_acquire_u64 (& c -> remove_count , & wrs1 );
519
577
utils_atomic_load_acquire_ptr ((void * * )& c -> root , (void * * )& n );
520
578
@@ -529,11 +587,25 @@ void *critnib_get(struct critnib *c, word key) {
529
587
}
530
588
531
589
/* ... as we check it at the end. */
532
- struct critnib_leaf * k = to_leaf (n );
590
+ k = to_leaf (n );
533
591
res = (n && k -> key == key ) ? k -> value : NULL ;
534
592
utils_atomic_load_acquire_u64 (& c -> remove_count , & wrs2 );
535
593
} while (wrs1 + DELETED_LIFE <= wrs2 );
536
594
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
+
537
609
return res ;
538
610
}
539
611
@@ -645,19 +717,38 @@ static struct critnib_leaf *find_le(struct critnib_node *__restrict n,
645
717
*
646
718
* Same guarantees as critnib_get().
647
719
*/
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 ;
649
722
uint64_t wrs1 , wrs2 ;
650
723
void * res ;
651
724
725
+ if (!ref ) {
726
+ return NULL ;
727
+ }
728
+
652
729
do {
653
730
utils_atomic_load_acquire_u64 (& c -> remove_count , & wrs1 );
654
731
struct critnib_node * n ; /* avoid a subtle TOCTOU */
655
732
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 ;
657
734
res = k ? k -> value : NULL ;
658
735
utils_atomic_load_acquire_u64 (& c -> remove_count , & wrs2 );
659
736
} while (wrs1 + DELETED_LIFE <= wrs2 );
660
737
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
+
661
752
return res ;
662
753
}
663
754
@@ -743,12 +834,16 @@ static struct critnib_leaf *find_ge(struct critnib_node *__restrict n,
743
834
* critnib_find -- parametrized query, returns 1 if found
744
835
*/
745
836
int 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 ) {
747
838
uint64_t wrs1 , wrs2 ;
748
839
struct critnib_leaf * k ;
749
840
uintptr_t _rkey = (uintptr_t )0x0 ;
750
841
void * * _rvalue = NULL ;
751
842
843
+ if (!ref ) {
844
+ return 0 ;
845
+ }
846
+
752
847
/* <42 ≡ ≤41 */
753
848
if (dir < -1 ) {
754
849
if (!key ) {
@@ -790,6 +885,18 @@ int critnib_find(struct critnib *c, uintptr_t key, enum find_dir_t dir,
790
885
} while (wrs1 + DELETED_LIFE <= wrs2 );
791
886
792
887
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
+
793
900
if (rkey ) {
794
901
* rkey = _rkey ;
795
902
}
0 commit comments