From fc20a29c991e0b2e6be4b97a2c1e1c8bd304e1d4 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Sun, 8 Sep 2013 17:17:18 +0000 Subject: [PATCH] Merged changes from trunk: added intrusive_ref_counter. [SVN r85609] --- .../boost/smart_ptr/intrusive_ref_counter.hpp | 187 ++++++++++++++++++ intrusive_ptr.html | 4 +- intrusive_ref_counter.html | 95 +++++++++ test/Jamfile.v2 | 1 + test/intrusive_ref_counter_test.cpp | 156 +++++++++++++++ 5 files changed, 442 insertions(+), 1 deletion(-) create mode 100644 include/boost/smart_ptr/intrusive_ref_counter.hpp create mode 100644 intrusive_ref_counter.html create mode 100644 test/intrusive_ref_counter_test.cpp diff --git a/include/boost/smart_ptr/intrusive_ref_counter.hpp b/include/boost/smart_ptr/intrusive_ref_counter.hpp new file mode 100644 index 0000000..82fa8bc --- /dev/null +++ b/include/boost/smart_ptr/intrusive_ref_counter.hpp @@ -0,0 +1,187 @@ +/* + * Copyright Andrey Semashev 2007 - 2013. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file intrusive_ref_counter.hpp + * \author Andrey Semashev + * \date 12.03.2009 + * + * This header contains a reference counter class for \c intrusive_ptr. + */ + +#ifndef BOOST_SMART_PTR_INTRUSIVE_REF_COUNTER_HPP_INCLUDED_ +#define BOOST_SMART_PTR_INTRUSIVE_REF_COUNTER_HPP_INCLUDED_ + +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +#if defined(_MSC_VER) +#pragma warning(push) +// This is a bogus MSVC warning, which is flagged by friend declarations of intrusive_ptr_add_ref and intrusive_ptr_release in intrusive_ref_counter: +// 'name' : the inline specifier cannot be used when a friend declaration refers to a specialization of a function template +// Note that there is no inline specifier in the declarations. +#pragma warning(disable: 4396) +#endif + +namespace boost { + +namespace sp_adl_block { + +/*! + * \brief Thread unsafe reference counter policy for \c intrusive_ref_counter + * + * The policy instructs the \c intrusive_ref_counter base class to implement + * a reference counter suitable for single threaded use only. Pointers to the same + * object with this kind of reference counter must not be used by different threads. + */ +struct thread_unsafe_counter +{ + typedef unsigned int type; + + static unsigned int load(unsigned int const& counter) BOOST_NOEXCEPT + { + return counter; + } + + static void increment(unsigned int& counter) BOOST_NOEXCEPT + { + ++counter; + } + + static unsigned int decrement(unsigned int& counter) BOOST_NOEXCEPT + { + return --counter; + } +}; + +/*! + * \brief Thread safe reference counter policy for \c intrusive_ref_counter + * + * The policy instructs the \c intrusive_ref_counter base class to implement + * a thread-safe reference counter, if the target platform supports multithreading. + */ +struct thread_safe_counter +{ + typedef boost::detail::atomic_count type; + + static unsigned int load(boost::detail::atomic_count const& counter) BOOST_NOEXCEPT + { + return static_cast< unsigned int >(static_cast< long >(counter)); + } + + static void increment(boost::detail::atomic_count& counter) BOOST_NOEXCEPT + { + ++counter; + } + + static unsigned int decrement(boost::detail::atomic_count& counter) BOOST_NOEXCEPT + { + return --counter; + } +}; + +template< typename DerivedT, typename CounterPolicyT = thread_safe_counter > +class intrusive_ref_counter; + +template< typename DerivedT, typename CounterPolicyT > +void intrusive_ptr_add_ref(const intrusive_ref_counter< DerivedT, CounterPolicyT >* p) BOOST_NOEXCEPT; +template< typename DerivedT, typename CounterPolicyT > +void intrusive_ptr_release(const intrusive_ref_counter< DerivedT, CounterPolicyT >* p) BOOST_NOEXCEPT; + +/*! + * \brief A reference counter base class + * + * This base class can be used with user-defined classes to add support + * for \c intrusive_ptr. The class contains a reference counter defined by the \c CounterPolicyT. + * Upon releasing the last \c intrusive_ptr referencing the object + * derived from the \c intrusive_ref_counter class, operator \c delete + * is automatically called on the pointer to the object. + * + * The other template parameter, \c DerivedT, is the user's class that derives from \c intrusive_ref_counter. + */ +template< typename DerivedT, typename CounterPolicyT > +class intrusive_ref_counter +{ +private: + //! Reference counter type + typedef typename CounterPolicyT::type counter_type; + //! Reference counter + mutable counter_type m_ref_counter; + +public: + /*! + * Default constructor + * + * \post use_count() == 0 + */ + intrusive_ref_counter() BOOST_NOEXCEPT : m_ref_counter(0) + { + } + + /*! + * Copy constructor + * + * \post use_count() == 0 + */ + intrusive_ref_counter(intrusive_ref_counter const&) BOOST_NOEXCEPT : m_ref_counter(0) + { + } + + /*! + * Assignment + * + * \post The reference counter is not modified after assignment + */ + intrusive_ref_counter& operator= (intrusive_ref_counter const&) BOOST_NOEXCEPT { return *this; } + + /*! + * \return The reference counter + */ + unsigned int use_count() const BOOST_NOEXCEPT + { + return CounterPolicyT::load(m_ref_counter); + } + +protected: + /*! + * Destructor + */ + BOOST_DEFAULTED_FUNCTION(~intrusive_ref_counter(), {}) + + friend void intrusive_ptr_add_ref< DerivedT, CounterPolicyT >(const intrusive_ref_counter< DerivedT, CounterPolicyT >* p) BOOST_NOEXCEPT; + friend void intrusive_ptr_release< DerivedT, CounterPolicyT >(const intrusive_ref_counter< DerivedT, CounterPolicyT >* p) BOOST_NOEXCEPT; +}; + +template< typename DerivedT, typename CounterPolicyT > +inline void intrusive_ptr_add_ref(const intrusive_ref_counter< DerivedT, CounterPolicyT >* p) BOOST_NOEXCEPT +{ + CounterPolicyT::increment(p->m_ref_counter); +} + +template< typename DerivedT, typename CounterPolicyT > +inline void intrusive_ptr_release(const intrusive_ref_counter< DerivedT, CounterPolicyT >* p) BOOST_NOEXCEPT +{ + if (CounterPolicyT::decrement(p->m_ref_counter) == 0) + delete static_cast< const DerivedT* >(p); +} + +} // namespace sp_adl_block + +using sp_adl_block::intrusive_ref_counter; +using sp_adl_block::thread_unsafe_counter; +using sp_adl_block::thread_safe_counter; + +} // namespace boost + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#endif // BOOST_SMART_PTR_INTRUSIVE_REF_COUNTER_HPP_INCLUDED_ diff --git a/intrusive_ptr.html b/intrusive_ptr.html index 16097dd..562d27a 100644 --- a/intrusive_ptr.html +++ b/intrusive_ptr.html @@ -24,7 +24,9 @@ compilers that support argument-dependent lookup, intrusive_ptr_add_ref and intrusive_ptr_release should be defined in the namespace that corresponds to their parameter; otherwise, the definitions need to go in - namespace boost.

+ namespace boost. The library provides a helper base class template + intrusive_ref_counter which may + help adding support for intrusive_ptr to user's types.

The class template is parameterized on T, the type of the object pointed to. intrusive_ptr<T> can be implicitly converted to intrusive_ptr<U> whenever T* can be implicitly converted to U*.

diff --git a/intrusive_ref_counter.html b/intrusive_ref_counter.html new file mode 100644 index 0000000..72a46e0 --- /dev/null +++ b/intrusive_ref_counter.html @@ -0,0 +1,95 @@ + + + + intrusive_ref_counter + + + +

boost.png (6897 bytes)basic_intrusive_ref_counter class template

+

+ Introduction
+ Synopsis
+ Members
+

+

Introduction

+

The intrusive_ref_counter class template implements a reference counter for a derived + user's class that is intended to be used with intrusive_ptr. + The base class has associated intrusive_ptr_add_ref and intrusive_ptr_release functions + which modify the reference counter as needed and destroy the user's object when the counter drops to zero.

+

The class template is parameterized on DerivedT and CounterPolicyT parameters. + The first parameter is the user's class that derives from intrusive_ref_counter. This type + is needed in order to destroy the object correctly when there are no references to it left.

+

The second parameter is a policy that defines the nature of the reference counter. + Boost.SmartPtr provides two such policies: thread_unsafe_counter and thread_safe_counter. The former + instructs the intrusive_ref_counter base class to use a counter only suitable for a single-threaded use. + Pointers to a single object that uses this kind of reference counter must not be used in different threads. The latter policy + makes the reference counter thread-safe, unless the target platform doesn't support threading. Since in modern systems support for + threading is common, the default counter policy is thread_safe_counter.

+

Synopsis

+
namespace boost {
+
+  struct thread_unsafe_counter;
+  struct thread_safe_counter;
+
+  template<class DerivedT, class CounterPolicyT = thread_safe_counter>
+  class intrusive_ref_counter
+  {
+  public:
+      intrusive_ref_counter() = noexcept;
+      intrusive_ref_counter(intrusive_ref_counter const & r) = noexcept;
+
+      intrusive_ref_counter & operator=(intrusive_ref_counter const & r) noexcept;
+
+      unsigned int use_count() const noexcept;
+
+  protected:
+      ~intrusive_ref_counter() = default;
+  };
+
+}
+

Members

+

constructors

+
intrusive_ref_counter();
+
+

Postconditions: use_count() == 0.

+

Throws: nothing.

+

Notes: The pointer to the constructed object is expected to be passed to intrusive_ptr + constructor, assignment operator or reset() method, which would increment the reference counter.

+
+
intrusive_ref_counter(intrusive_ref_counter const &);
+
+

Postconditions: use_count() == 0.

+

Throws: nothing.

+

Notes: The pointer to the constructed object is expected to be passed to intrusive_ptr + constructor, assignment operator or reset() method, which would increment the reference counter.

+
+

destructor

+
~intrusive_ref_counter();
+
+

Throws: nothing.

+

Effects: Destroys the counter object.

+

Notes: The destructor is protected so that the object can only be destroyed through the DerivedT class.

+
+

assignment

+
intrusive_ref_counter & operator=(intrusive_ref_counter const & r) noexcept;
+
+

Effects: Does nothing, reference counter is not modified.

+

Returns: *this.

+
+

use_count

+
unsigned int use_count() const noexcept;
+
+

Returns: The current value of the reference counter.

+

Throws: nothing.

+

Notes: The returned value may not be actual in multi-threaded applications.

+
+
+

+ $Date$

+

+ Copyright © 2013 Andrey Semashev. Distributed under the Boost Software License, Version + 1.0. See accompanying file LICENSE_1_0.txt or + copy at http://www.boost.org/LICENSE_1_0.txt.

+ + diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index d4f7af1..874968a 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -21,6 +21,7 @@ import testing ; [ run get_deleter_test.cpp ] [ run intrusive_ptr_test.cpp ] [ run intrusive_ptr_move_test.cpp ] + [ run intrusive_ref_counter_test.cpp ] [ run atomic_count_test.cpp ] [ run lw_mutex_test.cpp ] [ compile-fail shared_ptr_assign_fail.cpp ] diff --git a/test/intrusive_ref_counter_test.cpp b/test/intrusive_ref_counter_test.cpp new file mode 100644 index 0000000..53d0bd3 --- /dev/null +++ b/test/intrusive_ref_counter_test.cpp @@ -0,0 +1,156 @@ +/* + * Copyright Andrey Semashev 2013. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file intrusive_ref_counter_test.cpp + * \author Andrey Semashev + * \date 31.08.2013 + * + * This file contains tests for the \c intrusive_ref_counter base class. + */ + +#include + +#if defined(BOOST_MSVC) + +#pragma warning(disable: 4786) // identifier truncated in debug info +#pragma warning(disable: 4710) // function not inlined +#pragma warning(disable: 4711) // function selected for automatic inline expansion +#pragma warning(disable: 4514) // unreferenced inline removed +#pragma warning(disable: 4355) // 'this' : used in base member initializer list +#pragma warning(disable: 4511) // copy constructor could not be generated +#pragma warning(disable: 4512) // assignment operator could not be generated + +#if (BOOST_MSVC >= 1310) +#pragma warning(disable: 4675) // resolved overload found with Koenig lookup +#endif + +#endif + +#include +#include +#include +#include + +namespace N1 { + +class my_class : + public boost::intrusive_ref_counter< my_class > +{ +public: + static unsigned int destructor_count; + + ~my_class() + { + ++destructor_count; + } +}; + +unsigned int my_class::destructor_count = 0; + +} // namespace N1 + +namespace N2 { + +class my_class : + public boost::intrusive_ref_counter< my_class, boost::thread_unsafe_counter > +{ +public: + static unsigned int destructor_count; + + ~my_class() + { + ++destructor_count; + } +}; + +unsigned int my_class::destructor_count = 0; + +} // namespace N2 + +namespace N3 { + +struct root : + public boost::intrusive_ref_counter< root > +{ + virtual ~root() {} +}; + +} // namespace N3 + +namespace N4 { + +struct X : + public virtual N3::root +{ +}; + +} // namespace N4 + +namespace N5 { + +struct Y : + public virtual N3::root +{ +}; + +} // namespace N5 + +namespace N6 { + +struct Z : + public N4::X, + public N5::Y +{ + static unsigned int destructor_count; + + ~Z() + { + ++destructor_count; + } +}; + +unsigned int Z::destructor_count = 0; + +} // namespace N6 + + +int main() +{ + // The test check that ADL works + { + boost::intrusive_ptr< N1::my_class > p = new N1::my_class(); + p = NULL; + BOOST_TEST(N1::my_class::destructor_count == 1); + } + { + boost::intrusive_ptr< N2::my_class > p = new N2::my_class(); + p = NULL; + BOOST_TEST(N2::my_class::destructor_count == 1); + } + { + N1::my_class* p = new N1::my_class(); + intrusive_ptr_add_ref(p); + intrusive_ptr_release(p); + BOOST_TEST(N1::my_class::destructor_count == 2); + } + + // The test checks that destroying through the base class works + { + boost::intrusive_ptr< N6::Z > p1 = new N6::Z(); + BOOST_TEST(p1->use_count() == 1); + BOOST_TEST(N6::Z::destructor_count == 0); + boost::intrusive_ptr< N3::root > p2 = p1; + BOOST_TEST(p1->use_count() == 2); + BOOST_TEST(N6::Z::destructor_count == 0); + p1 = NULL; + BOOST_TEST(N6::Z::destructor_count == 0); + p2 = NULL; + BOOST_TEST(N6::Z::destructor_count == 1); + } + + return boost::report_errors(); +}