The std::atomic types allow atomic access to variables, but I would sometimes like non-atomic access, for example when the access is protected by a mutex. Consider a bitfield class that allows both multi-threaded access (via insert) and single-threaded vectorized access (via operator|=):
class Bitfield
{
const size_t size_, word_count_;
std::atomic<size_t> * words_;
std::mutex mutex_;
public:
Bitfield (size_t size) :
size_(size),
word_count_((size + 8 * sizeof(size_t) - 1) / (8 * sizeof(size_t)))
{
// make sure words are 32-byte aligned
posix_memalign(&words_, 32, word_count_ * sizeof(size_t));
for (int i = 0; i < word_count_; ++i) {
new(words_ + i) std::atomic<size_t>(0);
}
}
~Bitfield () { free(words_); }
private:
void insert_one (size_t pos)
{
size_t mask = size_t(1) << (pos % (8 * sizeof(size_t)));
std::atomic<size_t> * word = words_ + pos / (8 * sizeof(size_t));
word->fetch_or(mask, std::memory_order_relaxed);
}
public:
void insert (const std::set<size_t> & items)
{
std::lock_guard<std::mutex> lock(mutex_);
// do some sort of muti-threaded insert, with TBB or #pragma omp
parallel_foreach(items.begin(), items.end(), insert_one);
}
void operator |= (const Bitfield & other)
{
assert(other.size_ == size_);
std::unique_lock<std::mutex> lock1(mutex_, defer_lock);
std::unique_lock<std::mutex> lock2(other.mutex_, defer_lock);
std::lock(lock1, lock2); // edited to lock other_.mutex_ as well
// allow gcc to autovectorize (256 bits at once with AVX)
static_assert(sizeof(size_t) == sizeof(std::atomic<size_t>), "fail");
size_t * __restrict__ words = reinterpret_cast<size_t *>(words_);
const size_t * __restrict__ other_words
= reinterpret_cast<const size_t *>(other.words_);
for (size_t i = 0, end = word_count_; i < end; ++i) {
words[i] |= other_words[i];
}
}
};
Note operator|= is very close to what's in my real code, but insert(std::set) is just attempting to capture the idea that one can
acquire lock;
make many atomic accesses in parallel;
release lock;
My question is this: what is the best way to mix such atomic and non-atomic access? Answers to [1,2] below suggest that casting is wrong (and I agree). But surely the standard allows such apparently safe access?
More generally, can one use a reader-writer-lock and allow "readers" to read and write atomically, and the unique "writer" to read and write non-atomically?