diff --git a/openvpn/common/rc.hpp b/openvpn/common/rc.hpp index 17c6eb7a8..e75d75017 100644 --- a/openvpn/common/rc.hpp +++ b/openvpn/common/rc.hpp @@ -93,132 +93,369 @@ namespace openvpn { -// The smart pointer class +/** + @brief The smart pointer class + @tparam T an RC enabled type + + Defines a template class called RCPtr that implements a smart pointer for + reference counted objects. + + RCPtr is a template class, meaning it can be instantiated for any type T that + supports reference counting. It keeps track of a pointer to an object of type T, + and handles incrementing and decrementing the reference count automatically. + + The purpose of RCPtr is to automate reference counting for any reference-counted + object (any class that inherits from RC). It provides a safe way to have multiple + pointers to an object without worrying about memory leaks or double-frees. + + RCPtr has a pointer member variable px that holds a pointer to the T object it + references. It overloads operators like * and -> to dereference the pointer and + access the referenced object. + + The key methods are the constructors and destructor. The constructors increment + the reference count, and the destructor decrements it. This ensures the object + will stay allocated as long as any RCPtr points to it, and be freed when the + last RCPtr is destructed. + + Copy and move constructors increment the refcount before assigning px, while + move assignment operators decrement the old refcount after reassigning px. + + RCPtr is a smart pointer class that automates reference counting for any + reference-counted type T, allowing multiple pointer ownership without leaks or + double-frees. +*/ template class RCPtr { public: typedef T element_type; - RCPtr() noexcept - : px(nullptr) - { - } - - RCPtr(T *p, const bool add_ref = true) noexcept - : px(p) - { - if (px && add_ref) - intrusive_ptr_add_ref(px); - } - - RCPtr(const RCPtr &rhs) noexcept - : px(rhs.px) - { - if (px) - intrusive_ptr_add_ref(px); - } + RCPtr() noexcept; + RCPtr(T *p, const bool add_ref = true) noexcept; + RCPtr(const RCPtr &rhs) noexcept; + RCPtr(RCPtr &&rhs) noexcept; + template + RCPtr(const RCPtr &rhs) noexcept; + ~RCPtr(); - RCPtr(RCPtr &&rhs) noexcept - : px(rhs.px) - { - rhs.px = nullptr; - } + RCPtr &operator=(const RCPtr &rhs) noexcept; + RCPtr &operator=(RCPtr &&rhs) noexcept; - template - RCPtr(const RCPtr &rhs) noexcept - : px(rhs.get()) - { - if (px) - intrusive_ptr_add_ref(px); - } + void reset() noexcept; + void reset(T *rhs) noexcept; + void swap(RCPtr &rhs) noexcept; - ~RCPtr() - { - if (px) - intrusive_ptr_release(px); - } + T *get() const noexcept; + T &operator*() const noexcept; + T *operator->() const noexcept; - RCPtr &operator=(const RCPtr &rhs) noexcept - { - RCPtr(rhs).swap(*this); - return *this; - } + explicit operator bool() const noexcept; + bool operator==(const RCPtr &rhs) const; + bool operator!=(const RCPtr &rhs) const; - RCPtr &operator=(RCPtr &&rhs) noexcept - { - RCPtr(std::move(rhs)).swap(*this); - return *this; - } + RCPtr move_strong() noexcept; + template + RCPtr static_pointer_cast() const noexcept; + template + RCPtr dynamic_pointer_cast() const noexcept; - void reset() noexcept - { - RCPtr().swap(*this); - } + private: + T *px; ///< Pointer to the controlled object +}; +/** + @brief Construct a new RCPtr::RCPtr object + @tparam T an RC enabled type - void reset(T *rhs) noexcept - { - RCPtr(rhs).swap(*this); - } + The default constructor for the RCPtr class - void swap(RCPtr &rhs) noexcept - { - std::swap(px, rhs.px); - } + This constructor initializes the RCPtr class with no referenced object. It sets the + internal px pointer to nullptr. The purpose of this default constructor is to allow + creating an RCPtr instance that doesn't yet reference anything. This is useful when + you need to declare an RCPtr variable but don't have an object to assign it to yet. +*/ +template +RCPtr::RCPtr() noexcept + : px(nullptr){}; +/** + @brief Construct a new RCPtr::RCPtr object + @tparam T an RC enabled type + @param p pointer to an RC enabled object + @param add_ref bool that determines whether the RC of p is incremented - T *get() const noexcept - { - return px; - } + The RCPtr constructor taking a pointer and bool - T &operator*() const noexcept - { - return *px; - } + This constructor initializes an RCPtr instance to point to a provided object pointer p. - T *operator->() const noexcept - { - return px; - } + It takes two inputs: + - p - a pointer to an object of type T that inherits from RC (reference counted). + - add_ref - a bool indicating if the reference count of p should be incremented. - explicit operator bool() const noexcept - { - return px != nullptr; - } + It does not return anything directly. Its purpose is to construct an RCPtr instance. - bool operator==(const RCPtr &rhs) const - { - return px == rhs.px; - } + The key logic is: + - The px member is assigned the provided pointer p. + - If add_ref is true, and px is non-null, the reference count of px is incremented + via intrusive_ptr_add_ref(px). - bool operator!=(const RCPtr &rhs) const - { - return px != rhs.px; - } + This achieves the goal of constructing an RCPtr that points to the provided object pointer + p. If add_ref is true, it will also increment the ref count of p, indicating that RCPtr + now owns a count on that object. +*/ +template +RCPtr::RCPtr(T *p, const bool add_ref) noexcept + : px(p) +{ + if (px && add_ref) + intrusive_ptr_add_ref(px); +} +/** + @brief Copy constructor for RCPtr + @tparam T an RC enabled type + @param rhs the RCPtr to be copied + */ +template +RCPtr::RCPtr(const RCPtr &rhs) noexcept + : px(rhs.px) +{ + if (px) + intrusive_ptr_add_ref(px); +} +/** + @brief Construct a new RCPtr object via move + @tparam T an RC enabled type + @param rhs object from which to move +*/ +template +RCPtr::RCPtr(RCPtr &&rhs) noexcept + : px(rhs.px) +{ + rhs.px = nullptr; +} +/** + @brief Construct a new RCPtr::RCPtr object to type T and make it track an object of type U + @tparam T an RC enabled type + @tparam U an RC enabled type + @param rhs RCPtr pointing to the object the new RCPtr will reference as well + + This achieves the goal of creating an RCPtr that points to the same object as the RCPtr. +*/ +template +template +RCPtr::RCPtr(const RCPtr &rhs) noexcept + : px(rhs.get()) +{ + if (px) + intrusive_ptr_add_ref(px); +} +/** + @brief Destroy the RCPtr::RCPtr object + @tparam T an RC enabled type + + This achieves the goal of reducing the refcount when the RCPtr is destructed, possibly deleting + the object if no other RCPtrs reference it anymore. The key data transformation is decrementing + the refcount via intrusive_ptr_release(px). +*/ +template +RCPtr::~RCPtr() +{ + if (px) + intrusive_ptr_release(px); +} +/** + @brief Assigns an existing RCPtr to point to a different T + @tparam T an RC enabled type + @param rhs other RCPtr + @return reference to this + + Assigns an existing RCPtr to point to a different T, which is already controlled by another + RCPtr. Reduces the refcount on the current T. +*/ +template +RCPtr &RCPtr::operator=(const RCPtr &rhs) noexcept +{ + // notice that RCPtr(rhs) is a temp built from rhs, which will decrement old T when scope ends + RCPtr(rhs).swap(*this); + return *this; +} +/** + @brief Assigns an existing RCPtr to point to a different T using move + @tparam T an RC enabled type + @param rhs other RCPtr + @return reference to this + + Assigns an existing RCPtr to point to a different T using move, by stealing the guts of another + RCPtr. +*/ +template +RCPtr &RCPtr::operator=(RCPtr &&rhs) noexcept +{ + RCPtr(std::move(rhs)).swap(*this); + return *this; +} +/** + @brief Points this RCPtr to nullptr safely + @tparam T an RC enabled type +*/ +template +void RCPtr::reset() noexcept +{ + RCPtr().swap(*this); +} +/** + @brief Points this RCPtr to an RC enabled object T + @note It's critical that the object in question be allocated via new. + @tparam T an RC enabled type + @param rhs pointer to the object that will be managed by this pointer +*/ +template +void RCPtr::reset(T *rhs) noexcept +{ + RCPtr(rhs).swap(*this); +} +/** + @brief swaps the contents of two RCPtr + @tparam T an RC enabled type + @param rhs the other RCPtr +*/ +template +void RCPtr::swap(RCPtr &rhs) noexcept +{ + std::swap(px, rhs.px); +} +/** + @brief Returns the raw pointer to the object T, or nullptr. + @tparam T an RC enabled type + @return T* pointer we are tracking, or nullptr. +*/ +template +T *RCPtr::get() const noexcept +{ + return px; +} +/** + @brief Operator returns a ref to the pointed to T + @tparam T an RC enabled type + @return T& reference to the object this RCPtr points to + + Operator returns a ref to the pointed to T, or if the RCPtr does not point + to a valid T, undefined behavior due to dereference of invalid pointer. This + is identical to the behavior of a C ptr or the STL smart pointers. +*/ +template +T &RCPtr::operator*() const noexcept +{ + return *px; +} +/** + @brief Returns the raw pointer to the object T, or nullptr. + @tparam T an RC enabled type + @return T* pointer we are tracking, or nullptr. +*/ +template +T *RCPtr::operator->() const noexcept +{ + return px; +} +/** + @brief Evaluates to true if the internal pointer is not equal to nullptr + @tparam T an RC enabled type + @return true if internal pointer is not equal to nullptr + @return false if internal pointer is equal to nullptr +*/ +template +RCPtr::operator bool() const noexcept +{ + return px != nullptr; +} +/** + @brief Evaluates to true if the two RCPtr point to the same object. + @note Does not check equality of the pointed two object, rather checks for identity. + @tparam T an RC enabled type + @param rhs other RCPtr + @return true if *this and rhs point to the same instance or both equal nullptr + @return false if *this and rhs point to different instances or do not both equal nullptr +*/ +template +bool RCPtr::operator==(const RCPtr &rhs) const +{ + return px == rhs.px; +} +/** + @brief Evaluates to true if the two RCPtr point to different objects. + @tparam T an RC enabled type + @param rhs other RCPtr + @return true if *this and rhs point to different instances or do not both equal nullptr + @return false if *this and rhs point to the same instance or both equal nullptr +*/ +template +bool RCPtr::operator!=(const RCPtr &rhs) const +{ + return px != rhs.px; +} +/** + @brief Moves ownership of the internal pointer to the returned RCPtr + @tparam T an RC enabled type + @return The new owning RCPtr +*/ +template +RCPtr RCPtr::move_strong() noexcept +{ + T *p = px; + px = nullptr; + return RCPtr(p, false); +} +/** + @brief Returns a RCPtr that points to our T object + @tparam T an RC enabled type + @tparam U an RC enabled type + @return RCPtr that points to the same object this points to + + Performs a static_cast from T * to U * and then wraps the cast pointer in a new RCPtr +*/ +template +template +RCPtr RCPtr::static_pointer_cast() const noexcept +{ + return RCPtr(static_cast(px)); +} +/** + @brief Returns a RCPtr that points to our T object + @tparam T an RC enabled type + @tparam U an RC enabled type + @return RCPtr that points to the same object this points to, or nullptr + + Performs a dynamic_cast from T * to U * and then wraps the cast pointer in a new RCPtr, + or if the dynamic_cast fails the result will equal nullptr cast to U * in a new RCPtr. +*/ +template +template +RCPtr RCPtr::dynamic_pointer_cast() const noexcept +{ + return RCPtr(dynamic_cast(px)); +} +/** + @brief implements a weak pointer for reference counted objects. + @tparam T RCWeak enabled type - RCPtr move_strong() noexcept - { - T *p = px; - px = nullptr; - return RCPtr(p, false); - } + RCWeakPtr takes a template parameter T which is the type of the object it will hold a weak pointer + to. T must be a reference counted type. The purpose of RCWeakPtr is to hold a non-owning pointer to + a reference counted object that can be converted to a strong owning pointer if the object still + exists. It allows having a pointer to an object without affecting its reference count. - template - RCPtr static_pointer_cast() const noexcept - { - return RCPtr(static_cast(px)); - } + RCWeakPtr contains a member variable controller which holds a pointer to the reference count + controller object of the T object it points to. This allows it to query the reference count and + check if the object still exists. - template - RCPtr dynamic_pointer_cast() const noexcept - { - return RCPtr(dynamic_cast(px)); - } + The class provides methods to initialize the weak pointer from a strong pointer or raw pointer to a + T object. This sets the controller to point to the T object's reference count controller. - private: - T *px; -}; + It also provides methods to reset the pointer, check if it has expired (if the object was deleted), + get a strong owning pointer via lock() if the object still exists, and get the reference count. + The key benefit of RCWeakPtr is being able to hold a non-owning pointer to a reference counted object + without affecting its lifetime. It allows referencing the object without incrementing the reference + count and can check if the object was deleted. +*/ template class RCWeakPtr { @@ -227,82 +464,157 @@ class RCWeakPtr public: typedef T element_type; - RCWeakPtr() noexcept - { - } + RCWeakPtr() noexcept; + RCWeakPtr(const Strong &p) noexcept; + RCWeakPtr(T *p) noexcept; - RCWeakPtr(const Strong &p) noexcept - { - if (p) - controller = p->refcount_.controller; - } + void reset(const Strong &p) noexcept; + void reset(T *p) noexcept; + void reset() noexcept; - RCWeakPtr(T *p) noexcept - { - if (p) - controller = p->refcount_.controller; - } - - void reset(const Strong &p) noexcept - { - if (p) - controller = p->refcount_.controller; - else - controller.reset(); - } - - void reset(T *p) noexcept - { - if (p) - controller = p->refcount_.controller; - else - controller.reset(); - } - - void reset() noexcept - { - controller.reset(); - } - - void swap(RCWeakPtr &other) noexcept - { - controller.swap(other.controller); - } - - olong use_count() const noexcept - { - if (controller) - return controller->use_count(); - else - return 0; - } - - bool expired() const noexcept - { - return use_count() == 0; - } - - Strong lock() const noexcept - { - if (controller) - return controller->template lock(); - else - return Strong(); - } - - Strong move_strong() noexcept - { - typename T::Controller::Ptr c; - c.swap(controller); - if (c) - return c->template lock(); - else - return Strong(); - } + void swap(RCWeakPtr &other) noexcept; + olong use_count() const noexcept; + bool expired() const noexcept; + Strong lock() const noexcept; + Strong move_strong() noexcept; private: - typename T::Controller::Ptr controller; + typename T::Controller::Ptr controller; ///< Smart pointer to the T::ControllerF }; +/** + @brief Construct a new RCWeakPtr::RCWeakPtr object + @tparam T RCWeak enabled type +*/ +template +RCWeakPtr::RCWeakPtr() noexcept {}; +/** + @brief Construct a new RCWeakPtr::RCWeakPtr object + @tparam T RCWeak enabled type + @param p RCPtr<> that holds a reference to an RCWeak::Controller +*/ +template +RCWeakPtr::RCWeakPtr(const Strong &p) noexcept +{ + if (p) + controller = p->refcount_.controller; +} +/** + @brief Construct a new RCWeakPtr::RCWeakPtr object + @tparam T RCWeak enabled type + @param p raw pointer to T +*/ +template +RCWeakPtr::RCWeakPtr(T *p) noexcept +{ + if (p) + controller = p->refcount_.controller; +} +/** + @brief Reassign this weak ptr to the object referenced by the given strong (RCPtr) pointer + @tparam T RCWeak enabled type + @param p Strong pointer to an RCWeak enabled object instance +*/ +template +void RCWeakPtr::reset(const Strong &p) noexcept +{ + if (p) + controller = p->refcount_.controller; + else + controller.reset(); +} +/** + @brief Reassign this weak pointer to reference the controller within the specified object + @tparam T RCWeak enabled type + @param p an instance of an RCWeak enabled type +*/ +template +void RCWeakPtr::reset(T *p) noexcept +{ + if (p) + controller = p->refcount_.controller; + else + controller.reset(); +} +/** + @brief remove any existing reference + @tparam T RCWeak enabled type +*/ +template +void RCWeakPtr::reset() noexcept +{ + controller.reset(); +} +/** + @brief Swaps thing pointed to by *this withthing pointed to by other + @tparam T RCWeak enabled type + @param other the WeakPtr with which *this is to be swapped +*/ +template +void RCWeakPtr::swap(RCWeakPtr &other) noexcept +{ + controller.swap(other.controller); +} +/** + @brief Returns count of references to the object + @tparam T RCWeak enabled type + @return olong ref count + + If we point to a controller, we return the object use count for the object the controller + refers to. Otherwise we return zero. +*/ +template +olong RCWeakPtr::use_count() const noexcept +{ + if (controller) + return controller->use_count(); + else + return 0; +} +/** + @brief Returns true if the underlying object is already freed. + @tparam T RCWeak enabled type + @return true if the object has been freed. + @return false if the object still exists. +*/ +template +bool RCWeakPtr::expired() const noexcept +{ + return use_count() == 0; +} +/** + @brief Tries to upgrade the weak reference to a strong reference and returns that result + @tparam T RCWeak enabled type + @return RCWeakPtr::Strong + + If the underlying object has been freed, returns empty Strong ptr, otherwise returns a + Strong referring to the object. +*/ +template +typename RCWeakPtr::Strong RCWeakPtr::lock() const noexcept +{ + if (controller) + return controller->template lock(); + else + return Strong(); +} +/** + @brief Try to move the weak pointer into a strong pointer + @tparam T RCWeak enabled type + @return RCWeakPtr::Strong to the weakly referred to T or nullptr if the T is no longer available + + Releases the weak reference and either takes and returns a strong reference if possible + or nullptr if the lock cannot be accomplished. +*/ +template +typename RCWeakPtr::Strong RCWeakPtr::move_strong() noexcept +{ + typename T::Controller::Ptr c; + c.swap(controller); + if (c) + return c->template lock(); + else + return Strong(); +} /* We're pretty sure these are false positives. They only occur with very specific compiler versions and/or architectures. @@ -323,189 +635,289 @@ class RCWeakPtr #pragma GCC diagnostic warning "-Wstringop-overflow" #endif #endif - +/** + @brief implements a simple reference count for objects. + + The purpose of thread_unsafe_refcount is to keep track of how many references exist to an + object and automatically delete the object when the reference count reaches zero. It + provides methods to increment, decrement, and read the current reference count. + + thread_unsafe_refcount contains a member variable rc which holds the current reference count + number as a type olong. Overall, thread_unsafe_refcount provides simple reference counting + functionality to track object references in a single-threaded context. It could be used to + implement basic automatic memory management based on scope and references for objects. +*/ class thread_unsafe_refcount { public: - thread_unsafe_refcount() noexcept - : rc(olong(0)) - { - } + thread_unsafe_refcount() noexcept; + void operator++() noexcept; + olong operator--() noexcept; + bool inc_if_nonzero() noexcept; + olong use_count() const noexcept; - void operator++() noexcept + static constexpr bool is_thread_safe(); + +#ifdef OPENVPN_RC_NOTIFY + void notify_release() noexcept; +#endif + +#ifdef OPENVPN_RC_NOTIFY + template + class ListHead; +#endif + + private: + thread_unsafe_refcount(const thread_unsafe_refcount &) = delete; + thread_unsafe_refcount &operator=(const thread_unsafe_refcount &) = delete; + + olong rc; ///< The reference count, platform efficient integer type +}; +/** + @brief Construct a new thread unsafe refcount::thread unsafe refcount object + + initializes rc to 0 for a new object with no references. +*/ +inline thread_unsafe_refcount::thread_unsafe_refcount() noexcept + : rc(olong(0)){}; +/** + @brief Increment ref count by 1 +*/ +inline void thread_unsafe_refcount::operator++() noexcept +{ + ++rc; +} +/** + @brief Decrement ref count by 1 + @return olong +*/ +inline olong thread_unsafe_refcount::operator--() noexcept +{ + return --rc; +} +/** + @brief Increments refcount by 1 if refcount is not 0, returns true if it incremented refcount + @return true if the ref count was incremented + @return false if the ref count was not incremented +*/ +inline bool thread_unsafe_refcount::inc_if_nonzero() noexcept +{ + if (rc) { ++rc; + return true; } + else + return false; +} +/** + @brief Returns the internal use count + @return olong ref count +*/ +inline olong thread_unsafe_refcount::use_count() const noexcept +{ + return rc; +} +/** + @brief Returns false for this type + @return false + + This allows a uniform way to check at compile time or runtime and determine if the + refcount type is thread safe or not. This one is not. +*/ +inline constexpr bool thread_unsafe_refcount::is_thread_safe() +{ + return false; +} - olong operator--() noexcept - { - return --rc; - } +#ifdef OPENVPN_RC_NOTIFY +inline void thread_unsafe_refcount::notify_release() noexcept +{ +} +#endif - bool inc_if_nonzero() noexcept +#ifdef OPENVPN_RC_NOTIFY +template +class thread_unsafe_refcount::ListHead +{ + public: + ListHead() noexcept + : ptr(nullptr) { - if (rc) - { - ++rc; - return true; - } - else - return false; } - olong use_count() const noexcept + T *load() noexcept { - return rc; + return ptr; } - static constexpr bool is_thread_safe() + void insert(T *item) noexcept { - return false; + item->next = ptr; + ptr = item; } -#ifdef OPENVPN_RC_NOTIFY - void notify_release() noexcept - { - } -#endif + private: + ListHead(const ListHead &) = delete; + ListHead &operator=(const ListHead &) = delete; -#ifdef OPENVPN_RC_NOTIFY - template - class ListHead - { - public: - ListHead() noexcept - : ptr(nullptr) - { - } + T *ptr; +}; +#endif - T *load() noexcept - { - return ptr; - } +/** + @brief Implements a memory fenced ref count +*/ +class thread_safe_refcount +{ + public: + thread_safe_refcount() noexcept; + void operator++() noexcept; + olong operator--() noexcept; - void insert(T *item) noexcept - { - item->next = ptr; - ptr = item; - } + bool inc_if_nonzero() noexcept; + olong use_count() const noexcept; + static constexpr bool is_thread_safe(); - private: - ListHead(const ListHead &) = delete; - ListHead &operator=(const ListHead &) = delete; +#ifdef OPENVPN_RC_NOTIFY + void notify_release() noexcept; +#endif - T *ptr; - }; +#ifdef OPENVPN_RC_NOTIFY + template + class ListHead; #endif private: - thread_unsafe_refcount(const thread_unsafe_refcount &) = delete; - thread_unsafe_refcount &operator=(const thread_unsafe_refcount &) = delete; + thread_safe_refcount(const thread_safe_refcount &) = delete; + thread_safe_refcount &operator=(const thread_safe_refcount &) = delete; - olong rc; + std::atomic rc; }; - -class thread_safe_refcount +/** + @brief Construct a new thread safe refcount object +*/ +inline thread_safe_refcount::thread_safe_refcount() noexcept + : rc(olong(0)) { - public: - thread_safe_refcount() noexcept - : rc(olong(0)) +} +/** + @brief Atomically increment the refcount by 1 +*/ +inline void thread_safe_refcount::operator++() noexcept +{ + rc.fetch_add(1, std::memory_order_relaxed); +} +/** + @brief Atomically decrement the internal counter by 1 + @return olong decremented ref count +*/ +inline olong thread_safe_refcount::operator--() noexcept +{ + // http://www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html + const olong ret = rc.fetch_sub(1, std::memory_order_release) - 1; + if (ret == 0) + std::atomic_thread_fence(std::memory_order_acquire); + return ret; +} +/** + @brief Atomically increments refcount by 1 if refcount is not 0, returns true if it incremented refcount + @return true if refcount was incremented + @return false if refcount was not incremented + + If refcount is 0, do nothing and return false. If refcount != 0, increment it and return true. +*/ +inline bool thread_safe_refcount::inc_if_nonzero() noexcept +{ + olong previous = rc.load(std::memory_order_relaxed); + while (true) { + if (!previous) + break; + if (rc.compare_exchange_weak(previous, previous + 1, std::memory_order_relaxed)) + break; } + return previous > 0; +} +/** + @brief Returns the internal use count + @return olong ref count +*/ +inline olong thread_safe_refcount::use_count() const noexcept +{ + return rc.load(std::memory_order_relaxed); +} +/** + @brief Returns true for this class + @return true + + This allows a uniform way to check at compile time or runtime and determine if the + refcount type is thread safe or not. This one is thread safe. +*/ +inline constexpr bool thread_safe_refcount::is_thread_safe() +{ + return true; +} - void operator++() noexcept - { - rc.fetch_add(1, std::memory_order_relaxed); - } +#ifdef OPENVPN_RC_NOTIFY +inline void thread_safe_refcount::notify_release() noexcept +{ +} +#endif - olong operator--() noexcept +#ifdef OPENVPN_RC_NOTIFY +template +class thread_safe_refcount::ListHead +{ + public: + ListHead() noexcept + : ptr(nullptr) { - // http://www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html - const olong ret = rc.fetch_sub(1, std::memory_order_release) - 1; - if (ret == 0) - std::atomic_thread_fence(std::memory_order_acquire); - return ret; } - // If refcount is 0, do nothing and return false. - // If refcount != 0, increment it and return true. - bool inc_if_nonzero() noexcept + T *load() noexcept { - olong previous = rc.load(std::memory_order_relaxed); - while (true) - { - if (!previous) - break; - if (rc.compare_exchange_weak(previous, previous + 1, std::memory_order_relaxed)) - break; - } - return previous > 0; + return ptr; } - olong use_count() const noexcept + void insert(T *item) noexcept { - return rc.load(std::memory_order_relaxed); + item->next = ptr; + ptr = item; } - static constexpr bool is_thread_safe() - { - return true; - } + private: + ListHead(const ListHead &) = delete; + ListHead &operator=(const ListHead &) = delete; -#ifdef OPENVPN_RC_NOTIFY - void notify_release() noexcept - { - } + T *ptr; +}; #endif -#ifdef OPENVPN_RC_NOTIFY - template - class ListHead - { - public: - ListHead() noexcept - : ptr(nullptr) - { - } - - T *load() noexcept - { - return ptr.load(std::memory_order_relaxed); - } - void insert(T *item) noexcept - { - T *previous = ptr.load(std::memory_order_relaxed); - while (true) - { - item->next = previous; - if (ptr.compare_exchange_weak(previous, item, std::memory_order_relaxed)) - break; - } - } +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif - private: - ListHead(const ListHead &) = delete; - ListHead &operator=(const ListHead &) = delete; +/** + @brief Reference count base class for objects tracked by RCPtr. Disallows copying and assignment. - std::atomic ptr; - }; -#endif + Implements basic reference counting functionality for objects. - private: - thread_safe_refcount(const thread_safe_refcount &) = delete; - thread_safe_refcount &operator=(const thread_safe_refcount &) = delete; + The purpose of RC is to provide a base class that other classes can inherit from to enable + reference counting and automatic memory management. It takes a template parameter RCImpl which + specifies the actual reference count implementation class that will be used, either + thread_safe_refcount or thread_unsafe_refcount. - std::atomic rc; -}; + RC provides a common base class for enabling reference counting on other classes via inheritance. + It delegates the actual reference count tracking to the RCImpl implementation specified as a + template parameter. It also prohibits copy or assignment of the inheriting object. -#if !defined(__clang__) && defined(__GNUC__) -#pragma GCC diagnostic pop -#endif + The member functions just delegate to the refcount_ object that's injected via template. -// Reference count base class for objects tracked by RCPtr. -// Disallows copying and assignment. -template // RCImpl = thread_safe_refcount or thread_unsafe_refcount + @tparam RCImpl The ref count implementation that will be used - thread_safe_refcount or thread_unsafe_refcount +*/ +template class RC { public: @@ -517,15 +929,8 @@ class RC RC(const RC &) = delete; RC &operator=(const RC &) = delete; - olong use_count() const noexcept - { - return refcount_.use_count(); - } - - static constexpr bool is_thread_safe() - { - return RCImpl::is_thread_safe(); - } + olong use_count() const noexcept; + static constexpr bool is_thread_safe(); private: template @@ -534,48 +939,63 @@ class RC friend void intrusive_ptr_release(R *p) noexcept; RCImpl refcount_; }; - -// Like RC, but allows object to be copied and assigned. -template // RCImpl = thread_safe_refcount or thread_unsafe_refcount -class RCCopyable +/** + @brief Delegates call to RCImpl and returns the result + @tparam RCImpl a suitable ref count implementation + @return olong ref count +*/ +template +olong RC::use_count() const noexcept { - public: - RCCopyable() noexcept - { - } - - // Copy/move construction/assignment are no-ops because - // we always want to default-construct the refcount from - // scratch. We never want to actually copy or move - // the refcount because that would break any - // referencing smart pointers. + return refcount_.use_count(); +} +/** + @brief Delegates call to RCImpl and returns the result + @tparam RCImpl a suitable ref count implementation + @return true if the underlying RCImpl is thread safe + @return false if the underlying RCImpl is not thread safe +*/ +template +constexpr bool RC::is_thread_safe() +{ + return RCImpl::is_thread_safe(); +} - RCCopyable(const RCCopyable &) noexcept - { - } +/** + @brief Reference count base class for objects tracked by RCPtr. Allows copying and assignment. + @tparam RCImpl The ref count implementation that will be used - thread_safe_refcount or thread_unsafe_refcount - RCCopyable(RCCopyable &&) noexcept - { - } + Implements basic reference counting functionality for objects plus copy/assign. - RCCopyable &operator=(const RCCopyable &) noexcept - { - return *this; - } + The purpose of RCCopyable is to provide a base class that other classes can inherit from to + enable reference counting and automatic memory management. It takes a template parameter + RCImpl which specifies the actual reference count implementation class that will be used, either + thread_safe_refcount or thread_unsafe_refcount. - RCCopyable &operator=(RCCopyable &&) noexcept - { - return *this; - } + RCCopyable provides a common base class for enabling reference counting on other classes via + inheritance. It delegates the actual reference count tracking to the RCImpl implementation + specified as a template parameter. - virtual ~RCCopyable() - { - } + Since copy and assignment are allowed and this is a reference counted type, some atypical logic + is implemented to make those operations work properly. This means that while the rest of the + inheriting object will be copied as per usual, for this object we will give the copy a fresh + reference count, since the count is bookkeeping metadata. The referencing pointers to the individual + objects will then take care of properly maintaining the RC of the new and the donor object as + per usual. - olong use_count() const noexcept - { - return refcount_.use_count(); - } + The member functions just delegate to the refcount_ object that's injected via template. +*/ +template +class RCCopyable +{ + public: + virtual ~RCCopyable(){}; + RCCopyable() noexcept {}; + RCCopyable(const RCCopyable &) noexcept; + RCCopyable(RCCopyable &&) noexcept; + RCCopyable &operator=(const RCCopyable &) noexcept; + RCCopyable &operator=(RCCopyable &&) noexcept; + olong use_count() const noexcept; private: template @@ -585,7 +1005,97 @@ class RCCopyable RCImpl refcount_; }; -// Like RC, but also allows weak pointers and release notification callables +/** + @brief Construct a new RCCopyable object + @tparam RCImpl RC compatible type to use as ref count + + Copy/move construction/assignment are no-ops because we always want to + default-construct the refcount from scratch. We never want to actually copy + or move the refcount because that would break any referencing smart pointers. + + We make a fresh object (by copy/move/assignment) and we want a new ref count. +*/ +template +RCCopyable::RCCopyable(const RCCopyable &) noexcept {}; +/** + @brief Construct a new RCCopyable object by move + @tparam RCImpl RC compatible type to use as ref count + + Copy/move construction/assignment are no-ops because we always want to + default-construct the refcount from scratch. We never want to actually copy + or move the refcount because that would break any referencing smart pointers. + + We make a fresh object (by copy/move/assignment) and we want a new ref count. +*/ +template +RCCopyable::RCCopyable(RCCopyable &&) noexcept {}; +/** + @brief Ensures the new ref count is not copied with the rest of the object. + @tparam RCImpl + @return RCCopyable& + + Copy/move construction/assignment are no-ops because we always want to + default-construct the refcount from scratch. We never want to actually copy + or move the refcount because that would break any referencing smart pointers. + + We make a fresh object (by copy/move/assignment) and we want a new ref count. +*/ +template +RCCopyable &RCCopyable::operator=(const RCCopyable &) noexcept +{ + return *this; +} +/** + @brief Ensures the new ref count is not moved with the rest of the object. + @tparam RCImpl + @return RCCopyable& + + Copy/move construction/assignment are no-ops because we always want to + default-construct the refcount from scratch. We never want to actually copy + or move the refcount because that would break any referencing smart pointers. + + We make a fresh object (by copy/move/assignment) and we want a new ref count. +*/ +template +RCCopyable &RCCopyable::operator=(RCCopyable &&) noexcept +{ + return *this; +} +/** + @brief Returns the use count as reported by defering to the injected ref count type. + @tparam RCImpl the injected ref count implementation + @return olong current ref count +*/ +template +olong RCCopyable::use_count() const noexcept +{ + return refcount_.use_count(); +} + +/** + @brief Reference count base class for objects tracked by RCPtr. Like RC, but also allows weak + pointers and release notification callables. + @tparam RCImpl The ref count implementation that will be used - thread_safe_refcount or thread_unsafe_refcount + + Implements basic reference counting functionality for objects plus weak pointer capability. + + The purpose of RCWeak is to provide a base class that other classes can inherit from to + enable reference counting and automatic memory management. It takes a template parameter + RCImpl which specifies the actual reference count implementation class that will be used, + either thread_safe_refcount or thread_unsafe_refcount. + + RCWeak provides a common base class for enabling reference counting on other classes via + inheritance. It delegates the actual reference count tracking to the RCImpl implementation + specified as a template parameter. It also disables copy but enables move. + + Nested classes are used to facilitate the requirements of weak pointers and those classes + are documented seperately. The member functions just delegate to the refcount_ object + that's injected via template, with the previously mentionend nested classes changing the + way ref counts work as required. + + @see RCWeak::Controller for more details + @see RCWeak::ControllerRef for more details +*/ template // RCImpl = thread_safe_refcount or thread_unsafe_refcount class RCWeak { @@ -594,195 +1104,313 @@ class RCWeak #ifdef OPENVPN_RC_NOTIFY // Base class of release notification callables - class NotifyBase - { - public: - NotifyBase() noexcept - { - } - virtual void call() noexcept = 0; - virtual ~NotifyBase() - { - } - NotifyBase *next = nullptr; + class NotifyBase; + // A release notification callable + template + class NotifyItem; + // Head of a linked-list of release notification callables + class NotifyListHead; +#endif - private: - NotifyBase(const NotifyBase &) = delete; - NotifyBase &operator=(const NotifyBase &) = delete; - }; + struct Controller; + struct ControllerRef; + + public: + typedef RCPtr Ptr; + typedef RCWeakPtr WPtr; + + RCWeak() noexcept + : refcount_(this){}; + + virtual ~RCWeak() = default; + RCWeak(const RCWeak &) = delete; + RCWeak &operator=(const RCWeak &) = delete; + +#ifdef OPENVPN_RC_NOTIFY + // Add observers to be called just prior to object deletion, + // but after refcount has been decremented to 0. At this + // point, all weak pointers have expired, and no strong + // pointers are outstanding. Callables can access the + // object by raw pointer but must NOT attempt to create a + // strong pointer referencing the object. - // A release notification callable template - class NotifyItem : public NotifyBase + void rc_release_notify(const CALLABLE &c) noexcept { - public: - NotifyItem(const CALLABLE &c) noexcept - : callable(c) - { - } + refcount_.notify.add(c); + } - NotifyItem(CALLABLE &&c) noexcept - : callable(std::move(c)) - { - } + template + void rc_release_notify(CALLABLE &&c) noexcept + { + refcount_.notify.add(std::move(c)); + } +#endif - private: - virtual void call() noexcept override - { - callable(); - } + private: + template + friend void intrusive_ptr_add_ref(R *rcptr) noexcept; + template + friend void intrusive_ptr_release(R *rcptr) noexcept; - CALLABLE callable; - }; + ControllerRef refcount_; ///< Adapter instance that allows RCPtr to properly interact with RCWeak +}; - // Head of a linked-list of release notification callables - class NotifyListHead - { - public: - NotifyListHead() noexcept - { - } +/** + @brief Controller structure for our ptr/weakptr implementation + @tparam RCImpl The ref count implementation - template - void add(const CALLABLE &c) noexcept - { - NotifyBase *item = new NotifyItem(c); - head.insert(item); - } + For weak-referenceable objects, we must detach the refcount from the object and place it + in Controller. - template - void add(CALLABLE &&c) noexcept - { - NotifyBase *item = new NotifyItem(std::move(c)); - head.insert(item); - } + This struct implements 3 essential elements: - void release() noexcept - { - // In thread-safe mode, list traversal is guaranteed to be - // contention-free because we are not called until refcount - // reaches zero and after a std::memory_order_acquire fence. - NotifyBase *nb = head.load(); - while (nb) - { - NotifyBase *next = nb->next; - nb->call(); - delete nb; - nb = next; - } - } + - A pointer to the parent (RCWeak enabled) object. + - A ref count on Controller via RC inheritance. + - A ref count inside Controller via the RCImpl member 'rc'. - private: - NotifyListHead(const NotifyListHead &) = delete; - NotifyListHead &operator=(const NotifyListHead &) = delete; + They are used as follows. - typename RCImpl::template ListHead head; - }; -#endif + Controller::rc is the ref count of the object, and the inherited RCImpl is the ref count of the + Controller itself. The 'parent' pointer is used to form a Strong (owning) pointer on demand if + the Controller::rc refcount is greater than zero, or if not a nullptr Strong is returned. - // For weak-referenceable objects, we must detach the - // refcount from the object and place it in Controller. - struct Controller : public RC - { - typedef RCPtr Ptr; + An RCPtr<> will call intrusive_ptr_add_ref and intrusive_ptr_release helpers which will + increment and decrement refcount_, which for RCWeak is an instance of RCWeak<>::ControllerRef. + This ControllerRef instance will delegate those operations to the 'controller' member, which + is an RCWeak<>::Controller referenced via an RCPtr<> instance inside ControllerRef. This is + where the composed 'rc' refcount is modified. - Controller(RCWeak *parent_arg) noexcept - : parent(parent_arg) - { - } + The inherited RCImpl is controlled by the 'Controller::Ptr controller' member in ControllerRef + via the member refcount_ and by the RCWeakPtr reaching in and manipulating it. Thus the object + itself has 1 refcount on the controller and each weak ptr also holds a refcount on the controller. - olong use_count() const noexcept - { - return rc.use_count(); - } + When the last RCPtr on the object goes out of scope and decrements Controller::rc to zero, the + object will be destroyed. If the only refcount on the controller is the ControllerRef, then the + Controller instance will also be destroyed at this time. Otherwise the weak pointers to the + controller will control that object's lifespan, but no further Strong pointers to the (now + nonexistant) object will be allowed. This test for zero is what allows 'parent' to dangle + harmlessly in some cases. +*/ +template +struct RCWeak::Controller : public RC +{ + typedef RCPtr Ptr; - template - PTR lock() noexcept - { - if (rc.inc_if_nonzero()) - return PTR(static_cast(parent), false); - else - return PTR(); - } + Controller(RCWeak *parent_arg) noexcept; - RCWeak *const parent; // dangles (harmlessly) after rc decrements to 0 - RCImpl rc; // refcount - }; + olong use_count() const noexcept; - struct ControllerRef - { - explicit ControllerRef(RCWeak *parent) noexcept - : controller(new Controller(parent)) - { - } + template + PTR lock() noexcept; - void operator++() noexcept - { - ++controller->rc; - } + RCWeak *const parent; ///< Pointer to object, dangles (harmlessly) after rc decrements to 0 + RCImpl rc; ///< refcount implementation for 'parent' object +}; +/** + @brief Construct a new RCWeak::Controller object + @tparam RCImpl The ref count implementation + @param parent_arg raw pointer to the RCWeak enabled object instance +*/ +template +RCWeak::Controller::Controller(RCWeak *parent_arg) noexcept + : parent(parent_arg){}; +/** + @brief Get the use count of the underlying ref count implementation + @tparam RCImpl the ref count implementation + @return olong ref count +*/ +template +olong RCWeak::Controller::use_count() const noexcept +{ + return rc.use_count(); +} +/** + @brief Used internally to try a to get a pointer to the controlled instance + @tparam RCImpl the injected ref count implementation + @tparam PTR explicit return type + @return PTR pointer type referring to either the weakly ref'd object or nullptr + + Will return the requested pointer to the object if the ref count could be incremented, + or the specified pointer type set to nullptr +*/ +template +template +PTR RCWeak::Controller::lock() noexcept +{ + if (rc.inc_if_nonzero()) + return PTR(static_cast(parent), false); + else + return PTR(); +} - olong operator--() noexcept - { - return --controller->rc; - } +/** + @brief Adapter object for RCWeak::Controller <---> RCPtr + @tparam RCImpl the injected ref count implementation + + Serves as an adapter that changes the normal RCPtr/RC interaction such that the embedded controller + rc member is used as the ref count. +*/ +template +struct RCWeak::ControllerRef +{ + explicit ControllerRef(RCWeak *parent) noexcept; + + void operator++() noexcept; + olong operator--() noexcept; #ifdef OPENVPN_RC_NOTIFY - void notify_release() noexcept - { - notify.release(); - } + void notify_release() noexcept; #endif - typename Controller::Ptr controller; // object containing actual refcount + typename Controller::Ptr controller; ///< object containing actual refcount #ifdef OPENVPN_RC_NOTIFY - NotifyListHead notify; // linked list of callables to be notified on object release + NotifyListHead notify; // linked list of callables to be notified on object release +#endif +}; +/** + @brief Construct a new RCWeak::ControllerRef object + @tparam RCImpl The ref count implementation + @param parent raw pointer to RCWeak enabled type + + Creates a ControllerRef and associates that adapter with the specified instance +*/ +template +RCWeak::ControllerRef::ControllerRef(RCWeak *parent) noexcept + : controller(new Controller(parent)){}; +/** + @brief Increments the ref count + @tparam RCImpl the injected ref count implementation + + The adapter changes the usual implementation of this operator to increment a ref count 'rc' + that's inside the controller object for the controlled instance. +*/ +template +void RCWeak::ControllerRef::operator++() noexcept +{ + ++controller->rc; +} +/** + @brief Decrements the ref count + @tparam RCImpl the injected ref count implementation + + The adapter changes the usual implementation of this operator to decrement a ref count 'rc' + that's inside the controller object for the controlled instance. +*/ +template +olong RCWeak::ControllerRef::operator--() noexcept +{ + return --controller->rc; +} + +#ifdef OPENVPN_RC_NOTIFY +template +void RCWeak::ControllerRef::notify_release() noexcept +{ + notify.release(); +} #endif - }; +#ifdef OPENVPN_RC_NOTIFY +// Base class of release notification callables +template +class RCWeak::NotifyBase +{ public: - typedef RCPtr Ptr; - typedef RCWeakPtr WPtr; + NotifyBase() noexcept + { + } + virtual void call() noexcept = 0; + virtual ~NotifyBase() + { + } + NotifyBase *next = nullptr; - RCWeak() noexcept - : refcount_(this) + private: + NotifyBase(const NotifyBase &) = delete; + NotifyBase &operator=(const NotifyBase &) = delete; +}; + +// A release notification callable +template +template +class RCWeak::NotifyItem : public NotifyBase +{ + public: + NotifyItem(const CALLABLE &c) noexcept + : callable(c) { } - virtual ~RCWeak() = default; - RCWeak(const RCWeak &) = delete; - RCWeak &operator=(const RCWeak &) = delete; + NotifyItem(CALLABLE &&c) noexcept + : callable(std::move(c)) + { + } -#ifdef OPENVPN_RC_NOTIFY - // Add observers to be called just prior to object deletion, - // but after refcount has been decremented to 0. At this - // point, all weak pointers have expired, and no strong - // pointers are outstanding. Callables can access the - // object by raw pointer but must NOT attempt to create a - // strong pointer referencing the object. + private: + virtual void call() noexcept override + { + callable(); + } + + CALLABLE callable; +}; + +// Head of a linked-list of release notification callables +template +class RCWeak::NotifyListHead +{ + public: + NotifyListHead() noexcept + { + } template - void rc_release_notify(const CALLABLE &c) noexcept + void add(const CALLABLE &c) noexcept { - refcount_.notify.add(c); + NotifyBase *item = new NotifyItem(c); + head.insert(item); } template - void rc_release_notify(CALLABLE &&c) noexcept + void add(CALLABLE &&c) noexcept { - refcount_.notify.add(std::move(c)); + NotifyBase *item = new NotifyItem(std::move(c)); + head.insert(item); + } + + void release() noexcept + { + // In thread-safe mode, list traversal is guaranteed to be + // contention-free because we are not called until refcount + // reaches zero and after a std::memory_order_acquire fence. + NotifyBase *nb = head.load(); + while (nb) + { + NotifyBase *next = nb->next; + nb->call(); + delete nb; + nb = next; + } } -#endif private: - template - friend void intrusive_ptr_add_ref(R *rcptr) noexcept; - template - friend void intrusive_ptr_release(R *rcptr) noexcept; + NotifyListHead(const NotifyListHead &) = delete; + NotifyListHead &operator=(const NotifyListHead &) = delete; - ControllerRef refcount_; + typename RCImpl::template ListHead head; }; - +#endif +/** + @brief Helper to increment a ref count + @tparam R type that has an incrementable member refcount_ + @param rcptr pointer to instance of R + @todo consider removing debug cout + + Helper function template to implement incrementing of a member 'refcount_' of a type R; acts as an + adapter layer to implement this funtionality as well as some conditionally built debug logging. +*/ template inline void intrusive_ptr_add_ref(R *rcptr) noexcept { @@ -791,7 +1419,16 @@ inline void intrusive_ptr_add_ref(R *rcptr) noexcept #endif ++rcptr->refcount_; } - +/** + @brief Helper to decrement a ref count + @tparam R type that has an decrementable member refcount_ + @param rcptr pointer to instance of R + @todo consider removing debug cout + + Helper function template to implement decrementing of a member 'refcount_' of a type R; acts as an + adapter layer to implement this funtionality as well as some conditionally built debug logging and + a conditionally built notify hook. +*/ template inline void intrusive_ptr_release(R *rcptr) noexcept {