@@ -331,18 +331,26 @@ static void free_leaf(struct critnib *__restrict c,
331331 return ;
332332 }
333333
334+ // k should be added to the c->deleted_leaf list here
335+ // or in critnib_release() when the reference count drops to 0.
336+ utils_atomic_store_release_u8 (& k -> pending_deleted_leaf , 1 );
337+
334338 if (c -> cb_free_leaf ) {
335339 uint64_t ref_count ;
336340 utils_atomic_load_acquire_u64 (& k -> ref_count , & ref_count );
337341 if (ref_count > 0 ) {
338- // k will be added to c->deleted_leaf in critnib_release()
342+ // k will be added to the c->deleted_leaf list in critnib_release()
339343 // when the reference count drops to 0.
340- utils_atomic_store_release_u8 (& k -> pending_deleted_leaf , 1 );
341344 return ;
342345 }
343346 }
344347
345- add_to_deleted_leaf_list (c , k );
348+ uint8_t expected = 1 ;
349+ uint8_t desired = 0 ;
350+ if (utils_compare_exchange_u8 (& k -> pending_deleted_leaf , & expected ,
351+ & desired )) {
352+ add_to_deleted_leaf_list (c , k );
353+ }
346354}
347355
348356/*
@@ -392,8 +400,8 @@ int critnib_insert(struct critnib *c, word key, void *value, int update) {
392400 utils_atomic_store_release_u8 (& k -> pending_deleted_leaf , 0 );
393401
394402 if (c -> cb_free_leaf ) {
395- // mark the leaf as valid (ref_count == 1 )
396- utils_atomic_store_release_u64 (& k -> ref_count , 1ULL );
403+ // mark the leaf as valid (ref_count == 2 )
404+ utils_atomic_store_release_u64 (& k -> ref_count , 2ULL );
397405 } else {
398406 // the reference counter is not used in this case
399407 utils_atomic_store_release_u64 (& k -> ref_count , 0ULL );
@@ -602,35 +610,40 @@ int critnib_release(struct critnib *c, void *ref) {
602610 struct critnib_leaf * k = (struct critnib_leaf * )ref ;
603611
604612 uint64_t ref_count ;
605- utils_atomic_load_acquire_u64 (& k -> ref_count , & ref_count );
606-
607- if (ref_count == 0 ) {
608- return -1 ;
609- }
610-
613+ uint64_t ref_desired ;
611614 /* decrement the reference count */
612- if (utils_atomic_decrement_u64 (& k -> ref_count ) == 0 ) {
615+ do {
616+ utils_atomic_load_acquire_u64 (& k -> ref_count , & ref_count );
617+ if (ref_count == 0 ) {
618+ return -1 ;
619+ }
620+ ref_desired = ref_count - 1 ;
621+ } while (
622+ !utils_compare_exchange_u64 (& k -> ref_count , & ref_count , & ref_desired ));
623+
624+ if (ref_desired == 1 ) {
613625 void * to_be_freed = NULL ;
614626 utils_atomic_load_acquire_ptr (& k -> to_be_freed , & to_be_freed );
615- if (to_be_freed ) {
616- utils_atomic_store_release_ptr ( & k -> to_be_freed , NULL );
617- c -> cb_free_leaf ( c -> leaf_allocator , to_be_freed );
627+ if (to_be_freed == NULL ) {
628+ LOG_FATAL ( " to_be_freed == NULL, value: %p\n" , k -> value );
629+ assert ( to_be_freed != NULL );
618630 }
619- uint8_t pending_deleted_leaf ;
620- utils_atomic_load_acquire_u8 (& k -> pending_deleted_leaf ,
621- & pending_deleted_leaf );
622- if (pending_deleted_leaf ) {
623- utils_atomic_store_release_u8 (& k -> pending_deleted_leaf , 0 );
631+
632+ utils_atomic_store_release_ptr (& k -> to_be_freed , NULL );
633+ // mark the leaf as not used (ref_count == 0)
634+ utils_atomic_store_release_u64 (& k -> ref_count , 0ULL );
635+
636+ c -> cb_free_leaf (c -> leaf_allocator , to_be_freed );
637+
638+ uint8_t expected = 1 ;
639+ uint8_t desired = 0 ;
640+ if (utils_compare_exchange_u8 (& k -> pending_deleted_leaf , & expected ,
641+ & desired )) {
624642 add_to_deleted_leaf_list (c , k );
625643 }
626- }
627644
628- #ifndef NDEBUG
629- // check if the reference count is overflowed
630- utils_atomic_load_acquire_u64 (& k -> ref_count , & ref_count );
631- assert ((ref_count & (1ULL << 63 )) == 0 );
632- assert (ref_count != (uint64_t )(0 - 1ULL ));
633- #endif
645+ return 0 ;
646+ }
634647
635648 return 0 ;
636649}
0 commit comments