@@ -23,6 +23,12 @@ namespace ROCKSDB_NAMESPACE {
2323
2424namespace hyper_clock_cache {
2525
26+ inline uint64_t GetRefcount (uint64_t meta) {
27+ return ((meta >> ClockHandle::kAcquireCounterShift ) -
28+ (meta >> ClockHandle::kReleaseCounterShift )) &
29+ ClockHandle::kCounterMask ;
30+ }
31+
2632static_assert (sizeof (ClockHandle) == 64U ,
2733 " Expecting size / alignment with common cache line size" );
2834
@@ -49,6 +55,7 @@ ClockHandleTable::~ClockHandleTable() {
4955 break ;
5056 case ClockHandle::kStateInvisible : // rare but possible
5157 case ClockHandle::kStateVisible :
58+ assert (GetRefcount (h.meta ) == 0 );
5259 h.FreeData ();
5360#ifndef NDEBUG
5461 Rollback (h.hash , &h);
@@ -562,10 +569,7 @@ bool ClockHandleTable::Release(ClockHandle* h, bool useful,
562569 }
563570 // Take ownership if no refs
564571 do {
565- uint64_t refcount = ((old_meta >> ClockHandle::kAcquireCounterShift ) -
566- (old_meta >> ClockHandle::kReleaseCounterShift )) &
567- ClockHandle::kCounterMask ;
568- if (refcount != 0 ) {
572+ if (GetRefcount (old_meta) != 0 ) {
569573 // Not last ref at some point in time during this Release call
570574 // Correct for possible (but rare) overflow
571575 CorrectNearOverflow (old_meta, h->meta );
@@ -622,6 +626,8 @@ void ClockHandleTable::Ref(ClockHandle& h) {
622626
623627 assert ((old_meta >> ClockHandle::kStateShift ) &
624628 ClockHandle::kStateShareableBit );
629+ // Must have already had a reference
630+ assert (GetRefcount (old_meta) > 0 );
625631 (void )old_meta;
626632}
627633
@@ -671,10 +677,7 @@ void ClockHandleTable::Erase(const CacheKeyBytes& key, uint32_t hash) {
671677 old_meta &= ~(uint64_t {ClockHandle::kStateVisibleBit }
672678 << ClockHandle::kStateShift );
673679 for (;;) {
674- uint64_t refcount =
675- ((old_meta >> ClockHandle::kAcquireCounterShift ) -
676- (old_meta >> ClockHandle::kReleaseCounterShift )) &
677- ClockHandle::kCounterMask ;
680+ uint64_t refcount = GetRefcount (old_meta);
678681 assert (refcount > 0 );
679682 if (refcount > 1 ) {
680683 // Not last ref at some point in time during this Erase call
@@ -683,8 +686,10 @@ void ClockHandleTable::Erase(const CacheKeyBytes& key, uint32_t hash) {
683686 std::memory_order_release);
684687 break ;
685688 } else if (h->meta .compare_exchange_weak (
686- old_meta, uint64_t {ClockHandle::kStateConstruction }
687- << ClockHandle::kStateShift )) {
689+ old_meta,
690+ uint64_t {ClockHandle::kStateConstruction }
691+ << ClockHandle::kStateShift ,
692+ std::memory_order_acq_rel)) {
688693 // Took ownership
689694 assert (hash == h->hash );
690695 // TODO? Delay freeing?
@@ -740,20 +745,32 @@ void ClockHandleTable::ConstApplyToEntriesRange(
740745 for (uint32_t i = index_begin; i < index_end; i++) {
741746 ClockHandle& h = array_[i];
742747
748+ // Note: to avoid using compare_exchange, we have to be extra careful.
743749 uint64_t old_meta = h.meta .load (std::memory_order_relaxed);
744750 // Check if it's an entry visible to lookups
745751 if ((old_meta >> ClockHandle::kStateShift ) & check_state_mask) {
746- // Increment acquire counter
752+ // Increment acquire counter. Note: it's possible that the entry has
753+ // completely changed since we loaded old_meta, but incrementing acquire
754+ // count is always safe. (Similar to optimistic Lookup here.)
747755 old_meta = h.meta .fetch_add (ClockHandle::kAcquireIncrement ,
748756 std::memory_order_acquire);
749- // Double-check
750- if ((old_meta >> ClockHandle::kStateShift ) & check_state_mask) {
751- func (h);
757+ // Check whether we actually acquired a reference.
758+ if ((old_meta >> ClockHandle::kStateShift ) &
759+ ClockHandle::kStateShareableBit ) {
760+ // Apply func if appropriate
761+ if ((old_meta >> ClockHandle::kStateShift ) & check_state_mask) {
762+ func (h);
763+ }
764+ // Pretend we never took the reference
765+ h.meta .fetch_sub (ClockHandle::kAcquireIncrement ,
766+ std::memory_order_release);
767+ // No net change, so don't need to check for overflow
768+ } else {
769+ // For other states, incrementing the acquire counter has no effect
770+ // so we don't need to undo it. Furthermore, we cannot safely undo
771+ // it because we did not acquire a read reference to lock the
772+ // entry in a Shareable state.
752773 }
753- // Pretend we never took the reference
754- h.meta .fetch_sub (ClockHandle::kAcquireIncrement ,
755- std::memory_order_release);
756- // No net change, so don't need to check for overflow
757774 }
758775 }
759776}
@@ -763,12 +780,9 @@ void ClockHandleTable::EraseUnRefEntries() {
763780 ClockHandle& h = array_[i];
764781
765782 uint64_t old_meta = h.meta .load (std::memory_order_relaxed);
766- uint64_t refcount = ((old_meta >> ClockHandle::kAcquireCounterShift ) -
767- (old_meta >> ClockHandle::kReleaseCounterShift )) &
768- ClockHandle::kCounterMask ;
769783 if (old_meta & (uint64_t {ClockHandle::kStateShareableBit }
770784 << ClockHandle::kStateShift ) &&
771- refcount == 0 &&
785+ GetRefcount (old_meta) == 0 &&
772786 h.meta .compare_exchange_strong (old_meta,
773787 uint64_t {ClockHandle::kStateConstruction }
774788 << ClockHandle::kStateShift ,
@@ -877,13 +891,12 @@ void ClockHandleTable::Evict(size_t requested_charge, size_t* freed_charge,
877891 // Only clock update entries with no outstanding refs
878892 continue ;
879893 }
880- if (!(meta >> ClockHandle::kStateShift &
894+ if (!(( meta >> ClockHandle::kStateShift ) &
881895 ClockHandle::kStateShareableBit )) {
882896 // Only clock update Shareable entries
883897 continue ;
884898 }
885- // ModTableSize(old_clock_pointer + i));
886- if (meta >> ClockHandle::kStateShift == ClockHandle::kStateVisible &&
899+ if ((meta >> ClockHandle::kStateShift == ClockHandle::kStateVisible ) &&
887900 acquire_count > 0 ) {
888901 // Decrement clock
889902 uint64_t new_count = std::min (acquire_count - 1 ,
@@ -1101,9 +1114,7 @@ size_t ClockCacheShard::GetPinnedUsage() const {
11011114 table_.ConstApplyToEntriesRange (
11021115 [&table_pinned_usage, charge_metadata](const ClockHandle& h) {
11031116 uint64_t meta = h.meta .load (std::memory_order_relaxed);
1104- uint64_t refcount = ((meta >> ClockHandle::kAcquireCounterShift ) -
1105- (meta >> ClockHandle::kReleaseCounterShift )) &
1106- ClockHandle::kCounterMask ;
1117+ uint64_t refcount = GetRefcount (meta);
11071118 // Holding one ref for ConstApplyToEntriesRange
11081119 assert (refcount > 0 );
11091120 if (refcount > 1 ) {
0 commit comments