-
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
214 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,43 @@ | ||
#ifndef CACTUS_RT_EXPERIMENTAL_LOCKLESS_ATOMIC_BITSET_H_ | ||
#define CACTUS_RT_EXPERIMENTAL_LOCKLESS_ATOMIC_BITSET_H_ | ||
|
||
#include <atomic> | ||
#include <cstddef> | ||
#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(size_t i, std::memory_order order = std::memory_order_seq_cst); | ||
|
||
void Reset(size_t i, std::memory_order order = std::memory_order_seq_cst); | ||
|
||
void Flip(size_t t, std::memory_order order = std::memory_order_seq_cst); | ||
|
||
void SetValue(size_t i, bool value, std::memory_order order = std::memory_order_seq_cst); | ||
|
||
bool Test(size_t i, std::memory_order order = std::memory_order_seq_cst) const; | ||
|
||
bool operator[](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,69 @@ | ||
#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(size_t i, std::memory_order order) { | ||
assert(i < kCapacity); | ||
|
||
T bitmask = kOne << i; | ||
data_.fetch_or(bitmask, order); | ||
} | ||
|
||
template <typename T> | ||
void AtomicBitset<T>::Reset(size_t i, std::memory_order order) { | ||
assert(i < kCapacity); | ||
|
||
T bitmask = ~(kOne << i); | ||
data_.fetch_and(bitmask, order); | ||
} | ||
|
||
template <typename T> | ||
void AtomicBitset<T>::Flip(size_t i, std::memory_order order) { | ||
assert(i < kCapacity); | ||
|
||
T bitmask = kOne << i; | ||
data_.fetch_xor(bitmask, order); | ||
} | ||
|
||
template <typename T> | ||
void AtomicBitset<T>::SetValue(size_t i, bool value, std::memory_order order) { | ||
assert(i < kCapacity); | ||
|
||
if (value) { | ||
Set(i, order); | ||
} else { | ||
Reset(i, order); | ||
} | ||
} | ||
|
||
template <typename T> | ||
bool AtomicBitset<T>::Test(size_t i, 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,102 @@ | ||
#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, FlipAndTest) { | ||
AtomicBitset8 bitset8; | ||
|
||
bitset8.Flip(2); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8.Test(i), i == 2); | ||
} | ||
|
||
bitset8.Flip(2); | ||
|
||
for (size_t i = 0; i < 8; i++) { | ||
EXPECT_EQ(bitset8[i], false); | ||
} | ||
} | ||
|
||
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), ""); | ||
} |