|
#include <atomic> |
|
#include <thread> |
|
#include <chrono> |
|
#include <cstdio> |
|
#include <assert.h> |
|
|
|
static bool KEEP_GOING = true; |
|
|
|
template<class T> |
|
class AtomicPointer { |
|
public: |
|
AtomicPointer(): _ptr(nullptr), _counter(nullptr) |
|
{} |
|
AtomicPointer(T* ptr): _ptr(ptr), |
|
_counter(new std::atomic<size_t>(1)) |
|
{} |
|
bool valid() const { return _ptr.laod() == nullptr; } |
|
|
|
AtomicPointer(const AtomicPointer<T>& other) { |
|
_counter.store(other._counter.load()); |
|
_counter.load()->fetch_add(1); |
|
_ptr.store(other._ptr.load()); |
|
} |
|
|
|
AtomicPointer<T>& operator=(const AtomicPointer<T>& other) noexcept |
|
{ |
|
if (this != &other) { |
|
size_t checker = 0; |
|
if (_counter.load() != nullptr && (checker = _counter.load()->fetch_sub(1)) == 1) { |
|
printf("= DELETING: %p\n", _ptr.load()); |
|
assert(checker-1 == 0); |
|
delete _ptr.load(); |
|
delete _counter.load(); |
|
} |
|
_counter.store(other._counter.load()); |
|
_counter.load()->fetch_add(1); |
|
_ptr.store(other._ptr.load()); |
|
} |
|
return *this; |
|
} |
|
|
|
T* operator->() const { return _ptr.load(); } |
|
|
|
inline T* get() const { return _ptr.load(); } |
|
|
|
void reset(T* ptr = nullptr) { |
|
size_t checker = 0; |
|
if ((checker = _counter.load()->fetch_sub(1)) == 1) { |
|
printf("RESET DELETING: %p\n", _ptr.load()); |
|
assert(checker-1 == 0); |
|
delete _ptr.load(); |
|
delete _counter.load(); |
|
} |
|
_ptr.store(ptr); |
|
_counter.store(new std::atomic<size_t>(1)); |
|
} |
|
|
|
bool cmp_exchange(T* expected, T* desired = nullptr) { |
|
if (_ptr.compare_exchange_strong(expected, desired)) { |
|
size_t checker = 0; |
|
if ((checker = _counter.load()->fetch_sub(1)) == 1) { |
|
printf("EXCHANGE DELETING: %p\n", expected); |
|
assert(checker-1 == 0); |
|
delete expected; |
|
delete _counter.load(); |
|
} |
|
_counter.store(new std::atomic<size_t>(1)); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
~AtomicPointer() { |
|
size_t checker = 0; |
|
if ((checker = _counter.load()->fetch_sub(1)) == 1) { |
|
printf("DELETING: %p\n", _ptr.load()); |
|
assert(checker-1 == 0); |
|
delete _ptr.load(); |
|
assert(checker-1 == 0); |
|
_ptr.store(nullptr); |
|
delete _counter.load(); |
|
} |
|
} |
|
private: |
|
std::atomic<T*> _ptr; |
|
std::atomic<std::atomic<size_t>*> _counter; |
|
}; |
|
|
|
class Sample { |
|
public: |
|
Sample(): _a(0), _b(0) {} |
|
void set(long a = 0, long b = 0) { |
|
_a = a; |
|
_b = b; |
|
} |
|
|
|
long sum() const { return _a + _b; } |
|
private: |
|
long _a; |
|
long _b; |
|
}; |
|
|
|
#define THREAD_COUNT 10 |
|
|
|
static void doWork(AtomicPointer<Sample> ptr, int times) { |
|
size_t i = 0; |
|
AtomicPointer<Sample> cptr(new Sample()); |
|
printf("Starting do work\n"); |
|
if (times != 0) { |
|
std::thread(doWork, ptr, times - 1).detach(); |
|
} |
|
while (KEEP_GOING) { |
|
ptr->set(1, 3); |
|
long d = ptr->sum(); |
|
ptr.reset(new Sample()); |
|
Sample* nptr = new Sample(); |
|
//AtomicPointer<Sample> foo2(ptr); |
|
ptr.cmp_exchange(ptr.get(), nptr); |
|
if (i % 3 == 0) { |
|
ptr = cptr; |
|
} |
|
++i; |
|
} |
|
} |
|
|
|
int main(int argc, char const *argv[]) |
|
{ |
|
std::puts("Atomic Ref Count"); |
|
Sample* sptr = new Sample(); |
|
AtomicPointer<Sample> aptr(sptr); |
|
std::thread threadList[THREAD_COUNT]; |
|
for (int i = 0; i < THREAD_COUNT; ++i) |
|
{ |
|
printf("Starting a new thread\n"); |
|
threadList[i] = std::thread(doWork, aptr, 5); |
|
} |
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1)); |
|
KEEP_GOING = false; |
|
|
|
for (int i = 0; i < THREAD_COUNT; ++i) |
|
{ |
|
threadList[i].join(); |
|
} |
|
return 0; |
|
} |