Skip to content

Instantly share code, notes, and snippets.

@EvanBalster
Last active June 9, 2021 00:01
Show Gist options
  • Save EvanBalster/bda9340d094d6d5e650f2ee144b3b93b to your computer and use it in GitHub Desktop.
Save EvanBalster/bda9340d094d6d5e650f2ee144b3b93b to your computer and use it in GitHub Desktop.
Facilitate weak_ptr to non-heap objects (for AIO / observer patterns)
#pragma once
#include <memory>
#include <thread>
#include <atomic>
/*
weak_anchor is used to produce weak_ptr to a non-heap object.
A shared_ptr derived from these will block the anchor's destruction.
This class was designed for use with asynchronous I/O delegates.
It is particularly lightweight, using a timed backoff spinlock for synchronization.
Consequently, the user should be careful not to retain references too long.
*/
namespace telling
{
template<typename T>
class weak_anchor
{
private:
struct deleter
{
std::atomic<std::atomic_flag*> keep_alive;
deleter() : keep_alive(nullptr) {}
void operator()(T *ptr) const noexcept {keep_alive.load()->clear();}
};
public:
weak_anchor(T *ptr) : expire(0), hold(std::unique_ptr<T,deleter>(ptr, {})) {}
~weak_anchor() noexcept {destroy();}
/*
Get a weak_ptr to the object.
*/
std::weak_ptr<T> get() const noexcept {return hold;}
operator std::weak_ptr<T>() const noexcept {return hold;}
/*
Destroy the reference, waiting for any shared_ptr to expire.
*/
void destroy() noexcept
{
if (hold)
{
// Create a "keep alive" flag; when this is unset, destruction can complete
std::atomic_flag keep_alive;
keep_alive.test_and_set(std::memory_order_release);
std::get_deleter<deleter>(hold)->expire.store(&expire);
hold.reset();
// Spin 16,192 times
for (size_t i = 0; i < 0x4000; ++i)
if (!keep_alive.test_and_set(std::memory_order_acquire)) return;
// Exponential backoff from .000001 to ~.26 seconds
for (size_t i = 0; keep_alive.test_and_set(std::memory_order_acquire); ++i)
std::this_thread::sleep_for(std::chrono::microseconds(1 << std::min<size_t>(i, 18)));
}
}
protected:
std::shared_ptr<T> hold;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment