|
89 | 89 | #define NIB ((1ULL << SLICE) - 1) |
90 | 90 | #define SLNODES (1 << SLICE) |
91 | 91 |
|
| 92 | +// it has to be >= 2 |
| 93 | +#define LEAF_VALID (2ULL) |
| 94 | + |
92 | 95 | typedef uintptr_t word; |
93 | 96 | typedef uint8_t sh_t; |
94 | 97 |
|
@@ -331,18 +334,26 @@ static void free_leaf(struct critnib *__restrict c, |
331 | 334 | return; |
332 | 335 | } |
333 | 336 |
|
| 337 | + // k should be added to the c->deleted_leaf list here |
| 338 | + // or in critnib_release() when the reference count drops to 0. |
| 339 | + utils_atomic_store_release_u8(&k->pending_deleted_leaf, 1); |
| 340 | + |
334 | 341 | if (c->cb_free_leaf) { |
335 | 342 | uint64_t ref_count; |
336 | 343 | utils_atomic_load_acquire_u64(&k->ref_count, &ref_count); |
337 | 344 | if (ref_count > 0) { |
338 | | - // k will be added to c->deleted_leaf in critnib_release() |
| 345 | + // k will be added to the c->deleted_leaf list in critnib_release() |
339 | 346 | // when the reference count drops to 0. |
340 | | - utils_atomic_store_release_u8(&k->pending_deleted_leaf, 1); |
341 | 347 | return; |
342 | 348 | } |
343 | 349 | } |
344 | 350 |
|
345 | | - add_to_deleted_leaf_list(c, k); |
| 351 | + uint8_t expected = 1; |
| 352 | + uint8_t desired = 0; |
| 353 | + if (utils_compare_exchange_u8(&k->pending_deleted_leaf, &expected, |
| 354 | + &desired)) { |
| 355 | + add_to_deleted_leaf_list(c, k); |
| 356 | + } |
346 | 357 | } |
347 | 358 |
|
348 | 359 | /* |
@@ -392,8 +403,8 @@ int critnib_insert(struct critnib *c, word key, void *value, int update) { |
392 | 403 | utils_atomic_store_release_u8(&k->pending_deleted_leaf, 0); |
393 | 404 |
|
394 | 405 | if (c->cb_free_leaf) { |
395 | | - // mark the leaf as valid (ref_count == 1) |
396 | | - utils_atomic_store_release_u64(&k->ref_count, 1ULL); |
| 406 | + // mark the leaf as valid (ref_count == 2) |
| 407 | + utils_atomic_store_release_u64(&k->ref_count, LEAF_VALID); |
397 | 408 | } else { |
398 | 409 | // the reference counter is not used in this case |
399 | 410 | utils_atomic_store_release_u64(&k->ref_count, 0ULL); |
@@ -602,36 +613,50 @@ int critnib_release(struct critnib *c, void *ref) { |
602 | 613 | struct critnib_leaf *k = (struct critnib_leaf *)ref; |
603 | 614 |
|
604 | 615 | 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 | | - |
| 616 | + uint64_t ref_desired; |
611 | 617 | /* decrement the reference count */ |
612 | | - if (utils_atomic_decrement_u64(&k->ref_count) == 0) { |
613 | | - void *to_be_freed = NULL; |
614 | | - 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); |
618 | | - } |
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); |
624 | | - add_to_deleted_leaf_list(c, k); |
| 618 | + do { |
| 619 | + utils_atomic_load_acquire_u64(&k->ref_count, &ref_count); |
| 620 | + if (ref_count < LEAF_VALID) { |
| 621 | + LOG_FATAL("critnib_release() was called too many times (ref_count " |
| 622 | + "= %llu)\n", |
| 623 | + (unsigned long long)ref_count); |
| 624 | + assert(ref_count >= LEAF_VALID); |
| 625 | + return -1; |
625 | 626 | } |
| 627 | + ref_desired = ref_count - 1; |
| 628 | + } while ( |
| 629 | + !utils_compare_exchange_u64(&k->ref_count, &ref_count, &ref_desired)); |
| 630 | + |
| 631 | + if (ref_desired >= LEAF_VALID) { |
| 632 | + // ref_counter was decremented and it is still valid |
| 633 | + return 0; |
626 | 634 | } |
627 | 635 |
|
| 636 | + /* ref_counter == (LEAF_VALID - 1)) - the leaf will be freed */ |
| 637 | + void *to_be_freed = NULL; |
| 638 | + utils_atomic_load_acquire_ptr(&k->to_be_freed, &to_be_freed); |
| 639 | + utils_atomic_store_release_ptr(&k->to_be_freed, NULL); |
628 | 640 | #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)); |
| 641 | + if (to_be_freed == NULL) { |
| 642 | + LOG_FATAL("leaf will not be freed (to_be_freed == NULL, value = %p)\n", |
| 643 | + k->value); |
| 644 | + assert(to_be_freed != NULL); |
| 645 | + } |
633 | 646 | #endif |
634 | 647 |
|
| 648 | + // mark the leaf as not used (ref_count == 0) |
| 649 | + utils_atomic_store_release_u64(&k->ref_count, 0ULL); |
| 650 | + |
| 651 | + c->cb_free_leaf(c->leaf_allocator, to_be_freed); |
| 652 | + |
| 653 | + uint8_t expected = 1; |
| 654 | + uint8_t desired = 0; |
| 655 | + if (utils_compare_exchange_u8(&k->pending_deleted_leaf, &expected, |
| 656 | + &desired)) { |
| 657 | + add_to_deleted_leaf_list(c, k); |
| 658 | + } |
| 659 | + |
635 | 660 | return 0; |
636 | 661 | } |
637 | 662 |
|
@@ -661,7 +686,7 @@ static inline int increment_ref_count(struct critnib_leaf *k) { |
661 | 686 |
|
662 | 687 | do { |
663 | 688 | utils_atomic_load_acquire_u64(&k->ref_count, &expected); |
664 | | - if (expected == 0) { |
| 689 | + if (expected < LEAF_VALID) { |
665 | 690 | return -1; |
666 | 691 | } |
667 | 692 | desired = expected + 1; |
|
0 commit comments