diff --git a/include/cista/containers/bitvec.h b/include/cista/containers/bitvec.h index 2ac4dbf1..c0851510 100644 --- a/include/cista/containers/bitvec.h +++ b/include/cista/containers/bitvec.h @@ -88,6 +88,32 @@ struct basic_bitvec { } } +#if __has_cpp_attribute(__cpp_lib_atomic_ref) + constexpr void atomic_set( + Key const i, bool const val = true, + memory_order succ = std::memory_order::memory_order_seq_cst, + memory_order fail = std::memory_order::memory_order_seq_cst) noexcept { + assert(i < size_); + assert((to_idx(i) / bits_per_block) < blocks_.size()); + auto const block = std::atomic_ref{ + blocks_[static_cast(to_idx(i)) / bits_per_block]}; + auto const bit = to_idx(i) % bits_per_block; + + auto const update_block = [&](block_t const b) -> block_t { + if (val) { + return block | (block_t{1U} << bit); + } else { + return block & (~block_t{0U} ^ (block_t{1U} << bit)); + } + }; + + auto expected = block.load(); + while (std::atomic_compare_exchange_weak( + &block, &expected, update_block(expected), succ, fail)) { + } + } +#endif + void reset() noexcept { blocks_ = {}; } bool operator[](Key const i) const noexcept { return test(i); } diff --git a/test/bitvec_test.cc b/test/bitvec_test.cc index c6f433d4..220466cb 100644 --- a/test/bitvec_test.cc +++ b/test/bitvec_test.cc @@ -105,6 +105,37 @@ unsigned long get_random_number() { // period 2^96-1 return z; } +#if __has_cpp_attribute(__cpp_lib_atomic_ref) +TEST_CASE("bitvec atomic set") { + constexpr auto const kBits = 100'000U; + constexpr auto const kWorkers = 100U; + + auto start = std::atomic_bool{}; + auto b = cista::raw::bitvec{}; + b.resize(kBits); + auto workers = std::vector(kWorkers); + for (auto i = 0U; i != kWorkers; ++i) { + workers[i] = std::thread{[&, i]() { + while (!start) { + // wait for synchronized start + } + + for (auto j = i; j < kBits; j += kBits / kWorkers) { + b.atomic_set(j); + } + }}; + } + + start.store(true); + + for (auto& w : workers) { + w.join(); + } + + CHECK(b.count() == kBits); +} +#endif + TEST_CASE("bitvec parallel") { constexpr auto const kBits = 1'000'000U; constexpr auto const kWorkers = 100U;