#include <iostream>
#include <thread>
#include <vector>
#include <memory>
#include <atomic>
/* wrapping-structure for using vector which element is std::atomic<T> */
/* because vector's element must be copy-constructible and copy-assignable but atomic<T> is not. */
/* https://stackoverflow.com/questions/13193484/how-to-declare-a-vector-of-atomic-in-c */
template <typename T>
struct atomicwrapper {
std::atomic<T> _a;
atomicwrapper() : _a() {}
atomicwrapper(const std::atomic<T> &a) : _a(a.load()) {}
atomicwrapper(const atomicwrapper &obj) : _a(obj._a.load()) {}
atomicwrapper &operator=(const atomicwrapper &obj) { _a.store(obj._a.load()); }
};
void divmod(int64_t a, int64_t b, int64_t &q, int64_t &r) { q = a / b; r = a % b; }
using T = int;
class MyVectorBool {
private:
static constexpr T width = sizeof(T) * 8;
static constexpr T bits = width - 1;
static constexpr T mask_dirty = 1 << bits;
std::vector<atomicwrapper<T>> _myVectorBool;
int64_t _size;
public:
MyVectorBool() = delete;
MyVectorBool(int64_t size) {
_size = size;
_myVectorBool = std::vector<atomicwrapper<T>>((_size - 1) / bits + 1);
for (auto &c : _myVectorBool)
c._a.store(0);
}
bool test(int64_t index) {
if (index >= _size) throw 1;
int64_t q, r;
divmod(index, bits, q, r);
T elements;
for (;;) {
elements = _myVectorBool[q]._a.load();
if (!(elements & mask_dirty)) break;
}
return ((elements & (1 << r)) != 0);
}
#define MACRO_CAS(V) \
for (;;) { \
T before_cas; \
V = before_cas = _myVectorBool[q]._a.load(); /* preliminary data-loading */ \
if (V & mask_dirty) continue; /* if elment's dirty bit is on, spin lock */ \
CAS_START: \
if (_myVectorBool[q]._a.compare_exchange_weak(/*ref*/V, V | mask_dirty)) { \
/* when exchange-able, status is not dirty, nothing to do */ \
} else if (/*returned */ V != before_cas) { \
/* when not-change-able and atomic-value is actually change, spin lock */ \
continue; \
} else { \
/* when not-change-able and yet atomic-value is same befor and after CAS (it's called weak-CAS's Spurious Failure), do CAS again */ \
goto CAS_START; \
} \
break; \
}
/* end of macro */
void set(int64_t index) {
if (index >= _size) return;
int64_t q, r;
divmod(index, bits, q, r);
T elements;
MACRO_CAS(elements)
elements |= 1 << r;
_myVectorBool[q]._a.store(elements);
}
void reset(int64_t index) {
if (index >= _size) return;
int64_t q, r;
divmod(index, bits, q, r);
T elements;
MACRO_CAS(elements)
T mask = 1 << r;
elements &= ~mask;
_myVectorBool[q]._a.store(elements);
}
};
/*------------------------------------------------*/
int const A = 60; /* test A */
int const B = 15; /* test B */
MyVectorBool vb1(A);
MyVectorBool vb2(B);
void worker(int i) {
vb2.set(i);
}
int main() {
/* test A */
for (int i = 0; i < A; i++) {
std::cout << "set: i = " << i << " : ";
vb1.set(i);
if (vb1.test(i)) std::cout << "OK" << std::endl;
else { std::cout << "NG" << std::endl; return 0; }
}
for (int i = 0; i < A; i++) {
std::cout << "reset: i = " << i << " : ";
vb1.reset(i);
if (!vb1.test(i)) std::cout << "OK" << std::endl;
else { std::cout << "NG" << std::endl; return 0; }
}
/* test B */
std::cout << "core.n = " << std::thread::hardware_concurrency() << std::endl;
for (int i = 0; i < B; i++)
std::cout << (vb2.test(i) ? "1" : "0");
std::cout << std::endl;
std::vector<std::shared_ptr<std::thread>> threads(B);
int i = 0;
for (std::shared_ptr<std::thread> &t : threads)
t = std::make_shared<std::thread>(worker, i++);
for (std::shared_ptr<std::thread> t : threads)
t->join();
for (int i = 0; i < B; i++)
std::cout << (vb2.test(i) ? "1" : "0");
std::cout << std::endl;
return 0;
}
/* end */