From 1b69c14f453926e2fc9f3a29c2c04bcdfe0076ec Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Tue, 12 Feb 2002 16:55:25 +0000 Subject: [PATCH] weak_ptr made thread safe, shared->weak conversions, lightweight_mutex added. [SVN r12786] --- include/boost/detail/lightweight_mutex.hpp | 49 +++++++++++++ include/boost/detail/lwm_nop.hpp | 57 +++++++++++++++ include/boost/detail/lwm_pthreads.hpp | 78 ++++++++++++++++++++ include/boost/detail/lwm_win32.hpp | 84 ++++++++++++++++++++++ include/boost/detail/shared_count.hpp | 60 +++++++++++++--- include/boost/shared_ptr.hpp | 18 +++++ include/boost/weak_ptr.hpp | 58 +++++++++------ shared_ptr_test.cpp | 68 ++++++++++++++---- 8 files changed, 429 insertions(+), 43 deletions(-) create mode 100644 include/boost/detail/lightweight_mutex.hpp create mode 100644 include/boost/detail/lwm_nop.hpp create mode 100644 include/boost/detail/lwm_pthreads.hpp create mode 100644 include/boost/detail/lwm_win32.hpp diff --git a/include/boost/detail/lightweight_mutex.hpp b/include/boost/detail/lightweight_mutex.hpp new file mode 100644 index 0000000..9f80b00 --- /dev/null +++ b/include/boost/detail/lightweight_mutex.hpp @@ -0,0 +1,49 @@ +#ifndef BOOST_DETAIL_LIGHTWEIGHT_MUTEX_HPP_INCLUDED +#define BOOST_DETAIL_LIGHTWEIGHT_MUTEX_HPP_INCLUDED + +#if _MSC_VER >= 1020 +#pragma once +#endif + +// +// boost/detail/lightweight_mutex.hpp - lightweight mutex +// +// Copyright (c) 2002 Peter Dimov and Multi Media Ltd. +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +// typedef boost::detail::lightweight_mutex; +// +// boost::detail::lightweight_mutex meets the Mutex concept requirements +// See http://www.boost.org/libs/thread/doc/mutex_concept.html#Mutex +// +// * Used by the smart pointer library +// * Performance oriented +// * Header-only implementation +// * Small memory footprint +// * Not a general purpose mutex, use boost::mutex, CRITICAL_SECTION or +// pthread_mutex instead. +// * Never spin in a tight lock/do-something/unlock loop, since +// lightweight_mutex does not guarantee fairness. +// * Never keep a lightweight_mutex locked for long periods. +// + + +#include + +#ifndef BOOST_HAS_THREADS +# include +#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# include +//#elif defined(linux) || defined(__linux) || defined(__linux__) +//# include +#elif defined(BOOST_HAS_PTHREADS) +# include +#else +# include +#endif + +#endif // #ifndef BOOST_DETAIL_LIGHTWEIGHT_MUTEX_HPP_INCLUDED diff --git a/include/boost/detail/lwm_nop.hpp b/include/boost/detail/lwm_nop.hpp new file mode 100644 index 0000000..1d798e0 --- /dev/null +++ b/include/boost/detail/lwm_nop.hpp @@ -0,0 +1,57 @@ +#ifndef BOOST_DETAIL_LWM_NOP_HPP_INCLUDED +#define BOOST_DETAIL_LWM_NOP_HPP_INCLUDED + +#if _MSC_VER >= 1020 +#pragma once +#endif + +// +// boost/detail/lwm_nop.hpp +// +// Copyright (c) 2002 Peter Dimov and Multi Media Ltd. +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// + +namespace boost +{ + +namespace detail +{ + +class lightweight_mutex +{ +private: + + lightweight_mutex(lightweight_mutex const &); + lightweight_mutex & operator=(lightweight_mutex const &); + +public: + + lightweight_mutex() + { + } + + class scoped_lock + { + private: + + scoped_lock(scoped_lock const &); + scoped_lock & operator=(scoped_lock const &); + + public: + + explicit scoped_lock(lightweight_mutex &) + { + } + }; +}; + +} // namespace detail + +} // namespace boost + +#endif // #ifndef BOOST_DETAIL_LWM_NOP_HPP_INCLUDED diff --git a/include/boost/detail/lwm_pthreads.hpp b/include/boost/detail/lwm_pthreads.hpp new file mode 100644 index 0000000..ba8d762 --- /dev/null +++ b/include/boost/detail/lwm_pthreads.hpp @@ -0,0 +1,78 @@ +#ifndef BOOST_DETAIL_LWM_PTHREADS_HPP_INCLUDED +#define BOOST_DETAIL_LWM_PTHREADS_HPP_INCLUDED + +#if _MSC_VER >= 1020 +#pragma once +#endif + +// +// boost/detail/lwm_pthreads.hpp +// +// Copyright (c) 2002 Peter Dimov and Multi Media Ltd. +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// + +#include + +namespace boost +{ + +namespace detail +{ + +class lightweight_mutex +{ +private: + + pthread_mutex_t m_; + + lightweight_mutex(lightweight_mutex const &); + lightweight_mutex & operator=(lightweight_mutex const &); + +public: + + lightweight_mutex() + { + pthread_mutex_init(&m_, 0); + } + + ~lightweight_mutex() + { + pthread_mutex_destroy(&m_); + } + + class scoped_lock; + friend class scoped_lock; + + class scoped_lock + { + private: + + pthread_mutex_t & m_; + + scoped_lock(scoped_lock const &); + scoped_lock & operator=(scoped_lock const &); + + public: + + scoped_lock(lightweight_mutex & m): m_(m.m_) + { + pthread_mutex_lock(&m_); + } + + ~scoped_lock() + { + pthread_mutex_unlock(&m_); + } + }; +}; + +} // namespace detail + +} // namespace boost + +#endif // #ifndef BOOST_DETAIL_LWM_PTHREADS_HPP_INCLUDED diff --git a/include/boost/detail/lwm_win32.hpp b/include/boost/detail/lwm_win32.hpp new file mode 100644 index 0000000..2a29c3d --- /dev/null +++ b/include/boost/detail/lwm_win32.hpp @@ -0,0 +1,84 @@ +#ifndef BOOST_DETAIL_LWM_WIN32_HPP_INCLUDED +#define BOOST_DETAIL_LWM_WIN32_HPP_INCLUDED + +#if _MSC_VER >= 1020 +#pragma once +#endif + +// +// boost/detail/lwm_win32.hpp +// +// Copyright (c) 2002 Peter Dimov and Multi Media Ltd. +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// + +namespace boost +{ + +namespace detail +{ + +// Avoid #including + +namespace win32 +{ +extern "C" __declspec(dllimport) long __stdcall InterlockedExchange(long volatile *, long); +extern "C" __declspec(dllimport) void __stdcall Sleep(unsigned long); +} + +class lightweight_mutex +{ +private: + + long l_; + + lightweight_mutex(lightweight_mutex const &); + lightweight_mutex & operator=(lightweight_mutex const &); + +public: + + lightweight_mutex(): l_(0) + { + } + + class scoped_lock; + friend class scoped_lock; + + class scoped_lock + { + private: + + lightweight_mutex & m_; + + scoped_lock(scoped_lock const &); + scoped_lock & operator=(scoped_lock const &); + + public: + + explicit scoped_lock(lightweight_mutex & m): m_(m) + { + while( win32::InterlockedExchange(&m_.l_, 1) ) win32::Sleep(0); + } + + ~scoped_lock() + { + win32::InterlockedExchange(&m_.l_, 0); + + // Note: adding a win32::Sleep(0) here will make + // the mutex more fair and will increase the overall + // performance of the application substantially in + // high contention situations, but will penalize the + // low contention / single thread case up to 5x + } + }; +}; + +} // namespace detail + +} // namespace boost + +#endif // #ifndef BOOST_DETAIL_LWM_WIN32_HPP_INCLUDED diff --git a/include/boost/detail/shared_count.hpp b/include/boost/detail/shared_count.hpp index cf97746..ad08624 100644 --- a/include/boost/detail/shared_count.hpp +++ b/include/boost/detail/shared_count.hpp @@ -23,9 +23,10 @@ #endif #include -#include +#include -#include // for std::less +#include // for std::less +#include // for std::exception namespace boost { @@ -33,11 +34,19 @@ namespace boost namespace detail { -class counted_base +class bad_weak_to_shared_cast: public std::exception { public: - typedef atomic_count count_type; + virtual char const * what() const throw() + { + return "bad_weak_to_shared_cast"; + } +}; + +class counted_base +{ +public: // pre: initial_use_count <= initial_weak_count @@ -63,20 +72,31 @@ public: { } - void add_ref() // nothrow + void add_ref() { + lightweight_mutex::scoped_lock lock(mtx_); + if(use_count_ == 0) throw bad_weak_to_shared_cast(); ++use_count_; ++weak_count_; } void release() // nothrow { - if(--use_count_ == 0) + long new_use_count; + long new_weak_count; + + { + lightweight_mutex::scoped_lock lock(mtx_); + new_use_count = --use_count_; + new_weak_count = --weak_count_; + } + + if(new_use_count == 0) { dispose(); } - if(--weak_count_ == 0) + if(new_weak_count == 0) { // not a direct 'delete this', because the inlined // release() may use a different heap manager @@ -86,12 +106,20 @@ public: void weak_add_ref() // nothrow { + lightweight_mutex::scoped_lock lock(mtx_); ++weak_count_; } void weak_release() // nothrow { - if(--weak_count_ == 0) + long new_weak_count; + + { + lightweight_mutex::scoped_lock lock(mtx_); + new_weak_count = --weak_count_; + } + + if(new_weak_count == 0) { self_deleter_(this); } @@ -99,6 +127,7 @@ public: long use_count() const // nothrow { + lightweight_mutex::scoped_lock lock(mtx_); return use_count_; } @@ -114,9 +143,9 @@ private: // inv: use_count_ <= weak_count_ - count_type use_count_; - count_type weak_count_; - + long use_count_; + long weak_count_; + mutable lightweight_mutex mtx_; void (*self_deleter_) (counted_base *); }; @@ -191,6 +220,8 @@ public: pi_->add_ref(); } + explicit shared_count(weak_count const & r); // throws bad_weak_to_shared_cast when r.use_count() == 0 + shared_count & operator= (shared_count const & r) // nothrow { counted_base * tmp = r.pi_; @@ -235,6 +266,8 @@ private: counted_base * pi_; + friend class shared_count; + public: weak_count(): pi_(new counted_base(0, 1)) // can throw @@ -299,6 +332,11 @@ public: } }; +inline shared_count::shared_count(weak_count const & r): pi_(r.pi_) +{ + pi_->add_ref(); +} + } // namespace detail } // namespace boost diff --git a/include/boost/shared_ptr.hpp b/include/boost/shared_ptr.hpp index bcc6b9d..bd0fcff 100644 --- a/include/boost/shared_ptr.hpp +++ b/include/boost/shared_ptr.hpp @@ -97,6 +97,10 @@ public: // generated copy constructor, assignment, destructor are fine + explicit shared_ptr(weak_ptr const & r): px(r.px), pn(r.pn) // may throw + { + } + template shared_ptr(shared_ptr const & r): px(r.px), pn(r.pn) // never throws { @@ -191,6 +195,20 @@ public: return pn.use_count(); } + // implicit conversion to "bool" + + typedef long (this_type::*bool_type)() const; + + operator bool_type() const // never throws + { + return px == 0? 0: &this_type::use_count; + } + + bool operator! () const // never throws + { + return px == 0; + } + void swap(shared_ptr & other) // never throws { std::swap(px, other.px); diff --git a/include/boost/weak_ptr.hpp b/include/boost/weak_ptr.hpp index 0d09439..d3a01fe 100644 --- a/include/boost/weak_ptr.hpp +++ b/include/boost/weak_ptr.hpp @@ -30,6 +30,7 @@ private: // Borland 5.5.1 specific workarounds typedef weak_ptr this_type; + typedef shared_ptr shared_type; public: @@ -95,23 +96,32 @@ public: this_type().swap(*this); } - T * get() const // never throws + shared_type get() const // never throws { - return use_count() == 0? 0: px; + // optimization: avoid throw overhead + if(use_count() == 0) + { + return shared_type(); + } + + try + { + return shared_type(*this); + } + catch(boost::detail::bad_weak_to_shared_cast const &) + { + return shared_type(); + } } - typename detail::shared_ptr_traits::reference operator* () const // never throws - { - element_type * p = get(); - BOOST_ASSERT(p != 0); - return *p; - } + // operator* has been removed; it's unsafe. - T * operator-> () const // never throws + // operator-> retained for convenience, since it's safe + // in its current form. + + shared_type operator-> () const // may throw { - element_type * p = get(); - BOOST_ASSERT(p != 0); - return p; + return shared_type(*this); } long use_count() const // never throws @@ -119,7 +129,21 @@ public: return pn.use_count(); } - void swap(weak_ptr & other) // never throws + // implicit conversion to "bool" + + typedef long (this_type::*bool_type)() const; + + operator bool_type() const // never throws + { + return use_count() == 0? 0: &this_type::use_count; + } + + bool operator! () const // never throws + { + return use_count() == 0; + } + + void swap(this_type & other) // never throws { std::swap(px, other.px); pn.swap(other.pn); @@ -138,6 +162,7 @@ public: private: template friend class weak_ptr; + template friend class shared_ptr; #endif @@ -198,13 +223,6 @@ template weak_ptr shared_polymorphic_downcast(weak_pt return shared_static_cast(r); } -// get_pointer() enables boost::mem_fn to recognize weak_ptr - -template inline T * get_pointer(weak_ptr const & p) -{ - return p.get(); -} - } // namespace boost #ifdef BOOST_MSVC diff --git a/shared_ptr_test.cpp b/shared_ptr_test.cpp index 0ad4323..b5f80ed 100644 --- a/shared_ptr_test.cpp +++ b/shared_ptr_test.cpp @@ -96,18 +96,28 @@ void release_object(int * p) std::cout << "release_object()\n"; } -template void test_is_X(T const & p) +template void test_is_X(boost::shared_ptr const & p) { BOOST_TEST(p->id() == 1); BOOST_TEST((*p).id() == 1); } -template void test_is_Y(T const & p) +template void test_is_X(boost::weak_ptr const & p) +{ + BOOST_TEST(p->id() == 1); +} + +template void test_is_Y(boost::shared_ptr const & p) { BOOST_TEST(p->id() == 2); BOOST_TEST((*p).id() == 2); } +template void test_is_Y(boost::weak_ptr const & p) +{ + BOOST_TEST(p->id() == 2); +} + template void test_eq(T const & a, T const & b) { BOOST_TEST(a == b); @@ -136,6 +146,30 @@ template void test_ne2(T const & a, U const & b) BOOST_TEST(a != b); } +template void test_is_zero(boost::shared_ptr const & p) +{ + BOOST_TEST(!p); + BOOST_TEST(p.get() == 0); +} + +template void test_is_zero(boost::weak_ptr const & p) +{ + BOOST_TEST(!p); + test_is_zero(p.get()); +} + +template void test_is_nonzero(boost::shared_ptr const & p) +{ + BOOST_TEST(p); + BOOST_TEST(p.get() != 0); +} + +template void test_is_nonzero(boost::weak_ptr const & p) +{ + BOOST_TEST(p); + test_is_nonzero(p.get()); +} + int test_main(int, char * []) { using namespace boost; @@ -144,6 +178,8 @@ int test_main(int, char * []) shared_ptr p(new Y); shared_ptr p2(new X); + test_is_nonzero(p); + test_is_nonzero(p2); test_is_Y(p); test_is_X(p2); test_ne(p, p2); @@ -156,6 +192,9 @@ int test_main(int, char * []) shared_ptr p3 = shared_dynamic_cast(p); shared_ptr p4 = shared_dynamic_cast(p2); + test_is_nonzero(p3); + test_is_zero(p4); + BOOST_TEST(p.use_count() == 2); BOOST_TEST(p2.use_count() == 1); BOOST_TEST(p3.use_count() == 2); @@ -167,6 +206,7 @@ int test_main(int, char * []) shared_ptr p5(p); + test_is_nonzero(p5); test_eq2(p, p5); std::cout << "--\n"; @@ -176,6 +216,11 @@ int test_main(int, char * []) p3.reset(); p4.reset(); + test_is_zero(p); + test_is_zero(p2); + test_is_zero(p3); + test_is_zero(p4); + std::cout << "--\n"; BOOST_TEST(p5.use_count() == 1); @@ -183,25 +228,24 @@ int test_main(int, char * []) weak_ptr wp1; BOOST_TEST(wp1.use_count() == 0); - BOOST_TEST(wp1.get() == 0); + test_is_zero(wp1); weak_ptr wp2 = shared_static_cast(p5); BOOST_TEST(wp2.use_count() == 1); - BOOST_TEST(wp2.get() != 0); - + test_is_nonzero(wp2); test_is_Y(wp2); test_ne(wp1, wp2); weak_ptr wp3 = shared_dynamic_cast(wp2); BOOST_TEST(wp3.use_count() == 1); - BOOST_TEST(wp3.get() != 0); - + test_is_nonzero(wp3); test_eq2(wp2, wp3); weak_ptr wp4(wp3); + test_is_nonzero(wp4); test_eq(wp2, wp4); wp1 = p2; @@ -210,8 +254,7 @@ int test_main(int, char * []) wp1 = wp2; BOOST_TEST(wp1.use_count() == 1); - BOOST_TEST(wp1.get() != 0); - + test_is_nonzero(wp1); test_eq(wp1, wp2); weak_ptr wp5; @@ -220,15 +263,16 @@ int test_main(int, char * []) bool b2 = wp5 < wp1; p5.reset(); + test_is_zero(wp5); BOOST_TEST(wp1.use_count() == 0); - BOOST_TEST(wp1.get() == 0); + test_is_zero(wp1); BOOST_TEST(wp2.use_count() == 0); - BOOST_TEST(wp2.get() == 0); + test_is_zero(wp2); BOOST_TEST(wp3.use_count() == 0); - BOOST_TEST(wp3.get() == 0); + test_is_zero(wp3); // Test operator< stability for std::set< weak_ptr<> > // Thanks to Joe Gottman for pointing this out