From 59d72d745f954d602d2ce3b31014116b844c21a1 Mon Sep 17 00:00:00 2001 From: Alex Kiselev Date: Fri, 31 Jul 2026 00:22:21 +0200 Subject: [PATCH] Fix a race in snapshot_and_lock_two() so it returns correct buckets while a concurrent thread runs a resizing Consider the followging scenario with two threads T1 and T2: 1. T1 snapshot_and_lock_two() loads old hashpower H; 2. T2 cuckoo_fast_double() resizes to H+1, migrates entries, and increments the resize_counter; 3. T1 snapshot_and_lock_two() loads the new resize_counter; 4. T1 snapshot_and_lock_two() calculates incorrect bucket indexes using the stale H value that was read at the step 1; 5. T1 calls lock_two(). Its check_resize_counter() succeeds because both counter reads observe the new value As a result an entry moved to the new high-half bucket might be missed by readers. A solution is to load hashpower after loading the resize counter. The concurrent resizing thread uses the release semantic when incrementing the resize_counter, so there is a happens-before relationship with the load at the line 1. It guarantees that the following hp load will read either the value published by that resize or a newer value. It newer cases will be detected and discarded by later resize counter checks. 1: const ResizeCounter resize_counter = load_resize_counter(); 2: const size_type hp = hashpower(); const size_type i1 = index_hash(hp, hv.hash); const size_type i2 = alt_index(hp, hv.partial, i1); try { return lock_two(resize_counter, i1, i2, TABLE_MODE()); } catch (resize_counter_changed &) { continue; } --- libcuckoo/cuckoohash_map.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcuckoo/cuckoohash_map.hh b/libcuckoo/cuckoohash_map.hh index 1a63627..d1c7695 100644 --- a/libcuckoo/cuckoohash_map.hh +++ b/libcuckoo/cuckoohash_map.hh @@ -1087,8 +1087,8 @@ private: TwoBuckets snapshot_and_lock_two(const hash_value &hv) const { while (true) { // Keep the current hashpower and locks we're using to compute the buckets - const size_type hp = hashpower(); const ResizeCounter resize_counter = load_resize_counter(); + const size_type hp = hashpower(); const size_type i1 = index_hash(hp, hv.hash); const size_type i2 = alt_index(hp, hv.partial, i1); try {