I have two pointers: pA and pB. They points to two big hash map objects. when the hash map pointed by pB is updated completely, I want to swap pB and pA.
In C++ 17, how to swap them fast and thread safe? Atomic? I am new to c++ 17.
I have two pointers: pA and pB. They points to two big hash map objects. when the hash map pointed by pB is updated completely, I want to swap pB and pA.
In C++ 17, how to swap them fast and thread safe? Atomic? I am new to c++ 17.
Atomic wait-free exchange of 2 pointers can be implemented in the following manner:
#include <atomic>
#include <cstdint>
#include <cassert>
template<class T>
class Pointers2 {
uintptr_t const ab_;
std::atomic<uintptr_t> a_;
public:
Pointers2(T* a, T* b)
: ab_(reinterpret_cast<uintptr_t>(a) ^ reinterpret_cast<uintptr_t>(b))
, a_(reinterpret_cast<uintptr_t>(a))
{}
T* a() const { return reinterpret_cast<T*>(a_.load(std::memory_order_acquire)); }
T* b() const { return reinterpret_cast<T*>(a_.load(std::memory_order_acquire) ^ ab_); }
void exchange() { a_.fetch_xor(ab_, std::memory_order_release); }
};
int main() {
int a = 1, b = 2;
Pointers2<int> p2(&a, &b);
assert(p2.a() == &a);
assert(p2.b() == &b);
p2.exchange();
assert(p2.a() == &b);
assert(p2.b() == &a);
p2.exchange();
assert(p2.a() == &a);
assert(p2.b() == &b);
}
Acquire/release memory ordering is required to make sure that writes to shared data T
do not get reordered past exchange
.
On x86-64 you can atomically exchange 2 pointers only if they are adjacent in memory and aligned to 16 bytes with cmpxchg16b
instruction directly or by using libatomic_ops
:
AO_INLINE int
AO_compare_double_and_swap_double_full(volatile AO_double_t *addr,
AO_t old_val1, AO_t old_val2,
AO_t new_val1, AO_t new_val2)
{
char result;
__asm__ __volatile__("lock; cmpxchg16b %0; setz %1"
: "=m"(*addr), "=a"(result)
: "m"(*addr), "d" (old_val2), "a" (old_val1),
"c" (new_val2), "b" (new_val1)
: "memory");
return (int) result;
}
If cmpxchg16b
is unavailable you need to use a mutex to make exchanging 2 pointers atomic.
Maybe it's not just swap that should be atomic, but transaction should include check that swap should be made (is_the_hash_map_pointed_by_pB_updated_completely()). Probably hash maps should be protected from concurrent usage with mutex.