diff --git a/include/boost/url/grammar/impl/recycled.hpp b/include/boost/url/grammar/impl/recycled.hpp index b3e8abc0..bc866cb0 100644 --- a/include/boost/url/grammar/impl/recycled.hpp +++ b/include/boost/url/grammar/impl/recycled.hpp @@ -10,102 +10,14 @@ #ifndef BOOST_URL_GRAMMAR_IMPL_RECYCLED_PTR_HPP #define BOOST_URL_GRAMMAR_IMPL_RECYCLED_PTR_HPP +#include + namespace boost { namespace urls { namespace grammar { //------------------------------------------------ -template -recycled_ptr:: -~recycled_ptr() -{ - if(p_) - bin_->release(p_); -} - -template -recycled_ptr:: -recycled_ptr( - recycled& bin) - : bin_(&bin) - , p_(bin.try_acquire()) -{ - if(! p_) - p_ = new U; -} - -template -recycled_ptr:: -recycled_ptr( - recycled& bin, - std::nullptr_t) noexcept - : bin_(&bin) -{ -} - -template -recycled_ptr:: -recycled_ptr() - : recycled_ptr(nullptr) -{ - p_ = bin_->try_acquire(); - if(! p_) - p_ = new U; -} - -template -recycled_ptr:: -recycled_ptr( - std::nullptr_t) noexcept - : recycled_ptr([]() -> B& - { - // VFALCO this needs the guaranteed - // constexpr-init macro treatment - static B r; - return r; - }(), nullptr) -{ -} - -template -recycled_ptr:: -recycled_ptr( - recycled_ptr&& other) noexcept - : bin_(other.bin_) - , p_(other.p_) -{ - other.p_ = nullptr; -} - -template -T& -recycled_ptr:: -acquire() -{ - if(! p_) - { - p_ = bin_->try_acquire(); - if(! p_) - p_ = new U; - } - return p_->t; -} - -template -void -recycled_ptr:: -release() -{ - if(p_) - { - bin_->release(p_); - p_ = nullptr; - } -} - -//------------------------------------------------ - template recycled:: ~recycled() @@ -119,6 +31,8 @@ recycled:: { ++n; auto next = it->next; + BOOST_ASSERT( + it->refs == 0); delete it; it = next; } @@ -129,18 +43,28 @@ recycled:: template auto recycled:: -try_acquire() -> +acquire() -> U* { - std::lock_guard< - std::mutex> lock(m_); - auto p = head_; - if(p) + U* p; { - // recycle - head_ = head_->next; - detail::recycled_remove(sizeof(U)); + std::lock_guard< + std::mutex> lock(m_); + p = head_; + if(p) + { + // reuse + head_ = head_->next; + detail::recycled_remove( + sizeof(U)); + ++p->refs; + } + else + { + p = new U; + } } + BOOST_ASSERT(p->refs == 1); return p; } @@ -149,11 +73,139 @@ void recycled:: release(U* u) noexcept { + if(--u->refs != 0) + return; m_.lock(); u->next = head_; head_ = u; m_.unlock(); - detail::recycled_add(sizeof(U)); + detail::recycled_add( + sizeof(U)); +} + +//------------------------------------------------ + +template +recycled_ptr:: +~recycled_ptr() +{ + if(p_) + bin_->release(p_); +} + +template +recycled_ptr:: +recycled_ptr( + recycled& bin) + : bin_(&bin) + , p_(bin.acquire()) +{ +} + +template +recycled_ptr:: +recycled_ptr( + recycled& bin, + std::nullptr_t) noexcept + : bin_(&bin) +{ +} + +template +recycled_ptr:: +recycled_ptr() + : recycled_ptr(nullptr) +{ + p_ = bin_->acquire(); +} + +template +recycled_ptr:: +recycled_ptr( + std::nullptr_t) noexcept + : recycled_ptr([]() -> B& + { + // VFALCO need guaranteed constexpr-init + static B r; + return r; + }(), nullptr) +{ +} + +template +recycled_ptr:: +recycled_ptr( + recycled_ptr const& other) noexcept + : bin_(other.bin_) + , p_(other.p_) +{ + if(p_) + ++p_->refs; +} + +template +recycled_ptr:: +recycled_ptr( + recycled_ptr&& other) noexcept + : bin_(other.bin_) + , p_(other.p_) +{ + other.p_ = nullptr; +} + +template +auto +recycled_ptr:: +operator=( + recycled_ptr&& other) noexcept -> + recycled_ptr& +{ + BOOST_ASSERT( + bin_ == other.bin_); + if(p_) + bin_->release(p_); + p_ = other.p_; + other.p_ = nullptr; + return *this; +} + +template +auto +recycled_ptr:: +operator=( + recycled_ptr const& other) noexcept -> + recycled_ptr& +{ + BOOST_ASSERT( + bin_ == other.bin_); + if(p_) + bin_->release(p_); + p_ = other.p_; + if(p_) + ++p_->refs; + return *this; +} + +template +T& +recycled_ptr:: +acquire() +{ + if(! p_) + p_ = bin_->acquire(); + return p_->t; +} + +template +void +recycled_ptr:: +release() noexcept +{ + if(p_) + { + bin_->release(p_); + p_ = nullptr; + } } } // grammar diff --git a/include/boost/url/grammar/recycled.hpp b/include/boost/url/grammar/recycled.hpp index c1525016..85ca1cb7 100644 --- a/include/boost/url/grammar/recycled.hpp +++ b/include/boost/url/grammar/recycled.hpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include // ::max_align_t @@ -72,8 +73,8 @@ public: All recycled instances of T are destroyed. Undefined behavior results if there are - any @ref recycled_ptr which reference this - bin. + any @ref recycled_ptr which reference + this recycle bin. */ ~recycled(); @@ -89,11 +90,18 @@ private: { T t; U* next = nullptr; + std::atomic< + std::size_t> refs; + + U() + : refs{1} + { + } }; struct report; - U* try_acquire(); + U* acquire(); void release(U* u) noexcept; U* head_ = nullptr; @@ -102,12 +110,14 @@ private: //------------------------------------------------ -/** A pointer to an instance of T +/** A pointer to shared instance of T - This is a smart pointer container - which acquires an instance of `T` upon - construction. The instance is guaranteed - to be in a valid, but unknown state. + This is a smart pointer container which can + acquire shared ownership of an instance of + `T` upon or after construction. The instance + is guaranteed to be in a valid, but unknown + state. Every recycled pointer references + a valid recycle bin. @par Example @code @@ -119,8 +129,9 @@ private: ps->clear(); @endcode - @tparam T the type of object to acquire, - which must be DefaultConstructible. + @tparam T the type of object to + acquire, which must be + DefaultConstructible. */ template class recycled_ptr @@ -141,20 +152,26 @@ class recycled_ptr public: /** Destructor - If a pointee exists, it is - returned to the recycle bin. + If this is not empty, shared ownership + of the pointee is released. If this was + the last reference, the object is + returned to the original recycle bin. + + @par Effects + @code + this->release(); + @endcode */ ~recycled_ptr(); /** Constructor - Upon construction, the pointer - will acquire exclusive access to - an instance of `T` which is either - recycled from the specified bin, - or newly allocated. The instance - will be guaranteed to be in an - unknown but valid state. + Upon construction, this will acquire + exclusive access to an object of type + `T` which is either recycled from the + specified bin, or newly allocated. + The object will be in an unknown but + valid state. @par Example @code @@ -166,7 +183,12 @@ public: ps->clear(); @endcode - @param bin The bin to use for recycling. + @par Postconditions + @code + &this->bin() == &bin && ! this->empty() + @endcode + + @param bin The recycle bin to use @see @ref recycled. @@ -176,8 +198,8 @@ public: /** Constructor - The container will not hold - any instance of `T`. + After construction, this will be empty + and refer to the specified recycle bin. @par Example @code @@ -190,7 +212,15 @@ public: ps->clear(); @endcode - @param bin The bin to use for recycling. + @par Postconditions + @code + &this->bin() == &bin && this->empty() + @endcode + + @par Exception Safety + Throws nothing. + + @param bin The recycle bin to use @see @ref acquire, @@ -203,14 +233,12 @@ public: /** Constructor - Upon construction, the pointer - will acquire exclusive access to - an instance of `T` which is either - recycled from an implementation-defined - global instance of `recycled`, - or newly allocated. The instance - will be guaranteed to be in an - unknown but valid state. + Upon construction, this will acquire + exclusive access to an object of type + `T` which is either recycled from a + global recycle bin, or newly allocated. + The object will be in an unknown but + valid state. @par Example @code @@ -220,6 +248,11 @@ public: ps->clear(); @endcode + @par Postconditions + @code + &this->bin() != nullptr && ! this->empty() + @endcode + @see @ref recycled. */ @@ -227,8 +260,8 @@ public: /** Constructor - The container will not hold - any instance of `T`. + After construction, this will be empty + and refer to a global recycle bin. @par Example @code @@ -239,6 +272,14 @@ public: ps->clear(); @endcode + @par Postconditions + @code + &this->bin() != nullptr && this->empty() + @endcode + + @par Exception Safety + Throws nothing. + @param bin The bin to use for recycling. @see @@ -252,21 +293,104 @@ public: /** Constructor - After the move, the only valid operation - on the moved-from object is destruction. + If `other` references an object, the + newly constructed pointer will acquire + shared ownership. Otherwise this will + be empty. The new pointer will reference + the same recycle bin as `other`. + + @par Postconditions + @code + &this->bin() == &other->bin() && this->get() == other.get() + @endcode + + @par Exception Safety + Throws nothing. + + @param other The pointer to copy */ recycled_ptr( - recycled_ptr&&) noexcept; + recycled_ptr const& other) noexcept; - /** Assignment (deleted) + /** Constructor + + If `other` references an object, + ownership is transferred including + a reference to the recycle bin. After + the move, the moved-from object will + be empty. + + @par Postconditions + @code + &this->bin() == &other->bin() && ! this->empty() && other.empty() + @endcode + + @par Exception Safety + Throws nothing. + + @param other The pointer to move from */ - recycled_ptr& operator=( - recycled_ptr&&) = delete; + recycled_ptr( + recycled_ptr&& other) noexcept; - /** Return true if this does not own an object + /** Assignment - This can only happen after this - becomes moved-from. + If `other` references an object, + ownership is transferred including + a reference to the recycle bin. After + the move, the moved-from object will + be empty. + + @par Effects + @code + this->release() + @endcode + + @par Postconditions + @code + &this->bin() == &other->bin() + @endcode + + @par Exception Safety + Throws nothing. + + @param other The pointer to move from + */ + recycled_ptr& + operator=( + recycled_ptr&& other) noexcept; + + /** Assignment + + If `other` references an object, + this acquires shared ownership and + references the same recycle bin as + `other`. The previous object if any + is released. + + @par Effects + @code + this->release() + @endcode + + @par Postconditions + @code + &this->bin() == &other->bin() && this->get() == other.get() + @endcode + + @par Exception Safety + Throws nothing. + + @param other The pointer to copy from + */ + recycled_ptr& + operator=( + recycled_ptr const&) noexcept; + + /** Return true if this does not reference an object + + @par Exception Safety + Throws nothing. */ bool empty() const noexcept @@ -274,34 +398,83 @@ public: return p_ == nullptr; } - /** Return the pointed-to object + /** Return the referenced recycle bin + + @par Exception Safety + Throws nothing. + */ + recycled& + bin() const noexcept + { + return *bin_; + } + + /** Return the referenced object + + If this is empty, `nullptr` is returned. + + @par Exception Safety + Throws nothing. */ T* get() const noexcept { return &p_->t; } - /** Return the pointed-to object + /** Return the referenced object + + If this is empty, `nullptr` is returned. + + @par Exception Safety + Throws nothing. */ T* operator->() const noexcept { return get(); } - /** Return the pointed-to object + /** Return the referenced object + + @par Preconditions + @code + not this->empty() + @endcode */ T& operator*() const noexcept { return *get(); } - /** Return the pointed-to object + /** Return the referenced object + + If this references an object, it is + returned. Otherwise, exclusive ownership + of a new object of type `T` is acquired + and returned. + + @par Postconditions + @code + not this->empty() + @endcode */ T& acquire(); - /** Release the pointed-to object + /** Release the referenced object + + If this references an object, it is + released to the referenced recycle bin. + The pointer will continue to reference + the same recycle bin. + + @par Postconditions + @code + this->empty() + @endcode + + @par Exception Safety + Throws nothing. */ - void release(); + void release() noexcept; }; } // grammar