-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added atomic bit test data structure
- Loading branch information
Showing
3 changed files
with
290 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#ifndef CACTUS_RT_EXPERIMENTAL_LOCKLESS_ATOMIC_BITSET_H_ | ||
#define CACTUS_RT_EXPERIMENTAL_LOCKLESS_ATOMIC_BITSET_H_ | ||
|
||
#include <atomic> | ||
#include <cstddef> | ||
#include <initializer_list> | ||
#include <limits> | ||
|
||
namespace cactus_rt::experimental::lockless { | ||
|
||
template <typename T> | ||
class AtomicBitset { | ||
static_assert(std::atomic<T>::is_always_lock_free); | ||
std::atomic<T> data_; | ||
|
||
// Avoid any casting that might occur during bit shifting later. | ||
static constexpr T kOne = 1; | ||
|
||
public: | ||
static constexpr size_t kCapacity = std::numeric_limits<T>::digits; | ||
|
||
/** | ||
* Always initialize the bitset to be 0 at the start. | ||
*/ | ||
AtomicBitset() : data_(0) {} | ||
|
||
void Set(const size_t i, const std::memory_order order = std::memory_order_seq_cst); | ||
|
||
void SetRange(const std::initializer_list<size_t> indices, const std::memory_order order = std::memory_order_seq_cst); | ||
|
||
void Reset(const size_t i, const std::memory_order order = std::memory_order_seq_cst); | ||
|
||
void ResetRange(const std::initializer_list<size_t> indices, const std::memory_order order = std::memory_order_seq_cst); | ||
|
||
void Flip(const size_t t, const std::memory_order order = std::memory_order_seq_cst); | ||
|
||
void FlipRange(const std::initializer_list<size_t> indices, const std::memory_order order = std::memory_order_seq_cst); | ||
|
||
void SetValue(const size_t i, const bool value, const std::memory_order order = std::memory_order_seq_cst); | ||
|
||
bool Test(const size_t i, const std::memory_order order = std::memory_order_seq_cst) const; | ||
|
||
T Value(const std::memory_order order = std::memory_order_seq_cst) const { | ||
return data_.load(order); | ||
} | ||
|
||
bool operator[](const size_t i) const { | ||
return Test(i); | ||
} | ||
}; | ||
|
||
} // namespace cactus_rt::experimental::lockless | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
#include "cactus_rt/experimental/lockless/atomic_bitset.h" | ||
|
||
#include <cassert> // TODO: switch to some sort of bounded integer arrangement. | ||
|
||
namespace cactus_rt::experimental::lockless { | ||
template <typename T> | ||
void AtomicBitset<T>::Set(const size_t i, const std::memory_order order) { | ||
assert(i < kCapacity); | ||
|
||
T bitmask = kOne << i; | ||
data_.fetch_or(bitmask, order); | ||
} | ||
|
||
template <typename T> | ||
void AtomicBitset<T>::SetRange(const std::initializer_list<size_t> indices, const std::memory_order order) { | ||
T bitmask = 0; | ||
for (const auto i : indices) { | ||
bitmask |= (kOne << i); | ||
} | ||
|
||
data_.fetch_or(bitmask, order); | ||
} | ||
|
||
template <typename T> | ||
void AtomicBitset<T>::Reset(const size_t i, const std::memory_order order) { | ||
assert(i < kCapacity); | ||
|
||
T bitmask = ~(kOne << i); | ||
data_.fetch_and(bitmask, order); | ||
} | ||
|
||
template <typename T> | ||
void AtomicBitset<T>::ResetRange(const std::initializer_list<size_t> indices, const std::memory_order order) { | ||
T bitmask = 0; | ||
for (const auto i : indices) { | ||
bitmask |= (kOne << i); | ||
} | ||
|
||
bitmask = ~(bitmask); | ||
data_.fetch_and(bitmask, order); | ||
} | ||
|
||
template <typename T> | ||
void AtomicBitset<T>::Flip(const size_t i, const std::memory_order order) { | ||
assert(i < kCapacity); | ||
|
||
T bitmask = kOne << i; | ||
data_.fetch_xor(bitmask, order); | ||
} | ||
|
||
template <typename T> | ||
void AtomicBitset<T>::FlipRange(const std::initializer_list<size_t> indices, const std::memory_order order) { | ||
T bitmask = 0; | ||
for (const auto i : indices) { | ||
bitmask |= (kOne << i); | ||
} | ||
|
||
data_.fetch_xor(bitmask, order); | ||
} | ||
|
||
template <typename T> | ||
void AtomicBitset<T>::SetValue(const size_t i, const bool value, const std::memory_order order) { | ||
assert(i < kCapacity); | ||
|
||
if (value) { | ||
Set(i, order); | ||
} else { | ||
Reset(i, order); | ||
} | ||
} | ||
|
||
template <typename T> | ||
bool AtomicBitset<T>::Test(const size_t i, const std::memory_order order) const { | ||
assert(i < kCapacity); | ||
|
||
T bitmask = kOne << i; | ||
return data_.load(order) & bitmask; | ||
} | ||
|
||
#if (ATOMIC_LLONG_LOCK_FREE == 2) | ||
template class AtomicBitset<unsigned long long>; | ||
#endif | ||
|
||
#if (ATOMIC_LONG_LOCK_FREE == 2) | ||
template class AtomicBitset<unsigned long>; | ||
#endif | ||
|
||
#if (ATOMIC_INT_LOCK_FREE == 2) | ||
template class AtomicBitset<unsigned int>; | ||
#endif | ||
|
||
#if (ATOMIC_SHORT_LOCK_FREE == 2) | ||
template class AtomicBitset<unsigned short>; | ||
#endif | ||
|
||
#if (ATOMIC_CHAR_LOCK_FREE == 2) | ||
template class AtomicBitset<unsigned char>; | ||
#endif | ||
|
||
} // namespace cactus_rt::experimental::lockless |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
#include "cactus_rt/experimental/lockless/atomic_bitset.h" | ||
|
||
#include <gtest/gtest.h> | ||
|
||
using AtomicBitset8 = cactus_rt::experimental::lockless::AtomicBitset<uint8_t>; | ||
using AtomicBitset64 = cactus_rt::experimental::lockless::AtomicBitset<uint64_t>; | ||
|
||
TEST(AtomicBitsetTest, SetResetAndLoad) { | ||
AtomicBitset8 bitset8; | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_FALSE(bitset8.Test(i)); | ||
} | ||
|
||
bitset8.Set(0); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8.Test(i), i == 0); | ||
} | ||
|
||
bitset8.Set(2); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8.Test(i), i == 0 || i == 2); | ||
} | ||
|
||
bitset8.Set(7); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8.Test(i), i == 0 || i == 2 || i == 7); | ||
} | ||
|
||
bitset8.Reset(7); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8[i], i == 0 || i == 2); | ||
} | ||
|
||
bitset8.Reset(6); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8.Test(i), i == 0 || i == 2); | ||
} | ||
|
||
bitset8.Reset(0); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8.Test(i), i == 2); | ||
} | ||
} | ||
|
||
TEST(AtomicBitsetTest, SetRangeResetRangeAndTest) { | ||
AtomicBitset8 bitset8; | ||
|
||
bitset8.SetRange({1, 6}); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8.Test(i), (i == 1) || (i == 6)); | ||
} | ||
|
||
bitset8.ResetRange({1, 2}); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8.Test(i), i == 6); | ||
} | ||
|
||
bitset8.SetRange({2, 6}); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8.Test(i), (i == 2) || (i == 6)); | ||
} | ||
|
||
bitset8.ResetRange({1, 2, 6}); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8.Test(i), false); | ||
} | ||
} | ||
|
||
TEST(AtomicBitsetTest, FlipAndTest) { | ||
AtomicBitset8 bitset8; | ||
|
||
bitset8.Flip(2); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8.Test(i), i == 2); | ||
} | ||
|
||
bitset8.FlipRange({2, 3, 7}); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8[i], (i == 3) || (i == 7)); | ||
} | ||
|
||
bitset8.Flip(2); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8[i], (i == 2) || (i == 3) || (i == 7)); | ||
} | ||
} | ||
|
||
TEST(AtomicBitsetTest, SetValueAndTest) { | ||
AtomicBitset8 bitset8; | ||
|
||
bitset8.SetValue(2, true); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8.Test(i), i == 2); | ||
} | ||
|
||
bitset8.SetValue(2, false); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8[i], false); | ||
} | ||
|
||
bitset8.SetValue(2, false); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8[i], false); | ||
} | ||
} | ||
|
||
TEST(AtomicBitsetTest, OutOfRange) { | ||
AtomicBitset8 bitset8; | ||
EXPECT_DEATH(bitset8.Set(8), ""); | ||
|
||
AtomicBitset64 bitset64; | ||
bitset64.Set(8); | ||
|
||
for (size_t i = 0; i < 64; i++) { | ||
EXPECT_EQ(bitset64[i], i == 8); | ||
} | ||
|
||
EXPECT_DEATH(bitset8.Set(64), ""); | ||
} |