Skip to content

Instantly share code, notes, and snippets.

@EvanBalster
Last active March 6, 2022 23:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save EvanBalster/abb1f16a48d45d48589ab41231559a75 to your computer and use it in GitHub Desktop.
Save EvanBalster/abb1f16a48d45d48589ab41231559a75 to your computer and use it in GitHub Desktop.
A tiny non-blocking reference counter for recyclable objects.
#include <atomic> // Requires C++11 or a suitable polyfill
/*
A reference counting guard for reusable objects.
Works similar to the mechanisms of weak_ptr.
Any number of accessors may visit() the passage if not closed.
After successful entry they should call exit().
*/
struct visitor_guard
{
public:
using int_t = int;
static const int_t
flag_open = int_t(1) << (8*sizeof(int_t) - 2),
flag_locked = int_t(3) << (8*sizeof(int_t) - 2);
static_assert(flag_locked < 0);
static_assert(flag_locked & flag_open);
static_assert((flag_locked + 0xFFFF) & flag_open);
public:
visitor_guard(bool start_open = true) : _x(start_open ? flag_open : 0 ) {}
// Try to enter the passage, returning whether successful.
// visit() succeeds if the passage is open.
// enter() succeeds if the passage is open or closed (not locked).
// join () succeeds if the passage is open or not vacant.
// Call leave() following a success to avoid starving lock().
bool visit() noexcept {if (_x.fetch_add(1)>=flag_open) {return 1;} else {leave(); return 0;}}
bool join () noexcept {if (_x.fetch_add(1)>=1) {return 1;} else {leave(); return 0;}}
bool enter() noexcept {if (_x.fetch_add(1)>=0) {return 1;} else {leave(); return 0;}}
void leave() noexcept {_x.fetch_sub(1);}
// Close or reopen this passage.
void close () noexcept {_x.fetch_and(~flag_open);}
bool reopen() noexcept {return _x.fetch_or(flag_open) > 0;}
// Lock up the passage
// lock() will fail if the passage is open OR has visitors.
// Call unlock(), and perhaps reopen(), to re-enable entry.
bool try_lock() noexcept {int r = 0; return _x.compare_exchange_strong(r, flag_locked);}
void unlock() noexcept {_x.fetch_and(~flag_locked);}
// These functions may be used to observe state,
// But are unsuitable for synchronizing resource access.
bool is_open () const noexcept {return _x.load(std::memory_order_relaxed) >= flag_open;}
bool is_closed() const noexcept {return _x.load(std::memory_order_relaxed) < flag_open;}
bool is_locked() const noexcept {return _x.load(std::memory_order_relaxed) < 0;}
int_t visitors() const noexcept {return _x.load(std::memory_order_relaxed) & ~flag_open;}
bool is_vacant() const noexcept {return (_x.load(std::memory_order_relaxed)|flag_open) == flag_open;}
bool can_lock () const noexcept {return _x.load(std::memory_order_relaxed) == 0;}
private:
// Raw value of this primitive. Don't touch it.
std::atomic<int_t> _x;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment