From 5d4678364e17e945f5c5a09814496de809de04ee Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 8 Oct 2007 07:18:27 +0000 Subject: [PATCH 001/214] Use InterlockedCompareExchange when _ReadWriteBarrier not available [SVN r39777] --- .../boost/thread/win32/interlocked_read.hpp | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/include/boost/thread/win32/interlocked_read.hpp b/include/boost/thread/win32/interlocked_read.hpp index 98f0b3e6..77a333ba 100644 --- a/include/boost/thread/win32/interlocked_read.hpp +++ b/include/boost/thread/win32/interlocked_read.hpp @@ -9,7 +9,7 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#include +#ifdef BOOST_MSVC extern "C" void _ReadWriteBarrier(void); #pragma intrinsic(_ReadWriteBarrier) @@ -33,4 +33,26 @@ namespace boost } } +#else + +#include + +namespace boost +{ + namespace detail + { + inline long interlocked_read_acquire(long volatile* x) + { + return BOOST_INTERLOCKED_COMPARE_EXCHANGE(x,0,0); + } + inline void* interlocked_read_acquire(void* volatile* x) + { + return BOOST_INTERLOCKED_COMPARE_EXCHANGE_POINTER(x,0,0); + } + } +} + +#endif + + #endif From b15b2e666f7a4d21a129c7244462d947d2e98402 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 8 Oct 2007 09:48:57 +0000 Subject: [PATCH 002/214] added extended test for new call_once [SVN r39780] --- test/test_once.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/test_once.cpp b/test/test_once.cpp index f9f59b79..b57bf5d5 100644 --- a/test/test_once.cpp +++ b/test/test_once.cpp @@ -49,6 +49,54 @@ void test_call_once() BOOST_CHECK_EQUAL(var_to_init,1); } +int var_to_init_with_functor=0; + +struct increment_value +{ + int* value; + explicit increment_value(int* value_): + value(value_) + {} + + void operator()() const + { + boost::mutex::scoped_lock lock(m); + ++(*value); + } +}; + +void call_once_with_functor() +{ + unsigned const loop_count=100; + int my_once_value=0; + static boost::once_flag functor_flag=BOOST_ONCE_INIT; + for(unsigned i=0;iadd(BOOST_TEST_CASE(test_call_once)); + test->add(BOOST_TEST_CASE(test_call_once_arbitrary_functor)); return test; } From a706d1df00a73750619eeeaf35f66703b0f7a131 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 8 Oct 2007 09:55:56 +0000 Subject: [PATCH 003/214] call_once passes exceptions to caller and leaves flag unset [SVN r39781] --- test/test_once.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/test/test_once.cpp b/test/test_once.cpp index b57bf5d5..eb9510d8 100644 --- a/test/test_once.cpp +++ b/test/test_once.cpp @@ -83,7 +83,6 @@ void call_once_with_functor() BOOST_CHECK_EQUAL(my_once_value, 1); } - void test_call_once_arbitrary_functor() { unsigned const num_threads=100; @@ -98,6 +97,56 @@ void test_call_once_arbitrary_functor() } +struct throw_before_third_pass +{ + struct my_exception + {}; + + static unsigned pass_counter; + + void operator()() const + { + boost::mutex::scoped_lock lock(m); + ++pass_counter; + if(pass_counter<3) + { + throw my_exception(); + } + } +}; + +unsigned throw_before_third_pass::pass_counter=0; +unsigned exception_counter=0; + +void call_once_with_exception() +{ + static boost::once_flag functor_flag=BOOST_ONCE_INIT; + try + { + boost::call_once(functor_flag, throw_before_third_pass()); + } + catch(throw_before_third_pass::my_exception) + { + boost::mutex::scoped_lock lock(m); + ++exception_counter; + } +} + +void test_call_once_retried_on_exception() +{ + unsigned const num_threads=100; + boost::thread_group group; + + for(unsigned i=0;iadd(BOOST_TEST_CASE(test_call_once)); test->add(BOOST_TEST_CASE(test_call_once_arbitrary_functor)); + test->add(BOOST_TEST_CASE(test_call_once_retried_on_exception)); return test; } From 9d4c55161a70c20aa81c009ade0bad2703274179 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 8 Oct 2007 15:41:05 +0000 Subject: [PATCH 004/214] New mutex implementations, more akin to C++0x [SVN r39784] --- build/Jamfile.v2 | 4 +- include/boost/thread/condition.hpp | 48 +- include/boost/thread/locks.hpp | 486 ++++++++++++++++++ include/boost/thread/mutex.hpp | 177 +------ include/boost/thread/pthread/mutex.hpp | 216 ++++++++ .../boost/thread/pthread/recursive_mutex.hpp | 276 ++++++++++ include/boost/thread/pthread/timespec.hpp | 23 + include/boost/thread/recursive_mutex.hpp | 191 +------ include/boost/thread/thread_time.hpp | 40 ++ .../thread/win32/basic_recursive_mutex.hpp | 120 +++++ .../boost/thread/win32/basic_timed_mutex.hpp | 157 ++++++ include/boost/thread/win32/mutex.hpp | 61 +++ .../boost/thread/win32/recursive_mutex.hpp | 61 +++ .../boost/thread/win32/thread_primitives.hpp | 18 +- src/condition.cpp | 13 + src/thread.cpp | 1 + test/test_mutex.cpp | 18 +- 17 files changed, 1524 insertions(+), 386 deletions(-) create mode 100644 include/boost/thread/locks.hpp create mode 100644 include/boost/thread/pthread/mutex.hpp create mode 100644 include/boost/thread/pthread/recursive_mutex.hpp create mode 100644 include/boost/thread/pthread/timespec.hpp create mode 100644 include/boost/thread/thread_time.hpp create mode 100644 include/boost/thread/win32/basic_recursive_mutex.hpp create mode 100644 include/boost/thread/win32/basic_timed_mutex.hpp create mode 100644 include/boost/thread/win32/mutex.hpp create mode 100644 include/boost/thread/win32/recursive_mutex.hpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 2aadaf36..961e01e8 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -17,9 +17,9 @@ CPP_SOURCES = barrier condition exceptions - mutex +# mutex # once - recursive_mutex +# recursive_mutex # read_write_mutex thread tss_hooks diff --git a/include/boost/thread/condition.hpp b/include/boost/thread/condition.hpp index e5a1c317..64889d56 100644 --- a/include/boost/thread/condition.hpp +++ b/include/boost/thread/condition.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf +// Copyright (C) 2007 Anthony Williams // // 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) @@ -61,6 +62,7 @@ public: // still waiting to be removed from m_queue #elif defined(BOOST_HAS_PTHREADS) pthread_cond_t m_condition; + pthread_mutex_t m_mutex; #elif defined(BOOST_HAS_MPTASKS) MPSemaphoreID m_gate; MPSemaphoreID m_queue; @@ -90,7 +92,7 @@ public: if (!lock) throw lock_error(); - do_wait(lock.m_mutex); + do_wait(*lock.mutex()); } template @@ -100,7 +102,7 @@ public: throw lock_error(); while (!pred()) - do_wait(lock.m_mutex); + do_wait(*lock.mutex()); } template @@ -109,7 +111,7 @@ public: if (!lock) throw lock_error(); - return do_timed_wait(lock.m_mutex, xt); + return do_timed_wait(*lock.mutex(), xt); } template @@ -120,7 +122,7 @@ public: while (!pred()) { - if (!do_timed_wait(lock.m_mutex, xt)) + if (!do_timed_wait(*lock.mutex(), xt)) return false; } @@ -135,25 +137,22 @@ private: { #if (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS)) m_impl.enter_wait(); +#else + pthread_mutex_lock(&m_impl.m_mutex); #endif - typedef detail::thread::lock_ops -#if defined(__HP_aCC) && __HP_aCC <= 33900 && !defined(BOOST_STRICT_CONFIG) -# define lock_ops lock_ops_ // HP confuses lock_ops witht the template -#endif - lock_ops; - - typename lock_ops::lock_state state; - lock_ops::unlock(mutex, state); + mutex.unlock(); #if defined(BOOST_HAS_PTHREADS) - m_impl.do_wait(state.pmutex); + m_impl.do_wait(&m_impl.m_mutex); #elif (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS)) m_impl.do_wait(); #endif - lock_ops::lock(mutex, state); -#undef lock_ops +#if defined(BOOST_HAS_PTHREADS) + pthread_mutex_unlock(&m_impl.m_mutex); +#endif + mutex.lock(); } template @@ -161,27 +160,24 @@ private: { #if (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS)) m_impl.enter_wait(); +#else + pthread_mutex_lock(&m_impl.m_mutex); #endif - typedef detail::thread::lock_ops -#if defined(__HP_aCC) && __HP_aCC <= 33900 && !defined(BOOST_STRICT_CONFIG) -# define lock_ops lock_ops_ // HP confuses lock_ops witht the template -#endif - lock_ops; - - typename lock_ops::lock_state state; - lock_ops::unlock(mutex, state); + mutex.unlock(); bool ret = false; #if defined(BOOST_HAS_PTHREADS) - ret = m_impl.do_timed_wait(xt, state.pmutex); + ret = m_impl.do_timed_wait(xt, &m_impl.m_mutex); #elif (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS)) ret = m_impl.do_timed_wait(xt); #endif - lock_ops::lock(mutex, state); -#undef lock_ops +#if defined(BOOST_HAS_PTHREADS) + pthread_mutex_unlock(&m_impl.m_mutex); +#endif + mutex.lock(); return ret; } diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp new file mode 100644 index 00000000..258b0f60 --- /dev/null +++ b/include/boost/thread/locks.hpp @@ -0,0 +1,486 @@ +// 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) +// (C) Copyright 2007 Anthony Williams +#ifndef BOOST_THREAD_LOCKS_HPP +#define BOOST_THREAD_LOCKS_HPP +#include +#include +#include +#include +#include + +namespace boost +{ + struct defer_lock_t + {}; + struct try_to_lock_t + {}; + struct adopt_lock_t + {}; + + const defer_lock_t defer_lock={}; + const try_to_lock_t try_to_lock={}; + const adopt_lock_t adopt_lock={}; + + template + class shareable_lock; + + template + class exclusive_lock; + + template + class upgradeable_lock; + + template + class lock_guard + { + private: + Mutex& m; + + explicit lock_guard(lock_guard&); + lock_guard& operator=(lock_guard&); + public: + explicit lock_guard(Mutex& m_): + m(m_) + { + m.lock(); + } + lock_guard(Mutex& m_,adopt_lock_t): + m(m_) + {} + ~lock_guard() + { + m.unlock(); + } + }; + + + template + class unique_lock + { + private: + Mutex* m; + bool is_locked; + explicit unique_lock(unique_lock&); + unique_lock& operator=(unique_lock&); + public: + explicit unique_lock(Mutex& m_): + m(&m_),is_locked(false) + { + lock(); + } + unique_lock(Mutex& m_,adopt_lock_t): + m(&m_),is_locked(true) + {} + unique_lock(Mutex& m_,defer_lock_t): + m(&m_),is_locked(false) + {} + unique_lock(Mutex& m_,try_to_lock_t): + m(&m_),is_locked(false) + { + try_lock(); + } + unique_lock(Mutex& m_,system_time const& target_time): + m(&m_),is_locked(false) + { + timed_lock(target_time); + } + unique_lock(boost::move_t other): + m(other->m),is_locked(other->is_locked) + { + other->is_locked=false; + } + unique_lock(boost::move_t > other); + + unique_lock& operator=(boost::move_t other) + { + unique_lock temp(other); + swap(temp); + return *this; + } + + unique_lock& operator=(boost::move_t > other) + { + unique_lock temp(other); + swap(temp); + return *this; + } + + void swap(unique_lock& other) + { + std::swap(m,other.m); + std::swap(is_locked,other.is_locked); + } + void swap(boost::move_t other) + { + std::swap(m,other->m); + std::swap(is_locked,other->is_locked); + } + + ~unique_lock() + { + if(owns_lock()) + { + m->unlock(); + } + } + void lock() + { + if(owns_lock()) + { + throw boost::lock_error(); + } + m->lock(); + is_locked=true; + } + bool try_lock() + { + if(owns_lock()) + { + throw boost::lock_error(); + } + is_locked=m->try_lock(); + return is_locked; + } + template + bool timed_lock(TimeDuration const& relative_time) + { + is_locked=m->timed_lock(relative_time); + return is_locked; + } + + bool timed_lock(::boost::system_time const& absolute_time) + { + is_locked=m->timed_lock(absolute_time); + return is_locked; + } + void unlock() + { + if(!owns_lock()) + { + throw boost::lock_error(); + } + m->unlock(); + is_locked=false; + } + + typedef void (unique_lock::*bool_type)(); + operator bool_type() const + { + return is_locked?&unique_lock::lock:0; + } + bool owns_lock() const + { + return is_locked; + } + + Mutex* mutex() const + { + return m; + } + + Mutex* release() + { + Mutex* const res=m; + m=0; + is_locked=false; + return res; + } + + friend class shareable_lock; + friend class upgradeable_lock; + }; + + template + class shareable_lock + { + protected: + Mutex* m; + bool is_locked; + private: + explicit shareable_lock(shareable_lock&); + shareable_lock& operator=(shareable_lock&); + public: + explicit shareable_lock(Mutex& m_): + m(&m_),is_locked(false) + { + lock(); + } + shareable_lock(Mutex& m_,bool do_lock): + m(&m_),is_locked(false) + { + if(do_lock) + { + lock(); + } + } + shareable_lock(boost::move_t other): + m(other->m),is_locked(other->is_locked) + { + other->is_locked=false; + } + + shareable_lock(boost::move_t > other): + m(other->m),is_locked(other->is_locked) + { + other->is_locked=false; + if(is_locked) + { + m->unlock_and_lock_shareable(); + } + } + + shareable_lock(boost::move_t > other): + m(other->m),is_locked(other->is_locked) + { + other->is_locked=false; + if(is_locked) + { + m->unlock_upgradeable_and_lock_shareable(); + } + } + + shareable_lock& operator=(boost::move_t other) + { + shareable_lock temp(other); + swap(temp); + return *this; + } + + shareable_lock& operator=(boost::move_t > other) + { + shareable_lock temp(other); + swap(temp); + return *this; + } + + shareable_lock& operator=(boost::move_t > other) + { + shareable_lock temp(other); + swap(temp); + return *this; + } + + void swap(shareable_lock& other) + { + std::swap(m,other.m); + std::swap(is_locked,other.is_locked); + } + + ~shareable_lock() + { + if(owns_lock()) + { + m->unlock_shareable(); + } + } + void lock() + { + if(owns_lock()) + { + throw boost::lock_error(); + } + m->lock_shareable(); + is_locked=true; + } + bool try_lock() + { + if(owns_lock()) + { + throw boost::lock_error(); + } + is_locked=m->try_lock_shareable(); + return is_locked; + } + void unlock() + { + if(!owns_lock()) + { + throw boost::lock_error(); + } + m->unlock_shareable(); + is_locked=false; + } + + typedef void (shareable_lock::*bool_type)(); + operator bool_type() const + { + return is_locked?&shareable_lock::lock:0; + } + bool owns_lock() const + { + return is_locked; + } + + }; + + template + class upgradeable_lock + { + protected: + Mutex* m; + bool is_locked; + private: + explicit upgradeable_lock(upgradeable_lock&); + upgradeable_lock& operator=(upgradeable_lock&); + public: + explicit upgradeable_lock(Mutex& m_): + m(&m_),is_locked(false) + { + lock(); + } + upgradeable_lock(Mutex& m_,bool do_lock): + m(&m_),is_locked(false) + { + if(do_lock) + { + lock(); + } + } + upgradeable_lock(boost::move_t other): + m(other->m),is_locked(other->is_locked) + { + other->is_locked=false; + } + + upgradeable_lock(boost::move_t > other): + m(other->m),is_locked(other->is_locked) + { + other->is_locked=false; + if(is_locked) + { + m->unlock_and_lock_upgradeable(); + } + } + + upgradeable_lock& operator=(boost::move_t other) + { + upgradeable_lock temp(other); + swap(temp); + return *this; + } + + upgradeable_lock& operator=(boost::move_t > other) + { + upgradeable_lock temp(other); + swap(temp); + return *this; + } + + void swap(upgradeable_lock& other) + { + std::swap(m,other.m); + std::swap(is_locked,other.is_locked); + } + + ~upgradeable_lock() + { + if(owns_lock()) + { + m->unlock_upgradeable(); + } + } + void lock() + { + if(owns_lock()) + { + throw boost::lock_error(); + } + m->lock_upgradeable(); + is_locked=true; + } + bool try_lock() + { + if(owns_lock()) + { + throw boost::lock_error(); + } + is_locked=m->try_lock_upgradeable(); + return is_locked; + } + void unlock() + { + if(!owns_lock()) + { + throw boost::lock_error(); + } + m->unlock_upgradeable(); + is_locked=false; + } + + typedef void (upgradeable_lock::*bool_type)(); + operator bool_type() const + { + return is_locked?&upgradeable_lock::lock:0; + } + bool owns_lock() const + { + return is_locked; + } + friend class shareable_lock; + friend class unique_lock; + }; + + template + unique_lock::unique_lock(boost::move_t > other): + m(other->m),is_locked(other->is_locked) + { + other->is_locked=false; + if(is_locked) + { + m->unlock_upgradeable_and_lock(); + } + } + + template + class upgrade_to_unique_lock + { + private: + upgradeable_lock* source; + unique_lock exclusive; + + explicit upgrade_to_unique_lock(upgrade_to_unique_lock&); + upgrade_to_unique_lock& operator=(upgrade_to_unique_lock&); + public: + explicit upgrade_to_unique_lock(upgradeable_lock& m_): + source(&m_),exclusive(boost::move(*source)) + {} + ~upgrade_to_unique_lock() + { + if(source) + { + *source=boost::move(exclusive); + } + } + + upgrade_to_unique_lock(boost::move_t other): + source(other->source),exclusive(boost::move(other->exclusive)) + { + other->source=0; + } + + upgrade_to_unique_lock& operator=(boost::move_t other) + { + upgrade_to_unique_lock temp(other); + swap(temp); + return *this; + } + void swap(upgrade_to_unique_lock& other) + { + std::swap(source,other.source); + exclusive.swap(other.exclusive); + } + typedef void (upgrade_to_unique_lock::*bool_type)(upgrade_to_unique_lock&); + operator bool_type() const + { + return exclusive.owns_lock()?&upgrade_to_unique_lock::swap:0; + } + bool owns_lock() const + { + return exclusive.owns_lock(); + } + }; +} + +#endif diff --git a/include/boost/thread/mutex.hpp b/include/boost/thread/mutex.hpp index 1b74da55..98a1cbdd 100644 --- a/include/boost/thread/mutex.hpp +++ b/include/boost/thread/mutex.hpp @@ -1,170 +1,15 @@ -// Copyright (C) 2001-2003 -// William E. Kempf +#ifndef BOOST_THREAD_MUTEX_HPP +#define BOOST_THREAD_MUTEX_HPP + +// mutex.hpp // -// 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) +// (C) Copyright 2007 Anthony Williams +// +// 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) -#ifndef BOOST_MUTEX_WEK070601_HPP -#define BOOST_MUTEX_WEK070601_HPP +#include +#include BOOST_THREAD_PLATFORM(mutex.hpp) -#include - -#include -#include - -#if defined(BOOST_HAS_PTHREADS) -# include #endif - -#if defined(BOOST_HAS_MPTASKS) -# include "scoped_critical_region.hpp" -#endif - -namespace boost { - -struct xtime; -// disable warnings about non dll import -// see: http://www.boost.org/more/separate_compilation.html#dlls -#ifdef BOOST_MSVC -# pragma warning(push) -# pragma warning(disable: 4251 4231 4660 4275) -#endif - -class BOOST_THREAD_DECL mutex - : private noncopyable -{ -public: - friend class detail::thread::lock_ops; - - typedef detail::thread::scoped_lock scoped_lock; - - mutex(); - ~mutex(); - -private: -#if defined(BOOST_HAS_WINTHREADS) - typedef void* cv_state; -#elif defined(BOOST_HAS_PTHREADS) - struct cv_state - { - pthread_mutex_t* pmutex; - }; -#elif defined(BOOST_HAS_MPTASKS) - struct cv_state - { - }; -#endif - void do_lock(); - void do_unlock(); - void do_lock(cv_state& state); - void do_unlock(cv_state& state); - -#if defined(BOOST_HAS_WINTHREADS) - void* m_mutex; - bool m_critical_section; -#elif defined(BOOST_HAS_PTHREADS) - pthread_mutex_t m_mutex; -#elif defined(BOOST_HAS_MPTASKS) - threads::mac::detail::scoped_critical_region m_mutex; - threads::mac::detail::scoped_critical_region m_mutex_mutex; -#endif -}; - -class BOOST_THREAD_DECL try_mutex - : private noncopyable -{ -public: - friend class detail::thread::lock_ops; - - typedef detail::thread::scoped_lock scoped_lock; - typedef detail::thread::scoped_try_lock scoped_try_lock; - - try_mutex(); - ~try_mutex(); - -private: -#if defined(BOOST_HAS_WINTHREADS) - typedef void* cv_state; -#elif defined(BOOST_HAS_PTHREADS) - struct cv_state - { - pthread_mutex_t* pmutex; - }; -#elif defined(BOOST_HAS_MPTASKS) - struct cv_state - { - }; -#endif - void do_lock(); - bool do_trylock(); - void do_unlock(); - void do_lock(cv_state& state); - void do_unlock(cv_state& state); - -#if defined(BOOST_HAS_WINTHREADS) - void* m_mutex; - bool m_critical_section; -#elif defined(BOOST_HAS_PTHREADS) - pthread_mutex_t m_mutex; -#elif defined(BOOST_HAS_MPTASKS) - threads::mac::detail::scoped_critical_region m_mutex; - threads::mac::detail::scoped_critical_region m_mutex_mutex; -#endif -}; - -class BOOST_THREAD_DECL timed_mutex - : private noncopyable -{ -public: - friend class detail::thread::lock_ops; - - typedef detail::thread::scoped_lock scoped_lock; - typedef detail::thread::scoped_try_lock scoped_try_lock; - typedef detail::thread::scoped_timed_lock scoped_timed_lock; - - timed_mutex(); - ~timed_mutex(); - -private: -#if defined(BOOST_HAS_WINTHREADS) - typedef void* cv_state; -#elif defined(BOOST_HAS_PTHREADS) - struct cv_state - { - pthread_mutex_t* pmutex; - }; -#elif defined(BOOST_HAS_MPTASKS) - struct cv_state - { - }; -#endif - void do_lock(); - bool do_trylock(); - bool do_timedlock(const xtime& xt); - void do_unlock(); - void do_lock(cv_state& state); - void do_unlock(cv_state& state); - -#if defined(BOOST_HAS_WINTHREADS) - void* m_mutex; -#elif defined(BOOST_HAS_PTHREADS) - pthread_mutex_t m_mutex; - pthread_cond_t m_condition; - bool m_locked; -#elif defined(BOOST_HAS_MPTASKS) - threads::mac::detail::scoped_critical_region m_mutex; - threads::mac::detail::scoped_critical_region m_mutex_mutex; -#endif -}; -#ifdef BOOST_MSVC -# pragma warning(pop) -#endif -} // namespace boost - -// Change Log: -// 8 Feb 01 WEKEMPF Initial version. -// 22 May 01 WEKEMPF Modified to use xtime for time outs. Factored out -// to three classes, mutex, try_mutex and timed_mutex. -// 3 Jan 03 WEKEMPF Modified for DLL implementation. - -#endif // BOOST_MUTEX_WEK070601_HPP diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp new file mode 100644 index 00000000..09e5313b --- /dev/null +++ b/include/boost/thread/pthread/mutex.hpp @@ -0,0 +1,216 @@ +#ifndef BOOST_THREAD_PTHREAD_MUTEX_HPP +#define BOOST_THREAD_PTHREAD_MUTEX_HPP +// (C) Copyright 2007 Anthony Williams +// 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) + +#include +#include +#include +#include +#include +#include +#include +#include +#include "timespec.hpp" + +#ifdef _POSIX_TIMEOUTS +#if _POSIX_TIMEOUTS >= 0 +#define BOOST_PTHREAD_HAS_TIMEDLOCK +#endif +#endif + +namespace boost +{ + class mutex: + boost::noncopyable + { + private: + pthread_mutex_t m; + public: + mutex() + { + int const res=pthread_mutex_init(&m,NULL); + if(res) + { + throw thread_resource_error(); + } + } + ~mutex() + { + int const res=pthread_mutex_destroy(&m); + BOOST_ASSERT(!res); + } + + void lock() + { + int const res=pthread_mutex_lock(&m); + BOOST_ASSERT(!res); + } + + void unlock() + { + int const res=pthread_mutex_unlock(&m); + BOOST_ASSERT(!res); + } + + bool try_lock() + { + int const res=pthread_mutex_trylock(&m); + BOOST_ASSERT(!res || res==EBUSY); + return !res; + } + typedef unique_lock scoped_lock; + typedef scoped_lock scoped_try_lock; + }; + + typedef mutex try_mutex; + + class timed_mutex: + boost::noncopyable + { + private: + pthread_mutex_t m; +#ifdef BOOST_PTHREAD_HAS_TIMEDLOCK + pthread_cond_t cond; + bool is_locked; + + struct pthread_mutex_scoped_lock + { + pthread_mutex_t* m; + explicit pthread_mutex_scoped_lock(pthread_mutex_t* m_): + m(m_) + { + int const res=pthread_mutex_lock(m); + BOOST_ASSERT(!res); + } + ~pthread_mutex_scoped_lock() + { + int const res=pthread_mutex_unlock(m); + BOOST_ASSERT(!res); + } + + }; + + +#endif + public: + timed_mutex() + { + int const res=pthread_mutex_init(&m,NULL); + if(res) + { + throw thread_resource_error(); + } +#ifdef BOOST_PTHREAD_HAS_TIMEDLOCK + int const res2=pthread_cond_init(&cond,NULL); + if(res2) + { + int const destroy_res=pthread_mutex_destroy(&m); + BOOST_ASSERT(!destroy_res); + throw thread_resource_error(); + } + is_locked=false; +#endif + } + ~timed_mutex() + { + int const res=pthread_mutex_destroy(&m); + BOOST_ASSERT(!res); +#ifdef BOOST_PTHREAD_HAS_TIMEDLOCK + int const res2=pthread_cond_destroy(&cond); + BOOST_ASSERT(!res2); +#endif + } + + template + bool timed_lock(TimeDuration const & relative_time) + { + return timed_lock(get_system_time()+relative_time); + } + +#ifdef BOOST_PTHREAD_HAS_TIMEDLOCK + void lock() + { + int const res=pthread_mutex_lock(&m); + BOOST_ASSERT(!res); + } + + void unlock() + { + int const res=pthread_mutex_unlock(&m); + BOOST_ASSERT(!res); + } + + bool try_lock() + { + int const res=pthread_mutex_trylock(&m); + BOOST_ASSERT(!res || res==EBUSY); + return !res; + } + bool timed_lock(system_time const & abs_time) + { + struct timespec const timeout=detail::get_timespec(abs_time); + int const res=pthread_mutex_timedlock(&m,&timeout); + BOOST_ASSERT(!res || res==EBUSY); + return !res; + } +#else + void lock() + { + pthread_mutex_scoped_lock const _(&m); + while(is_locked) + { + int const cond_res=pthread_cond_wait(&cond,&m); + BOOST_ASSERT(!cond_res); + } + is_locked=true; + } + + void unlock() + { + pthread_mutex_scoped_lock const _(&m); + is_locked=false; + int const res=pthread_cond_signal(&cond); + BOOST_ASSERT(!res); + } + + bool try_lock() + { + pthread_mutex_scoped_lock const _(&m); + if(is_locked) + { + return false; + } + is_locked=true; + return true; + } + + bool timed_lock(system_time const & abs_time) + { + struct timespec const timeout=detail::get_timespec(abs_time); + pthread_mutex_scoped_lock const _(&m); + while(is_locked) + { + int const cond_res=pthread_cond_timewait(&cond,&m,&timeout); + if(cond_res==ETIMEOUT) + { + return false; + } + BOOST_ASSERT(!cond_res); + } + is_locked=true; + return true; + } +#endif + + typedef unique_lock scoped_timed_lock; + typedef scoped_timed_lock scoped_try_lock; + typedef scoped_timed_lock scoped_lock; + }; + +} + + +#endif diff --git a/include/boost/thread/pthread/recursive_mutex.hpp b/include/boost/thread/pthread/recursive_mutex.hpp new file mode 100644 index 00000000..2ccc3ae1 --- /dev/null +++ b/include/boost/thread/pthread/recursive_mutex.hpp @@ -0,0 +1,276 @@ +#ifndef BOOST_THREAD_PTHREAD_RECURSIVE_MUTEX_HPP +#define BOOST_THREAD_PTHREAD_RECURSIVE_MUTEX_HPP +// (C) Copyright 2007 Anthony Williams +// 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) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "timespec.hpp" + +#ifdef _POSIX_TIMEOUTS +#if _POSIX_TIMEOUTS >= 0 +#define BOOST_PTHREAD_HAS_TIMEDLOCK +#endif +#endif + +namespace boost +{ + class recursive_mutex: + boost::noncopyable + { + private: + pthread_mutex_t m; + public: + recursive_mutex() + { + pthread_mutexattr_t attr; + + int const init_attr_res=pthread_mutexattr_init(&attr); + if(init_attr_res) + { + throw thread_resource_error(); + } + int const set_attr_res=pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE); + if(set_attr_res) + { + throw thread_resource_error(); + } + + int const res=pthread_mutex_init(&m,&attr); + if(res) + { + throw thread_resource_error(); + } + int const destroy_attr_res=pthread_mutexattr_destroy(&attr); + BOOST_ASSERT(!destroy_attr_res); + } + ~recursive_mutex() + { + int const res=pthread_mutex_destroy(&m); + BOOST_ASSERT(!res); + } + + void lock() + { + int const res=pthread_mutex_lock(&m); + BOOST_ASSERT(!res); + } + + void unlock() + { + int const res=pthread_mutex_unlock(&m); + BOOST_ASSERT(!res); + } + + bool try_lock() + { + int const res=pthread_mutex_trylock(&m); + BOOST_ASSERT(!res || res==EBUSY); + return !res; + } + typedef unique_lock scoped_lock; + typedef scoped_lock scoped_try_lock; + }; + + typedef recursive_mutex recursive_try_mutex; + + class recursive_timed_mutex: + boost::noncopyable + { + private: + pthread_mutex_t m; +#ifndef BOOST_PTHREAD_HAS_TIMEDLOCK + pthread_cond_t cond; + bool is_locked; + pthread_t owner; + unsigned count; + + struct pthread_mutex_scoped_lock + { + pthread_mutex_t* m; + explicit pthread_mutex_scoped_lock(pthread_mutex_t* m_): + m(m_) + { + int const res=pthread_mutex_lock(m); + BOOST_ASSERT(!res); + } + ~pthread_mutex_scoped_lock() + { + int const res=pthread_mutex_unlock(m); + BOOST_ASSERT(!res); + } + + }; + + +#endif + public: + recursive_timed_mutex() + { +#ifdef BOOST_PTHREAD_HAS_TIMEDLOCK + pthread_mutexattr_t attr; + + int const init_attr_res=pthread_mutexattr_init(&attr); + if(init_attr_res) + { + throw thread_resource_error(); + } + int const set_attr_res=pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE); + if(set_attr_res) + { + throw thread_resource_error(); + } + + int const res=pthread_mutex_init(&m,&attr); + if(res) + { + throw thread_resource_error(); + } + int const destroy_attr_res=pthread_mutexattr_destroy(&attr); + BOOST_ASSERT(!destroy_attr_res); +#else + int const res=pthread_mutex_init(&m,NULL); + if(res) + { + throw thread_resource_error(); + } + int const res2=pthread_cond_init(&cond,NULL); + if(res2) + { + int const destroy_res=pthread_mutex_destroy(&m); + BOOST_ASSERT(!destroy_res); + throw thread_resource_error(); + } + is_locked=false; + count=0; +#endif + } + ~recursive_timed_mutex() + { + int const res=pthread_mutex_destroy(&m); + BOOST_ASSERT(!res); +#ifndef BOOST_PTHREAD_HAS_TIMEDLOCK + int const res2=pthread_cond_destroy(&cond); + BOOST_ASSERT(!res2); +#endif + } + + template + bool timed_lock(TimeDuration const & relative_time) + { + return timed_lock(get_system_time()+relative_time); + } + +#ifdef BOOST_PTHREAD_HAS_TIMEDLOCK + void lock() + { + int const res=pthread_mutex_lock(&m); + BOOST_ASSERT(!res); + } + + void unlock() + { + int const res=pthread_mutex_unlock(&m); + BOOST_ASSERT(!res); + } + + bool try_lock() + { + int const res=pthread_mutex_trylock(&m); + BOOST_ASSERT(!res || res==EBUSY); + return !res; + } + bool timed_lock(system_time const & abs_time) + { + struct timespec const timeout=detail::get_timespec(abs_time); + int const res=pthread_mutex_timedlock(&m,&timeout); + BOOST_ASSERT(!res || res==EBUSY); + return !res; + } +#else + void lock() + { + pthread_mutex_scoped_lock const _(&m); + if(is_locked && owner==pthread_self()) + { + ++count; + return; + } + + while(is_locked) + { + int const cond_res=pthread_cond_wait(&cond,&m); + BOOST_ASSERT(!cond_res); + } + is_locked=true; + ++count; + owner=pthread_self(); + } + + void unlock() + { + pthread_mutex_scoped_lock const _(&m); + if(!--count) + { + is_locked=false; + } + int const res=pthread_cond_signal(&cond); + BOOST_ASSERT(!res); + } + + bool try_lock() + { + pthread_mutex_scoped_lock const _(&m); + if(is_locked && owner!=pthread_self()) + { + return false; + } + is_locked=true; + ++count; + owner=pthread_self(); + return true; + } + + bool timed_lock(system_time const & abs_time) + { + struct timespec const timeout=detail::get_timespec(abs_time); + pthread_mutex_scoped_lock const _(&m); + if(is_locked && owner==pthread_self()) + { + ++count; + return; + } + while(is_locked) + { + int const cond_res=pthread_cond_timewait(&cond,&m,&timeout); + if(cond_res==ETIMEOUT) + { + return false; + } + BOOST_ASSERT(!cond_res); + } + is_locked=true; + ++count; + owner=pthread_self(); + return true; + } +#endif + + typedef unique_lock scoped_timed_lock; + typedef scoped_timed_lock scoped_try_lock; + typedef scoped_timed_lock scoped_lock; + }; + +} + + +#endif diff --git a/include/boost/thread/pthread/timespec.hpp b/include/boost/thread/pthread/timespec.hpp new file mode 100644 index 00000000..a7039f7a --- /dev/null +++ b/include/boost/thread/pthread/timespec.hpp @@ -0,0 +1,23 @@ +#ifndef BOOST_THREAD_PTHREAD_TIMESPEC_HPP +#define BOOST_THREAD_PTHREAD_TIMESPEC_HPP + +#include +#include + +namespace boost +{ + namespace detail + { + inline struct timespec get_timespec(boost::system_time const& abs_time) + { + struct timespec timeout={0}; + boost::posix_time::time_duration const time_since_epoch=abs_time-boost::posix_time::from_time_t(0); + + timeout.tv_sec=time_since_epoch.total_seconds(); + timeout.tv_nsec=time_since_epoch.fractional_seconds()*(1000000000/time_since_epoch.ticks_per_second()); + return timeout; + } + } +} + +#endif diff --git a/include/boost/thread/recursive_mutex.hpp b/include/boost/thread/recursive_mutex.hpp index a7efa48f..f01ae11b 100644 --- a/include/boost/thread/recursive_mutex.hpp +++ b/include/boost/thread/recursive_mutex.hpp @@ -1,184 +1,15 @@ -// Copyright (C) 2001-2003 -// William E. Kempf +#ifndef BOOST_THREAD_RECURSIVE_MUTEX_HPP +#define BOOST_THREAD_RECURSIVE_MUTEX_HPP + +// recursive_mutex.hpp // -// 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) +// (C) Copyright 2007 Anthony Williams +// +// 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) -#ifndef BOOST_RECURSIVE_MUTEX_WEK070601_HPP -#define BOOST_RECURSIVE_MUTEX_WEK070601_HPP +#include +#include BOOST_THREAD_PLATFORM(recursive_mutex.hpp) -#include - -#include -#include - -#if defined(BOOST_HAS_PTHREADS) -# include #endif - -#if defined(BOOST_HAS_MPTASKS) -# include "scoped_critical_region.hpp" -#endif - -namespace boost { - -struct xtime; -// disable warnings about non dll import -// see: http://www.boost.org/more/separate_compilation.html#dlls -#ifdef BOOST_MSVC -# pragma warning(push) -# pragma warning(disable: 4251 4231 4660 4275) -#endif -class BOOST_THREAD_DECL recursive_mutex - : private noncopyable -{ -public: - friend class detail::thread::lock_ops; - - typedef detail::thread::scoped_lock scoped_lock; - - recursive_mutex(); - ~recursive_mutex(); - -private: -#if (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS)) - typedef std::size_t cv_state; -#elif defined(BOOST_HAS_PTHREADS) - struct cv_state - { - long count; - pthread_mutex_t* pmutex; - }; -#endif - void do_lock(); - void do_unlock(); - void do_lock(cv_state& state); - void do_unlock(cv_state& state); - -#if defined(BOOST_HAS_WINTHREADS) - void* m_mutex; - bool m_critical_section; - unsigned long m_count; -#elif defined(BOOST_HAS_PTHREADS) - pthread_mutex_t m_mutex; - unsigned m_count; -# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - pthread_cond_t m_unlocked; - pthread_t m_thread_id; - bool m_valid_id; -# endif -#elif defined(BOOST_HAS_MPTASKS) - threads::mac::detail::scoped_critical_region m_mutex; - threads::mac::detail::scoped_critical_region m_mutex_mutex; - std::size_t m_count; -#endif -}; - -class BOOST_THREAD_DECL recursive_try_mutex - : private noncopyable -{ -public: - friend class detail::thread::lock_ops; - - typedef detail::thread::scoped_lock scoped_lock; - typedef detail::thread::scoped_try_lock< - recursive_try_mutex> scoped_try_lock; - - recursive_try_mutex(); - ~recursive_try_mutex(); - -private: -#if (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS)) - typedef std::size_t cv_state; -#elif defined(BOOST_HAS_PTHREADS) - struct cv_state - { - long count; - pthread_mutex_t* pmutex; - }; -#endif - void do_lock(); - bool do_trylock(); - void do_unlock(); - void do_lock(cv_state& state); - void do_unlock(cv_state& state); - -#if defined(BOOST_HAS_WINTHREADS) - void* m_mutex; - bool m_critical_section; - unsigned long m_count; -#elif defined(BOOST_HAS_PTHREADS) - pthread_mutex_t m_mutex; - unsigned m_count; -# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - pthread_cond_t m_unlocked; - pthread_t m_thread_id; - bool m_valid_id; -# endif -#elif defined(BOOST_HAS_MPTASKS) - threads::mac::detail::scoped_critical_region m_mutex; - threads::mac::detail::scoped_critical_region m_mutex_mutex; - std::size_t m_count; -#endif -}; - -class BOOST_THREAD_DECL recursive_timed_mutex - : private noncopyable -{ -public: - friend class detail::thread::lock_ops; - - typedef detail::thread::scoped_lock scoped_lock; - typedef detail::thread::scoped_try_lock< - recursive_timed_mutex> scoped_try_lock; - typedef detail::thread::scoped_timed_lock< - recursive_timed_mutex> scoped_timed_lock; - - recursive_timed_mutex(); - ~recursive_timed_mutex(); - -private: -#if (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS)) - typedef std::size_t cv_state; -#elif defined(BOOST_HAS_PTHREADS) - struct cv_state - { - long count; - pthread_mutex_t* pmutex; - }; -#endif - void do_lock(); - bool do_trylock(); - bool do_timedlock(const xtime& xt); - void do_unlock(); - void do_lock(cv_state& state); - void do_unlock(cv_state& state); - -#if defined(BOOST_HAS_WINTHREADS) - void* m_mutex; - unsigned long m_count; -#elif defined(BOOST_HAS_PTHREADS) - pthread_mutex_t m_mutex; - pthread_cond_t m_unlocked; - pthread_t m_thread_id; - bool m_valid_id; - unsigned m_count; -#elif defined(BOOST_HAS_MPTASKS) - threads::mac::detail::scoped_critical_region m_mutex; - threads::mac::detail::scoped_critical_region m_mutex_mutex; - std::size_t m_count; -#endif -}; -#ifdef BOOST_MSVC -# pragma warning(pop) -#endif -} // namespace boost - -#endif // BOOST_RECURSIVE_MUTEX_WEK070601_HPP - -// Change Log: -// 8 Feb 01 WEKEMPF Initial version. -// 1 Jun 01 WEKEMPF Modified to use xtime for time outs. Factored out -// to three classes, mutex, try_mutex and timed_mutex. -// 11 Jun 01 WEKEMPF Modified to use PTHREAD_MUTEX_RECURSIVE if available. -// 3 Jan 03 WEKEMPF Modified for DLL implementation. diff --git a/include/boost/thread/thread_time.hpp b/include/boost/thread/thread_time.hpp new file mode 100644 index 00000000..8c735977 --- /dev/null +++ b/include/boost/thread/thread_time.hpp @@ -0,0 +1,40 @@ +#ifndef BOOST_THREAD_TIME_HPP +#define BOOST_THREAD_TIME_HPP +#include +#include + +namespace boost +{ + typedef boost::posix_time::ptime system_time; + + inline system_time get_system_time() + { + return boost::date_time::microsec_clock::universal_time(); + } + + namespace detail + { + inline system_time get_system_time_sentinel() + { + return system_time(boost::posix_time::pos_infin); + } + + inline unsigned get_milliseconds_until(system_time const& target_time) + { + if(target_time.is_pos_infinity()) + { + return ~0u; + } + system_time const now=get_system_time(); + if(target_time<=now) + { + return 0; + } + return (target_time-now).total_milliseconds(); + } + + } + +} + +#endif diff --git a/include/boost/thread/win32/basic_recursive_mutex.hpp b/include/boost/thread/win32/basic_recursive_mutex.hpp new file mode 100644 index 00000000..e5f79981 --- /dev/null +++ b/include/boost/thread/win32/basic_recursive_mutex.hpp @@ -0,0 +1,120 @@ +#ifndef BOOST_BASIC_RECURSIVE_MUTEX_WIN32_HPP +#define BOOST_BASIC_RECURSIVE_MUTEX_WIN32_HPP + +// basic_recursive_mutex.hpp +// +// (C) Copyright 2006-7 Anthony Williams +// +// 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) + +#include "thread_primitives.hpp" +#include "basic_timed_mutex.hpp" + +namespace boost +{ + namespace detail + { + template + struct basic_recursive_mutex_impl + { + long recursion_count; + long locking_thread_id; + underlying_mutex_type mutex; + + void initialize() + { + recursion_count=0; + locking_thread_id=0; + mutex.initialize(); + } + + void destroy() + { + mutex.destroy(); + } + + bool try_lock() + { + long const current_thread_id=win32::GetCurrentThreadId(); + return try_recursive_lock(current_thread_id) || try_basic_lock(current_thread_id); + } + + void lock() + { + long const current_thread_id=win32::GetCurrentThreadId(); + if(!try_recursive_lock(current_thread_id)) + { + mutex.lock(); + BOOST_INTERLOCKED_EXCHANGE(&locking_thread_id,current_thread_id); + recursion_count=1; + } + } + bool timed_lock(::boost::system_time const& target) + { + long const current_thread_id=win32::GetCurrentThreadId(); + return try_recursive_lock(current_thread_id) || try_timed_lock(current_thread_id,target); + } + long get_active_count() + { + return mutex.get_active_count(); + } + + void unlock() + { + if(!--recursion_count) + { + BOOST_INTERLOCKED_EXCHANGE(&locking_thread_id,0); + mutex.unlock(); + } + } + + bool locked() + { + return mutex.locked(); + } + + private: + bool try_recursive_lock(long current_thread_id) + { + if(::boost::detail::interlocked_read_acquire(&locking_thread_id)==current_thread_id) + { + ++recursion_count; + return true; + } + return false; + } + + bool try_basic_lock(long current_thread_id) + { + if(mutex.try_lock()) + { + BOOST_INTERLOCKED_EXCHANGE(&locking_thread_id,current_thread_id); + recursion_count=1; + return true; + } + return false; + } + + bool try_timed_lock(long current_thread_id,::boost::system_time const& target) + { + if(mutex.timed_lock(target)) + { + BOOST_INTERLOCKED_EXCHANGE(&locking_thread_id,current_thread_id); + recursion_count=1; + return true; + } + return false; + } + + }; + + typedef basic_recursive_mutex_impl basic_recursive_mutex; + typedef basic_recursive_mutex_impl basic_recursive_timed_mutex; + } +} + +#define BOOST_BASIC_RECURSIVE_MUTEX_INITIALIZER {0} + +#endif diff --git a/include/boost/thread/win32/basic_timed_mutex.hpp b/include/boost/thread/win32/basic_timed_mutex.hpp new file mode 100644 index 00000000..bc2b1af0 --- /dev/null +++ b/include/boost/thread/win32/basic_timed_mutex.hpp @@ -0,0 +1,157 @@ +#ifndef BOOST_BASIC_TIMED_MUTEX_WIN32_HPP +#define BOOST_BASIC_TIMED_MUTEX_WIN32_HPP + +// basic_timed_mutex_win32.hpp +// +// (C) Copyright 2006 Anthony Williams +// +// 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) + +#include +#include "thread_primitives.hpp" +#include "interlocked_read.hpp" +#include + +namespace boost +{ + namespace detail + { + struct basic_timed_mutex + { + BOOST_STATIC_CONSTANT(long,lock_flag_value=0x80000000); + long active_count; + void* event; + + void initialize() + { + active_count=0; + event=0; + } + + void destroy() + { + void* const old_event=BOOST_INTERLOCKED_EXCHANGE_POINTER(&event,0); + if(old_event) + { + win32::CloseHandle(old_event); + } + } + + + bool try_lock() + { + long old_count=active_count&~lock_flag_value; + do + { + long const current_count=BOOST_INTERLOCKED_COMPARE_EXCHANGE(&active_count,(old_count+1)|lock_flag_value,old_count); + if(current_count==old_count) + { + return true; + } + old_count=current_count; + } + while(!(old_count&lock_flag_value)); + return false; + } + + void lock() + { + bool const success=timed_lock(::boost::detail::get_system_time_sentinel()); + BOOST_ASSERT(success); + } + bool timed_lock(::boost::system_time const& wait_until) + { + long old_count=active_count; + while(true) + { + long const current_count=BOOST_INTERLOCKED_COMPARE_EXCHANGE(&active_count,(old_count+1)|lock_flag_value,old_count); + if(current_count==old_count) + { + break; + } + old_count=current_count; + } + + if(old_count&lock_flag_value) + { + bool lock_acquired=false; + void* const sem=get_event(); + ++old_count; // we're waiting, too + do + { + old_count-=(lock_flag_value+1); // there will be one less active thread on this mutex when it gets unlocked + if(win32::WaitForSingleObject(sem,::boost::detail::get_milliseconds_until(wait_until))!=0) + { + BOOST_INTERLOCKED_DECREMENT(&active_count); + return false; + } + do + { + long const current_count=BOOST_INTERLOCKED_COMPARE_EXCHANGE(&active_count,old_count|lock_flag_value,old_count); + if(current_count==old_count) + { + break; + } + old_count=current_count; + } + while(!(old_count&lock_flag_value)); + lock_acquired=!(old_count&lock_flag_value); + } + while(!lock_acquired); + } + return true; + } + + long get_active_count() + { + return ::boost::detail::interlocked_read_acquire(&active_count); + } + + void unlock() + { + long const offset=lock_flag_value+1; + long old_count=BOOST_INTERLOCKED_EXCHANGE_ADD(&active_count,-offset); + + if(old_count>offset) + { + win32::SetEvent(get_event()); + } + } + + bool locked() + { + return get_active_count()>=lock_flag_value; + } + + private: + void* get_event() + { + void* current_event=::boost::detail::interlocked_read_acquire(&event); + + if(!current_event) + { + void* const new_event=win32::create_anonymous_event(win32::auto_reset_event,win32::event_initially_reset); + void* const old_event=BOOST_INTERLOCKED_COMPARE_EXCHANGE_POINTER(&event,new_event,0); + if(old_event!=0) + { + win32::CloseHandle(new_event); + return old_event; + } + else + { + return new_event; + } + } + return current_event; + } + + }; + + } +} + +#define BOOST_BASIC_TIMED_MUTEX_INITIALIZER {0} + +#endif diff --git a/include/boost/thread/win32/mutex.hpp b/include/boost/thread/win32/mutex.hpp new file mode 100644 index 00000000..29759a9a --- /dev/null +++ b/include/boost/thread/win32/mutex.hpp @@ -0,0 +1,61 @@ +#ifndef BOOST_THREAD_WIN32_MUTEX_HPP +#define BOOST_THREAD_WIN32_MUTEX_HPP +// (C) Copyright 2005-7 Anthony Williams +// 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) + +#include "basic_timed_mutex.hpp" +#include +#include +#include + +namespace boost +{ + namespace detail + { + typedef ::boost::detail::basic_timed_mutex underlying_mutex; + } + + class mutex: + boost::noncopyable, + public ::boost::detail::underlying_mutex + { + public: + mutex() + { + initialize(); + } + ~mutex() + { + destroy(); + } + + typedef unique_lock scoped_lock; + typedef scoped_lock scoped_try_lock; + }; + + typedef mutex try_mutex; + + class timed_mutex: + boost::noncopyable, + public ::boost::detail::basic_timed_mutex + { + public: + timed_mutex() + { + initialize(); + } + + ~timed_mutex() + { + destroy(); + } + + typedef unique_lock scoped_timed_lock; + typedef scoped_timed_lock scoped_try_lock; + typedef scoped_timed_lock scoped_lock; + }; +} + +#endif diff --git a/include/boost/thread/win32/recursive_mutex.hpp b/include/boost/thread/win32/recursive_mutex.hpp new file mode 100644 index 00000000..855dd15b --- /dev/null +++ b/include/boost/thread/win32/recursive_mutex.hpp @@ -0,0 +1,61 @@ +#ifndef BOOST_RECURSIVE_MUTEX_WIN32_HPP +#define BOOST_RECURSIVE_MUTEX_WIN32_HPP + +// recursive_mutex.hpp +// +// (C) Copyright 2006-7 Anthony Williams +// +// 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) + + +#include +#include "basic_recursive_mutex.hpp" +#include +#include + +namespace boost +{ + class recursive_mutex: + boost::noncopyable, + public ::boost::detail::basic_recursive_mutex + { + public: + recursive_mutex() + { + ::boost::detail::basic_recursive_mutex::initialize(); + } + ~recursive_mutex() + { + ::boost::detail::basic_recursive_mutex::destroy(); + } + + typedef unique_lock scoped_lock; + typedef scoped_lock scoped_try_lock; + }; + + typedef recursive_mutex recursive_try_mutex; + + class recursive_timed_mutex: + boost::noncopyable, + public ::boost::detail::basic_recursive_timed_mutex + { + public: + recursive_timed_mutex() + { + ::boost::detail::basic_recursive_timed_mutex::initialize(); + } + ~recursive_timed_mutex() + { + ::boost::detail::basic_recursive_timed_mutex::destroy(); + } + + typedef unique_lock scoped_timed_lock; + typedef scoped_timed_lock scoped_try_lock; + typedef scoped_timed_lock scoped_lock; + }; +} + + +#endif diff --git a/include/boost/thread/win32/thread_primitives.hpp b/include/boost/thread/win32/thread_primitives.hpp index 2213c7b8..6ada6ae4 100644 --- a/include/boost/thread/win32/thread_primitives.hpp +++ b/include/boost/thread/win32/thread_primitives.hpp @@ -113,13 +113,21 @@ namespace boost inline handle create_anonymous_event(event_type type,initial_event_state state) { handle const res=CreateEventA(0,type,state,0); - return res?res:throw thread_resource_error(); + if(!res) + { + throw thread_resource_error(); + } + return res; } inline handle create_anonymous_semaphore(long initial_count,long max_count) { handle const res=CreateSemaphoreA(NULL,initial_count,max_count,NULL); - return res?res:throw thread_resource_error(); + if(!res) + { + throw thread_resource_error(); + } + return res; } inline handle duplicate_handle(handle source) @@ -128,7 +136,11 @@ namespace boost long const same_access_flag=2; handle new_handle=0; bool const success=DuplicateHandle(current_process,source,current_process,&new_handle,0,false,same_access_flag)!=0; - return success?new_handle:throw thread_resource_error(); + if(!success) + { + throw thread_resource_error(); + } + return new_handle; } inline void release_semaphore(handle semaphore,long count) diff --git a/src/condition.cpp b/src/condition.cpp index ffccad05..18ca0ef8 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -342,6 +342,9 @@ condition_impl::condition_impl() res = pthread_cond_init(&m_condition, 0); if (res != 0) throw thread_resource_error(); + res = pthread_mutex_init(&m_mutex, 0); + if (res != 0) + throw thread_resource_error(); } condition_impl::~condition_impl() @@ -349,20 +352,30 @@ condition_impl::~condition_impl() int res = 0; res = pthread_cond_destroy(&m_condition); assert(res == 0); + res = pthread_mutex_destroy(&m_mutex); + assert(res == 0); } void condition_impl::notify_one() { int res = 0; + res = pthread_mutex_lock(&m_mutex); + assert(res == 0); res = pthread_cond_signal(&m_condition); assert(res == 0); + res = pthread_mutex_unlock(&m_mutex); + assert(res == 0); } void condition_impl::notify_all() { int res = 0; + res = pthread_mutex_lock(&m_mutex); + assert(res == 0); res = pthread_cond_broadcast(&m_condition); assert(res == 0); + res = pthread_mutex_unlock(&m_mutex); + assert(res == 0); } void condition_impl::do_wait(pthread_mutex_t* pmutex) diff --git a/src/thread.cpp b/src/thread.cpp index e96e5829..dc8ff078 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #if defined(BOOST_HAS_WINTHREADS) diff --git a/test/test_mutex.cpp b/test/test_mutex.cpp index 831e6bba..08170fd2 100644 --- a/test/test_mutex.cpp +++ b/test/test_mutex.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include @@ -29,7 +29,7 @@ struct test_lock // Test the lock's constructors. { - lock_type lock(mutex, false); + lock_type lock(mutex, boost::defer_lock); BOOST_CHECK(!lock); } lock_type lock(mutex); @@ -69,10 +69,10 @@ struct test_trylock BOOST_CHECK(lock ? true : false); } { - try_lock_type lock(mutex, false); + try_lock_type lock(mutex, boost::defer_lock); BOOST_CHECK(!lock); } - try_lock_type lock(mutex, true); + try_lock_type lock(mutex); BOOST_CHECK(lock ? true : false); // Construct and initialize an xtime for a fast time out. @@ -110,16 +110,16 @@ struct test_timedlock // Test the lock's constructors. { // Construct and initialize an xtime for a fast time out. - boost::xtime xt = delay(0, 100); + boost::system_time xt = boost::get_system_time()+boost::posix_time::milliseconds(100); timed_lock_type lock(mutex, xt); BOOST_CHECK(lock ? true : false); } { - timed_lock_type lock(mutex, false); + timed_lock_type lock(mutex, boost::defer_lock); BOOST_CHECK(!lock); } - timed_lock_type lock(mutex, true); + timed_lock_type lock(mutex); BOOST_CHECK(lock ? true : false); // Construct and initialize an xtime for a fast time out. @@ -139,8 +139,8 @@ struct test_timedlock BOOST_CHECK(lock ? true : false); lock.unlock(); BOOST_CHECK(!lock); - xt = delay(0, 100); - BOOST_CHECK(lock.timed_lock(xt)); + boost::system_time target = boost::get_system_time()+boost::posix_time::milliseconds(100); + BOOST_CHECK(lock.timed_lock(target)); BOOST_CHECK(lock ? true : false); } }; From 041530a953a7f5125838f035799cd4e08ed208d9 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 8 Oct 2007 15:44:13 +0000 Subject: [PATCH 005/214] added backwards-compatibility overload for call_once [SVN r39785] --- include/boost/thread/once.hpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/include/boost/thread/once.hpp b/include/boost/thread/once.hpp index 7ff1464a..d29efe8f 100644 --- a/include/boost/thread/once.hpp +++ b/include/boost/thread/once.hpp @@ -10,19 +10,14 @@ // http://www.boost.org/LICENSE_1_0.txt) #include -#ifdef BOOST_HAS_MPTASKS -namespace boost { - -typedef long once_flag; -#define BOOST_ONCE_INIT 0 - -void call_once(once_flag& flag, void (*func)()); +#include BOOST_THREAD_PLATFORM(once.hpp) +namespace boost +{ + inline void call_once(void (*func)(),once_flag& flag) + { + call_once(flag,func); + } } -#else -#include BOOST_THREAD_PLATFORM(once.hpp) -#endif - - #endif From a87914ef2375f568e49d6f2c8dae8bcb72d6a589 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 9 Oct 2007 06:59:14 +0000 Subject: [PATCH 006/214] added missing move.hpp header [SVN r39832] --- include/boost/thread/detail/move.hpp | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 include/boost/thread/detail/move.hpp diff --git a/include/boost/thread/detail/move.hpp b/include/boost/thread/detail/move.hpp new file mode 100644 index 00000000..ba4bb655 --- /dev/null +++ b/include/boost/thread/detail/move.hpp @@ -0,0 +1,33 @@ +// 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) +// (C) Copyright 2007 Anthony Williams + +#ifndef BOOST_THREAD_MOVE_HPP +#define BOOST_THREAD_MOVE_HPP + +namespace boost +{ + template + struct move_t + { + T& t; + move_t(T& t_): + t(t_) + {} + + T* operator->() const + { + return &t; + } + }; + + template + move_t move(T& t) + { + return move_t(t); + } +} + + +#endif From 432bd29c1c39f9bb996bfad9783ec61dc2469ef6 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 9 Oct 2007 12:23:09 +0000 Subject: [PATCH 007/214] fixed direction of conditional [SVN r39836] --- include/boost/thread/pthread/mutex.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index 09e5313b..83dec8bf 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -72,7 +72,7 @@ namespace boost { private: pthread_mutex_t m; -#ifdef BOOST_PTHREAD_HAS_TIMEDLOCK +#ifndef BOOST_PTHREAD_HAS_TIMEDLOCK pthread_cond_t cond; bool is_locked; From 16c7cf9b5e6439c4a30d9f80a2ceb61bba593edc Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 9 Oct 2007 12:45:46 +0000 Subject: [PATCH 008/214] fixed typo in pthread_cond_timedwait and ETIMEDOUT [SVN r39838] --- include/boost/thread/pthread/mutex.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index 83dec8bf..97cb3b91 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -193,8 +193,8 @@ namespace boost pthread_mutex_scoped_lock const _(&m); while(is_locked) { - int const cond_res=pthread_cond_timewait(&cond,&m,&timeout); - if(cond_res==ETIMEOUT) + int const cond_res=pthread_cond_timedwait(&cond,&m,&timeout); + if(cond_res==ETIMEDOUT) { return false; } From 55afcf678d5cd4a9e2c6b7d003b4888907cdb1b2 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 9 Oct 2007 14:08:22 +0000 Subject: [PATCH 009/214] fixed typo in pthread_cond_timedwait and ETIMEDOUT [SVN r39839] --- include/boost/thread/pthread/recursive_mutex.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/thread/pthread/recursive_mutex.hpp b/include/boost/thread/pthread/recursive_mutex.hpp index 2ccc3ae1..4a52ea69 100644 --- a/include/boost/thread/pthread/recursive_mutex.hpp +++ b/include/boost/thread/pthread/recursive_mutex.hpp @@ -251,8 +251,8 @@ namespace boost } while(is_locked) { - int const cond_res=pthread_cond_timewait(&cond,&m,&timeout); - if(cond_res==ETIMEOUT) + int const cond_res=pthread_cond_timedwait(&cond,&m,&timeout); + if(cond_res==ETIMEDOUT) { return false; } From 5a4d5ddb9d9bc2819f282e3f1272725f8906b1c4 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 9 Oct 2007 14:44:37 +0000 Subject: [PATCH 010/214] fixed more has-timed-lock backwards conditions [SVN r39841] --- include/boost/thread/pthread/mutex.hpp | 4 ++-- include/boost/thread/pthread/recursive_mutex.hpp | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index 97cb3b91..b8b9c670 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -103,7 +103,7 @@ namespace boost { throw thread_resource_error(); } -#ifdef BOOST_PTHREAD_HAS_TIMEDLOCK +#ifndef BOOST_PTHREAD_HAS_TIMEDLOCK int const res2=pthread_cond_init(&cond,NULL); if(res2) { @@ -118,7 +118,7 @@ namespace boost { int const res=pthread_mutex_destroy(&m); BOOST_ASSERT(!res); -#ifdef BOOST_PTHREAD_HAS_TIMEDLOCK +#ifndef BOOST_PTHREAD_HAS_TIMEDLOCK int const res2=pthread_cond_destroy(&cond); BOOST_ASSERT(!res2); #endif diff --git a/include/boost/thread/pthread/recursive_mutex.hpp b/include/boost/thread/pthread/recursive_mutex.hpp index 4a52ea69..92002600 100644 --- a/include/boost/thread/pthread/recursive_mutex.hpp +++ b/include/boost/thread/pthread/recursive_mutex.hpp @@ -133,6 +133,8 @@ namespace boost int const res=pthread_mutex_init(&m,&attr); if(res) { + int const destroy_attr_res=pthread_mutexattr_destroy(&attr); + BOOST_ASSERT(!destroy_attr_res); throw thread_resource_error(); } int const destroy_attr_res=pthread_mutexattr_destroy(&attr); @@ -247,7 +249,7 @@ namespace boost if(is_locked && owner==pthread_self()) { ++count; - return; + return true; } while(is_locked) { From 291873248103655039d9dd958da2baa7b1e24b1d Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 10 Oct 2007 15:33:49 +0000 Subject: [PATCH 011/214] read_write_mutex makes a comeback --- as shared_mutex [SVN r39891] --- include/boost/thread/locks.hpp | 162 ++- include/boost/thread/pthread/shared_mutex.hpp | 300 +++++ include/boost/thread/shared_mutex.hpp | 15 + include/boost/thread/thread_time.hpp | 2 +- include/boost/thread/win32/shared_mutex.hpp | 516 ++++++++ .../boost/thread/win32/thread_primitives.hpp | 3 +- include/boost/thread/xtime.hpp | 12 + src/mutex.cpp | 561 --------- src/mutex.inl | 132 --- src/once.cpp | 50 - src/recursive_mutex.cpp | 1040 ----------------- test/Jamfile.v2 | 3 +- test/test_lock_concept.cpp | 173 +++ test/test_shared_mutex.cpp | 460 ++++++++ 14 files changed, 1579 insertions(+), 1850 deletions(-) create mode 100644 include/boost/thread/pthread/shared_mutex.hpp create mode 100644 include/boost/thread/shared_mutex.hpp create mode 100644 include/boost/thread/win32/shared_mutex.hpp delete mode 100644 src/mutex.cpp delete mode 100644 src/mutex.inl delete mode 100644 src/once.cpp delete mode 100644 src/recursive_mutex.cpp create mode 100644 test/test_lock_concept.cpp create mode 100644 test/test_shared_mutex.cpp diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 258b0f60..b56c996e 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -24,13 +24,13 @@ namespace boost const adopt_lock_t adopt_lock={}; template - class shareable_lock; + class shared_lock; template class exclusive_lock; template - class upgradeable_lock; + class upgrade_lock; template class lock_guard @@ -86,21 +86,21 @@ namespace boost { timed_lock(target_time); } - unique_lock(boost::move_t other): + unique_lock(boost::move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; } - unique_lock(boost::move_t > other); + unique_lock(boost::move_t > other); - unique_lock& operator=(boost::move_t other) + unique_lock& operator=(boost::move_t > other) { unique_lock temp(other); swap(temp); return *this; } - unique_lock& operator=(boost::move_t > other) + unique_lock& operator=(boost::move_t > other) { unique_lock temp(other); swap(temp); @@ -112,7 +112,7 @@ namespace boost std::swap(m,other.m); std::swap(is_locked,other.is_locked); } - void swap(boost::move_t other) + void swap(boost::move_t > other) { std::swap(m,other->m); std::swap(is_locked,other->is_locked); @@ -170,6 +170,10 @@ namespace boost { return is_locked?&unique_lock::lock:0; } + bool operator!() const + { + return !owns_lock(); + } bool owns_lock() const { return is_locked; @@ -188,91 +192,100 @@ namespace boost return res; } - friend class shareable_lock; - friend class upgradeable_lock; + friend class shared_lock; + friend class upgrade_lock; }; template - class shareable_lock + class shared_lock { protected: Mutex* m; bool is_locked; private: - explicit shareable_lock(shareable_lock&); - shareable_lock& operator=(shareable_lock&); + explicit shared_lock(shared_lock&); + shared_lock& operator=(shared_lock&); public: - explicit shareable_lock(Mutex& m_): + explicit shared_lock(Mutex& m_): m(&m_),is_locked(false) { lock(); } - shareable_lock(Mutex& m_,bool do_lock): + shared_lock(Mutex& m_,adopt_lock_t): + m(&m_),is_locked(true) + {} + shared_lock(Mutex& m_,defer_lock_t): + m(&m_),is_locked(false) + {} + shared_lock(Mutex& m_,try_to_lock_t): m(&m_),is_locked(false) { - if(do_lock) - { - lock(); - } + try_lock(); } - shareable_lock(boost::move_t other): + shared_lock(Mutex& m_,system_time const& target_time): + m(&m_),is_locked(false) + { + timed_lock(target_time); + } + + shared_lock(boost::move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; } - shareable_lock(boost::move_t > other): + shared_lock(boost::move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; if(is_locked) { - m->unlock_and_lock_shareable(); + m->unlock_and_lock_shared(); } } - shareable_lock(boost::move_t > other): + shared_lock(boost::move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; if(is_locked) { - m->unlock_upgradeable_and_lock_shareable(); + m->unlock_upgrade_and_lock_shared(); } } - shareable_lock& operator=(boost::move_t other) + shared_lock& operator=(boost::move_t > other) { - shareable_lock temp(other); + shared_lock temp(other); swap(temp); return *this; } - shareable_lock& operator=(boost::move_t > other) + shared_lock& operator=(boost::move_t > other) { - shareable_lock temp(other); + shared_lock temp(other); swap(temp); return *this; } - shareable_lock& operator=(boost::move_t > other) + shared_lock& operator=(boost::move_t > other) { - shareable_lock temp(other); + shared_lock temp(other); swap(temp); return *this; } - void swap(shareable_lock& other) + void swap(shared_lock& other) { std::swap(m,other.m); std::swap(is_locked,other.is_locked); } - ~shareable_lock() + ~shared_lock() { if(owns_lock()) { - m->unlock_shareable(); + m->unlock_shared(); } } void lock() @@ -281,7 +294,7 @@ namespace boost { throw boost::lock_error(); } - m->lock_shareable(); + m->lock_shared(); is_locked=true; } bool try_lock() @@ -290,7 +303,16 @@ namespace boost { throw boost::lock_error(); } - is_locked=m->try_lock_shareable(); + is_locked=m->try_lock_shared(); + return is_locked; + } + bool timed_lock(boost::system_time const& target_time) + { + if(owns_lock()) + { + throw boost::lock_error(); + } + is_locked=m->timed_lock_shared(target_time); return is_locked; } void unlock() @@ -299,14 +321,18 @@ namespace boost { throw boost::lock_error(); } - m->unlock_shareable(); + m->unlock_shared(); is_locked=false; } - typedef void (shareable_lock::*bool_type)(); + typedef void (shared_lock::*bool_type)(); operator bool_type() const { - return is_locked?&shareable_lock::lock:0; + return is_locked?&shared_lock::lock:0; + } + bool operator!() const + { + return !owns_lock(); } bool owns_lock() const { @@ -316,21 +342,21 @@ namespace boost }; template - class upgradeable_lock + class upgrade_lock { protected: Mutex* m; bool is_locked; private: - explicit upgradeable_lock(upgradeable_lock&); - upgradeable_lock& operator=(upgradeable_lock&); + explicit upgrade_lock(upgrade_lock&); + upgrade_lock& operator=(upgrade_lock&); public: - explicit upgradeable_lock(Mutex& m_): + explicit upgrade_lock(Mutex& m_): m(&m_),is_locked(false) { lock(); } - upgradeable_lock(Mutex& m_,bool do_lock): + upgrade_lock(Mutex& m_,bool do_lock): m(&m_),is_locked(false) { if(do_lock) @@ -338,47 +364,47 @@ namespace boost lock(); } } - upgradeable_lock(boost::move_t other): + upgrade_lock(boost::move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; } - upgradeable_lock(boost::move_t > other): + upgrade_lock(boost::move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; if(is_locked) { - m->unlock_and_lock_upgradeable(); + m->unlock_and_lock_upgrade(); } } - upgradeable_lock& operator=(boost::move_t other) + upgrade_lock& operator=(boost::move_t > other) { - upgradeable_lock temp(other); + upgrade_lock temp(other); swap(temp); return *this; } - upgradeable_lock& operator=(boost::move_t > other) + upgrade_lock& operator=(boost::move_t > other) { - upgradeable_lock temp(other); + upgrade_lock temp(other); swap(temp); return *this; } - void swap(upgradeable_lock& other) + void swap(upgrade_lock& other) { std::swap(m,other.m); std::swap(is_locked,other.is_locked); } - ~upgradeable_lock() + ~upgrade_lock() { if(owns_lock()) { - m->unlock_upgradeable(); + m->unlock_upgrade(); } } void lock() @@ -387,7 +413,7 @@ namespace boost { throw boost::lock_error(); } - m->lock_upgradeable(); + m->lock_upgrade(); is_locked=true; } bool try_lock() @@ -396,7 +422,7 @@ namespace boost { throw boost::lock_error(); } - is_locked=m->try_lock_upgradeable(); + is_locked=m->try_lock_upgrade(); return is_locked; } void unlock() @@ -405,31 +431,35 @@ namespace boost { throw boost::lock_error(); } - m->unlock_upgradeable(); + m->unlock_upgrade(); is_locked=false; } - typedef void (upgradeable_lock::*bool_type)(); + typedef void (upgrade_lock::*bool_type)(); operator bool_type() const { - return is_locked?&upgradeable_lock::lock:0; + return is_locked?&upgrade_lock::lock:0; + } + bool operator!() const + { + return !owns_lock(); } bool owns_lock() const { return is_locked; } - friend class shareable_lock; + friend class shared_lock; friend class unique_lock; }; template - unique_lock::unique_lock(boost::move_t > other): + unique_lock::unique_lock(boost::move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; if(is_locked) { - m->unlock_upgradeable_and_lock(); + m->unlock_upgrade_and_lock(); } } @@ -437,13 +467,13 @@ namespace boost class upgrade_to_unique_lock { private: - upgradeable_lock* source; + upgrade_lock* source; unique_lock exclusive; explicit upgrade_to_unique_lock(upgrade_to_unique_lock&); upgrade_to_unique_lock& operator=(upgrade_to_unique_lock&); public: - explicit upgrade_to_unique_lock(upgradeable_lock& m_): + explicit upgrade_to_unique_lock(upgrade_lock& m_): source(&m_),exclusive(boost::move(*source)) {} ~upgrade_to_unique_lock() @@ -454,13 +484,13 @@ namespace boost } } - upgrade_to_unique_lock(boost::move_t other): + upgrade_to_unique_lock(boost::move_t > other): source(other->source),exclusive(boost::move(other->exclusive)) { other->source=0; } - upgrade_to_unique_lock& operator=(boost::move_t other) + upgrade_to_unique_lock& operator=(boost::move_t > other) { upgrade_to_unique_lock temp(other); swap(temp); @@ -476,6 +506,10 @@ namespace boost { return exclusive.owns_lock()?&upgrade_to_unique_lock::swap:0; } + bool operator!() const + { + return !owns_lock(); + } bool owns_lock() const { return exclusive.owns_lock(); diff --git a/include/boost/thread/pthread/shared_mutex.hpp b/include/boost/thread/pthread/shared_mutex.hpp new file mode 100644 index 00000000..673cfc00 --- /dev/null +++ b/include/boost/thread/pthread/shared_mutex.hpp @@ -0,0 +1,300 @@ +#ifndef BOOST_THREAD_PTHREAD_SHARED_MUTEX_HPP +#define BOOST_THREAD_PTHREAD_SHARED_MUTEX_HPP + +// (C) Copyright 2006-7 Anthony Williams +// +// 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) + +#include +#include +#include +#include +#include + +namespace boost +{ + class shared_mutex + { + private: + struct state_data + { + unsigned shared_count; + bool exclusive; + bool upgrade; + bool exclusive_waiting_blocked; + }; + + + + state_data state; + boost::mutex state_change; + boost::condition shared_cond; + boost::condition exclusive_cond; + boost::condition upgrade_cond; + + void release_waiters() + { + exclusive_cond.notify_one(); + shared_cond.notify_all(); + } + + + public: + shared_mutex() + { + state_data state_={0}; + state=state_; + } + + ~shared_mutex() + { + } + + void lock_shared() + { + boost::mutex::scoped_lock lock(state_change); + + while(true) + { + if(!state.exclusive && !state.exclusive_waiting_blocked) + { + ++state.shared_count; + return; + } + + shared_cond.wait(lock); + } + } + + bool try_lock_shared() + { + boost::mutex::scoped_lock lock(state_change); + + if(state.exclusive || state.exclusive_waiting_blocked) + { + return false; + } + else + { + ++state.shared_count; + return true; + } + } + + bool timed_lock_shared(system_time const& timeout) + { + boost::mutex::scoped_lock lock(state_change); + + while(true) + { + if(!state.exclusive && !state.exclusive_waiting_blocked) + { + ++state.shared_count; + return true; + } + + if(!shared_cond.timed_wait(lock,get_xtime(timeout))) + { + return false; + } + } + } + + void unlock_shared() + { + boost::mutex::scoped_lock lock(state_change); + bool const last_reader=!--state.shared_count; + + if(last_reader) + { + if(state.upgrade) + { + state.upgrade=false; + state.exclusive=true; + upgrade_cond.notify_one(); + } + else + { + state.exclusive_waiting_blocked=false; + } + release_waiters(); + } + } + + void lock() + { + boost::mutex::scoped_lock lock(state_change); + + while(true) + { + if(state.shared_count || state.exclusive) + { + state.exclusive_waiting_blocked=true; + } + else + { + state.exclusive=true; + return; + } + exclusive_cond.wait(lock); + } + } + + bool timed_lock(system_time const& timeout) + { + boost::mutex::scoped_lock lock(state_change); + + while(true) + { + if(state.shared_count || state.exclusive) + { + state.exclusive_waiting_blocked=true; + } + else + { + state.exclusive=true; + return true; + } + if(!exclusive_cond.timed_wait(lock,get_xtime(timeout))) + { + return false; + } + } + } + + bool try_lock() + { + boost::mutex::scoped_lock lock(state_change); + + if(state.shared_count || state.exclusive) + { + return false; + } + else + { + state.exclusive=true; + return true; + } + + } + + void unlock() + { + boost::mutex::scoped_lock lock(state_change); + state.exclusive=false; + state.exclusive_waiting_blocked=false; + release_waiters(); + } + + void lock_upgrade() + { + boost::mutex::scoped_lock lock(state_change); + while(true) + { + if(!state.exclusive && !state.exclusive_waiting_blocked && !state.upgrade) + { + ++state.shared_count; + state.upgrade=true; + return; + } + + shared_cond.wait(lock); + } + } + + bool timed_lock_upgrade(system_time const& timeout) + { + boost::mutex::scoped_lock lock(state_change); + while(true) + { + if(!state.exclusive && !state.exclusive_waiting_blocked && !state.upgrade) + { + ++state.shared_count; + state.upgrade=true; + return true; + } + + if(!shared_cond.timed_wait(lock,get_xtime(timeout))) + { + return false; + } + } + } + + bool try_lock_upgrade() + { + boost::mutex::scoped_lock lock(state_change); + if(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) + { + return false; + } + else + { + ++state.shared_count; + state.upgrade=true; + return true; + } + } + + void unlock_upgrade() + { + boost::mutex::scoped_lock lock(state_change); + state.upgrade=false; + bool const last_reader=!--state.shared_count; + + if(last_reader) + { + state.exclusive_waiting_blocked=false; + release_waiters(); + } + } + + void unlock_upgrade_and_lock() + { + boost::mutex::scoped_lock lock(state_change); + --state.shared_count; + while(true) + { + if(!state.shared_count) + { + state.upgrade=false; + state.exclusive=true; + break; + } + upgrade_cond.wait(lock); + } + } + + void unlock_and_lock_upgrade() + { + boost::mutex::scoped_lock lock(state_change); + state.exclusive=false; + state.upgrade=true; + ++state.shared_count; + state.exclusive_waiting_blocked=false; + release_waiters(); + } + + void unlock_and_lock_shared() + { + boost::mutex::scoped_lock lock(state_change); + state.exclusive=false; + ++state.shared_count; + state.exclusive_waiting_blocked=false; + release_waiters(); + } + + void unlock_upgrade_and_lock_shared() + { + boost::mutex::scoped_lock lock(state_change); + state.upgrade=false; + state.exclusive_waiting_blocked=false; + release_waiters(); + } + }; +} + + +#endif diff --git a/include/boost/thread/shared_mutex.hpp b/include/boost/thread/shared_mutex.hpp new file mode 100644 index 00000000..17f72d84 --- /dev/null +++ b/include/boost/thread/shared_mutex.hpp @@ -0,0 +1,15 @@ +#ifndef BOOST_THREAD_SHARED_MUTEX_HPP +#define BOOST_THREAD_SHARED_MUTEX_HPP + +// shared_mutex.hpp +// +// (C) Copyright 2007 Anthony Williams +// +// 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) + +#include +#include BOOST_THREAD_PLATFORM(shared_mutex.hpp) + +#endif diff --git a/include/boost/thread/thread_time.hpp b/include/boost/thread/thread_time.hpp index 8c735977..017a112f 100644 --- a/include/boost/thread/thread_time.hpp +++ b/include/boost/thread/thread_time.hpp @@ -30,7 +30,7 @@ namespace boost { return 0; } - return (target_time-now).total_milliseconds(); + return (target_time-now).total_milliseconds()+1; } } diff --git a/include/boost/thread/win32/shared_mutex.hpp b/include/boost/thread/win32/shared_mutex.hpp new file mode 100644 index 00000000..2b0cf8d8 --- /dev/null +++ b/include/boost/thread/win32/shared_mutex.hpp @@ -0,0 +1,516 @@ +#ifndef BOOST_THREAD_WIN32_SHARED_MUTEX_HPP +#define BOOST_THREAD_WIN32_SHARED_MUTEX_HPP + +// (C) Copyright 2006-7 Anthony Williams +// +// 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) + +#include +#include +#include +#include +#include +#include +#include + +namespace boost +{ + class shared_mutex: + private boost::noncopyable + { + private: + struct state_data + { + unsigned shared_count:11; + unsigned shared_waiting:11; + unsigned exclusive:1; + unsigned upgrade:1; + unsigned exclusive_waiting:7; + unsigned exclusive_waiting_blocked:1; + + friend bool operator==(state_data const& lhs,state_data const& rhs) + { + return *reinterpret_cast(&lhs)==*reinterpret_cast(&rhs); + } + }; + + + template + T interlocked_compare_exchange(T* target,T new_value,T comparand) + { + BOOST_STATIC_ASSERT(sizeof(T)==sizeof(long)); + long const res=BOOST_INTERLOCKED_COMPARE_EXCHANGE(reinterpret_cast(target), + *reinterpret_cast(&new_value), + *reinterpret_cast(&comparand)); + return *reinterpret_cast(&res); + } + + state_data state; + void* semaphores[2]; + void* &unlock_sem; + void* &exclusive_sem; + void* upgrade_sem; + + void release_waiters(state_data old_state) + { + if(old_state.exclusive_waiting) + { + bool const success=detail::win32::ReleaseSemaphore(exclusive_sem,1,NULL)!=0; + BOOST_ASSERT(success); + } + + if(old_state.shared_waiting || old_state.exclusive_waiting) + { + bool const success=detail::win32::ReleaseSemaphore(unlock_sem,old_state.shared_waiting + (old_state.exclusive_waiting?1:0),NULL)!=0; + BOOST_ASSERT(success); + } + } + + + public: + shared_mutex(): + unlock_sem(semaphores[0]), + exclusive_sem(semaphores[1]) + { + unlock_sem=detail::win32::create_anonymous_semaphore(0,LONG_MAX); + exclusive_sem=detail::win32::create_anonymous_semaphore(0,LONG_MAX); + upgrade_sem=detail::win32::create_anonymous_semaphore(0,LONG_MAX); + state_data state_={0}; + state=state_; + } + + ~shared_mutex() + { + detail::win32::CloseHandle(upgrade_sem); + detail::win32::CloseHandle(unlock_sem); + detail::win32::CloseHandle(exclusive_sem); + } + + bool try_lock_shared() + { + state_data old_state=state; + do + { + state_data new_state=old_state; + if(!new_state.exclusive && !new_state.exclusive_waiting_blocked) + { + ++new_state.shared_count; + } + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + break; + } + old_state=current_state; + } + while(true); + return !(old_state.exclusive| old_state.exclusive_waiting_blocked); + } + + void lock_shared() + { + bool const success=timed_lock_shared(::boost::detail::get_system_time_sentinel()); + BOOST_ASSERT(success); + } + + bool timed_lock_shared(boost::system_time const& wait_until) + { + while(true) + { + state_data old_state=state; + do + { + state_data new_state=old_state; + if(new_state.exclusive || new_state.exclusive_waiting_blocked) + { + ++new_state.shared_waiting; + } + else + { + ++new_state.shared_count; + } + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + break; + } + old_state=current_state; + } + while(true); + + if(!(old_state.exclusive| old_state.exclusive_waiting_blocked)) + { + return true; + } + + unsigned long const res=detail::win32::WaitForSingleObject(unlock_sem,::boost::detail::get_milliseconds_until(wait_until)); + if(res==detail::win32::timeout) + { + do + { + state_data new_state=old_state; + if(new_state.exclusive || new_state.exclusive_waiting_blocked) + { + if(new_state.shared_waiting) + { + --new_state.shared_waiting; + } + } + else + { + ++new_state.shared_count; + } + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + break; + } + old_state=current_state; + } + while(true); + + if(!(old_state.exclusive| old_state.exclusive_waiting_blocked)) + { + return true; + } + return false; + } + + BOOST_ASSERT(res==0); + } + } + + void unlock_shared() + { + state_data old_state=state; + do + { + state_data new_state=old_state; + bool const last_reader=!--new_state.shared_count; + + if(last_reader) + { + if(new_state.upgrade) + { + new_state.upgrade=false; + new_state.exclusive=true; + } + else + { + if(new_state.exclusive_waiting) + { + --new_state.exclusive_waiting; + new_state.exclusive_waiting_blocked=false; + } + new_state.shared_waiting=0; + } + } + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + if(last_reader) + { + if(old_state.upgrade) + { + bool const success=detail::win32::ReleaseSemaphore(upgrade_sem,1,NULL)!=0; + BOOST_ASSERT(success); + } + else + { + release_waiters(old_state); + } + } + break; + } + old_state=current_state; + } + while(true); + } + + void lock() + { + bool const success=timed_lock(::boost::detail::get_system_time_sentinel()); + BOOST_ASSERT(success); + } + + bool timed_lock(boost::system_time const& wait_until) + { + while(true) + { + state_data old_state=state; + + do + { + state_data new_state=old_state; + if(new_state.shared_count || new_state.exclusive) + { + ++new_state.exclusive_waiting; + new_state.exclusive_waiting_blocked=true; + } + else + { + new_state.exclusive=true; + } + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + break; + } + old_state=current_state; + } + while(true); + + if(!old_state.shared_count && !old_state.exclusive) + { + return true; + } + unsigned long const wait_res=detail::win32::WaitForMultipleObjects(2,semaphores,true,::boost::detail::get_milliseconds_until(wait_until)); + if(wait_res==detail::win32::timeout) + { + do + { + state_data new_state=old_state; + if(new_state.shared_count || new_state.exclusive) + { + if(new_state.exclusive_waiting) + { + --new_state.exclusive_waiting; + } + } + else + { + new_state.exclusive=true; + } + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + break; + } + old_state=current_state; + } + while(true); + if(!old_state.shared_count && !old_state.exclusive) + { + return true; + } + return false; + } + BOOST_ASSERT(wait_res<2); + } + } + + void unlock() + { + state_data old_state=state; + do + { + state_data new_state=old_state; + new_state.exclusive=false; + if(new_state.exclusive_waiting) + { + --new_state.exclusive_waiting; + new_state.exclusive_waiting_blocked=false; + } + new_state.shared_waiting=0; + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + break; + } + old_state=current_state; + } + while(true); + release_waiters(old_state); + } + + void lock_upgrade() + { + while(true) + { + state_data old_state=state; + do + { + state_data new_state=old_state; + if(new_state.exclusive || new_state.exclusive_waiting_blocked || new_state.upgrade) + { + ++new_state.shared_waiting; + } + else + { + ++new_state.shared_count; + new_state.upgrade=true; + } + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + break; + } + old_state=current_state; + } + while(true); + + if(!(old_state.exclusive|| old_state.exclusive_waiting_blocked|| old_state.upgrade)) + { + return; + } + + unsigned long const res=detail::win32::WaitForSingleObject(unlock_sem,detail::win32::infinite); + BOOST_ASSERT(res==0); + } + } + + void unlock_upgrade() + { + state_data old_state=state; + do + { + state_data new_state=old_state; + new_state.upgrade=false; + bool const last_reader=!--new_state.shared_count; + + if(last_reader) + { + if(new_state.exclusive_waiting) + { + --new_state.exclusive_waiting; + new_state.exclusive_waiting_blocked=false; + } + new_state.shared_waiting=0; + } + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + if(last_reader) + { + release_waiters(old_state); + } + break; + } + old_state=current_state; + } + while(true); + } + + void unlock_upgrade_and_lock() + { + state_data old_state=state; + do + { + state_data new_state=old_state; + bool const last_reader=!--new_state.shared_count; + + if(last_reader) + { + new_state.upgrade=false; + new_state.exclusive=true; + } + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + if(!last_reader) + { + unsigned long const res=detail::win32::WaitForSingleObject(upgrade_sem,detail::win32::infinite); + BOOST_ASSERT(res==0); + } + break; + } + old_state=current_state; + } + while(true); + } + + void unlock_and_lock_upgrade() + { + state_data old_state=state; + do + { + state_data new_state=old_state; + new_state.exclusive=false; + new_state.upgrade=true; + ++new_state.shared_count; + if(new_state.exclusive_waiting) + { + --new_state.exclusive_waiting; + new_state.exclusive_waiting_blocked=false; + } + new_state.shared_waiting=0; + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + break; + } + old_state=current_state; + } + while(true); + release_waiters(old_state); + } + + void unlock_and_lock_shared() + { + state_data old_state=state; + do + { + state_data new_state=old_state; + new_state.exclusive=false; + ++new_state.shared_count; + if(new_state.exclusive_waiting) + { + --new_state.exclusive_waiting; + new_state.exclusive_waiting_blocked=false; + } + new_state.shared_waiting=0; + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + break; + } + old_state=current_state; + } + while(true); + release_waiters(old_state); + } + + void unlock_upgrade_and_lock_shared() + { + state_data old_state=state; + do + { + state_data new_state=old_state; + new_state.upgrade=false; + if(new_state.exclusive_waiting) + { + --new_state.exclusive_waiting; + new_state.exclusive_waiting_blocked=false; + } + new_state.shared_waiting=0; + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + break; + } + old_state=current_state; + } + while(true); + release_waiters(old_state); + } + + }; +} + + +#endif diff --git a/include/boost/thread/win32/thread_primitives.hpp b/include/boost/thread/win32/thread_primitives.hpp index 6ada6ae4..604263e1 100644 --- a/include/boost/thread/win32/thread_primitives.hpp +++ b/include/boost/thread/win32/thread_primitives.hpp @@ -112,7 +112,7 @@ namespace boost inline handle create_anonymous_event(event_type type,initial_event_state state) { - handle const res=CreateEventA(0,type,state,0); + handle const res=win32::CreateEventA(0,type,state,0); if(!res) { throw thread_resource_error(); @@ -177,6 +177,7 @@ namespace boost { cleanup(); handle_to_manage=new_handle; + return *this; } operator handle() const diff --git a/include/boost/thread/xtime.hpp b/include/boost/thread/xtime.hpp index 5f66c629..a8fa1c9f 100644 --- a/include/boost/thread/xtime.hpp +++ b/include/boost/thread/xtime.hpp @@ -10,6 +10,8 @@ #include #include +#include +#include namespace boost { @@ -49,6 +51,16 @@ inline int xtime_cmp(const xtime& xt1, const xtime& xt2) return (xt1.sec > xt2.sec) ? 1 : -1; } +inline xtime get_xtime(boost::system_time const& abs_time) +{ + xtime res={0}; + boost::posix_time::time_duration const time_since_epoch=abs_time-boost::posix_time::from_time_t(0); + + res.sec=time_since_epoch.total_seconds(); + res.nsec=time_since_epoch.fractional_seconds()*(1000000000/time_since_epoch.ticks_per_second()); + return res; +} + } // namespace boost #endif //BOOST_XTIME_WEK070601_HPP diff --git a/src/mutex.cpp b/src/mutex.cpp deleted file mode 100644 index bb73b9ce..00000000 --- a/src/mutex.cpp +++ /dev/null @@ -1,561 +0,0 @@ -// Copyright (C) 2001-2003 -// William E. Kempf -// -// 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) - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include "timeconv.inl" - -#if defined(BOOST_HAS_WINTHREADS) -# include -# include -# include -# include -# include "mutex.inl" -#elif defined(BOOST_HAS_PTHREADS) -# include -#elif defined(BOOST_HAS_MPTASKS) -# include -# include "mac/init.hpp" -# include "mac/safe.hpp" -#endif - -namespace boost { - -#if defined(BOOST_HAS_WINTHREADS) - -mutex::mutex() - : m_mutex(0) - , m_critical_section(false) -{ - m_critical_section = true; - if (m_critical_section) - m_mutex = new_critical_section(); - else - m_mutex = new_mutex(0); -} - -mutex::~mutex() -{ - if (m_critical_section) - delete_critical_section(m_mutex); - else - delete_mutex(m_mutex); -} - -void mutex::do_lock() -{ - if (m_critical_section) - wait_critical_section_infinite(m_mutex); - else - wait_mutex(m_mutex, INFINITE); -} - -void mutex::do_unlock() -{ - if (m_critical_section) - release_critical_section(m_mutex); - else - release_mutex(m_mutex); -} - -void mutex::do_lock(cv_state&) -{ - do_lock(); -} - -void mutex::do_unlock(cv_state&) -{ - do_unlock(); -} - -try_mutex::try_mutex() - : m_mutex(0) - , m_critical_section(false) -{ - m_critical_section = has_TryEnterCriticalSection(); - if (m_critical_section) - m_mutex = new_critical_section(); - else - m_mutex = new_mutex(0); -} - -try_mutex::~try_mutex() -{ - if (m_critical_section) - delete_critical_section(m_mutex); - else - delete_mutex(m_mutex); -} - -void try_mutex::do_lock() -{ - if (m_critical_section) - wait_critical_section_infinite(m_mutex); - else - wait_mutex(m_mutex, INFINITE); -} - -bool try_mutex::do_trylock() -{ - if (m_critical_section) - return wait_critical_section_try(m_mutex); - else - return wait_mutex(m_mutex, 0) == WAIT_OBJECT_0; -} - -void try_mutex::do_unlock() -{ - if (m_critical_section) - release_critical_section(m_mutex); - else - release_mutex(m_mutex); -} - -void try_mutex::do_lock(cv_state&) -{ - do_lock(); -} - -void try_mutex::do_unlock(cv_state&) -{ - do_unlock(); -} - -timed_mutex::timed_mutex() - : m_mutex(0) -{ - m_mutex = new_mutex(0); -} - -timed_mutex::~timed_mutex() -{ - delete_mutex(m_mutex); -} - -void timed_mutex::do_lock() -{ - wait_mutex(m_mutex, INFINITE); -} - -bool timed_mutex::do_trylock() -{ - return wait_mutex(m_mutex, 0) == WAIT_OBJECT_0; -} - -bool timed_mutex::do_timedlock(const xtime& xt) -{ - for (;;) - { - int milliseconds; - to_duration(xt, milliseconds); - - int res = wait_mutex(m_mutex, milliseconds); - - if (res == WAIT_TIMEOUT) - { - boost::xtime cur; - boost::xtime_get(&cur, boost::TIME_UTC); - if (boost::xtime_cmp(xt, cur) > 0) - continue; - } - - return res == WAIT_OBJECT_0; - } -} - -void timed_mutex::do_unlock() -{ - release_mutex(m_mutex); -} - -void timed_mutex::do_lock(cv_state&) -{ - do_lock(); -} - -void timed_mutex::do_unlock(cv_state&) -{ - do_unlock(); -} - -#elif defined(BOOST_HAS_PTHREADS) - -mutex::mutex() -{ - int res = 0; - res = pthread_mutex_init(&m_mutex, 0); - if (res != 0) - throw thread_resource_error(); -} - -mutex::~mutex() -{ - int res = 0; - res = pthread_mutex_destroy(&m_mutex); - assert(res == 0); -} - -void mutex::do_lock() -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - if (res == EDEADLK) throw lock_error(); - assert(res == 0); -} - -void mutex::do_unlock() -{ - int res = 0; - res = pthread_mutex_unlock(&m_mutex); - if (res == EPERM) throw lock_error(); - assert(res == 0); -} - -void mutex::do_lock(cv_state&) -{ -} - -void mutex::do_unlock(cv_state& state) -{ - state.pmutex = &m_mutex; -} - -try_mutex::try_mutex() -{ - int res = 0; - res = pthread_mutex_init(&m_mutex, 0); - if (res != 0) - throw thread_resource_error(); -} - -try_mutex::~try_mutex() -{ - int res = 0; - res = pthread_mutex_destroy(&m_mutex); - assert(res == 0); -} - -void try_mutex::do_lock() -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - if (res == EDEADLK) throw lock_error(); - assert(res == 0); -} - -bool try_mutex::do_trylock() -{ - int res = 0; - res = pthread_mutex_trylock(&m_mutex); - if (res == EDEADLK) throw lock_error(); - assert(res == 0 || res == EBUSY); - return res == 0; -} - -void try_mutex::do_unlock() -{ - int res = 0; - res = pthread_mutex_unlock(&m_mutex); - if (res == EPERM) throw lock_error(); - assert(res == 0); -} - -void try_mutex::do_lock(cv_state&) -{ -} - -void try_mutex::do_unlock(cv_state& state) -{ - state.pmutex = &m_mutex; -} - -timed_mutex::timed_mutex() - : m_locked(false) -{ - int res = 0; - res = pthread_mutex_init(&m_mutex, 0); - if (res != 0) - throw thread_resource_error(); - - res = pthread_cond_init(&m_condition, 0); - if (res != 0) - { - pthread_mutex_destroy(&m_mutex); - throw thread_resource_error(); - } -} - -timed_mutex::~timed_mutex() -{ - assert(!m_locked); - int res = 0; - res = pthread_mutex_destroy(&m_mutex); - assert(res == 0); - - res = pthread_cond_destroy(&m_condition); - assert(res == 0); -} - -void timed_mutex::do_lock() -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - while (m_locked) - { - res = pthread_cond_wait(&m_condition, &m_mutex); - assert(res == 0); - } - - assert(!m_locked); - m_locked = true; - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); -} - -bool timed_mutex::do_trylock() -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - bool ret = false; - if (!m_locked) - { - m_locked = true; - ret = true; - } - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - return ret; -} - -bool timed_mutex::do_timedlock(const xtime& xt) -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - timespec ts; - to_timespec(xt, ts); - - while (m_locked) - { - res = pthread_cond_timedwait(&m_condition, &m_mutex, &ts); - assert(res == 0 || res == ETIMEDOUT); - - if (res == ETIMEDOUT) - break; - } - - bool ret = false; - if (!m_locked) - { - m_locked = true; - ret = true; - } - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - return ret; -} - -void timed_mutex::do_unlock() -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - assert(m_locked); - m_locked = false; - - res = pthread_cond_signal(&m_condition); - assert(res == 0); - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); -} - -void timed_mutex::do_lock(cv_state&) -{ - int res = 0; - while (m_locked) - { - res = pthread_cond_wait(&m_condition, &m_mutex); - assert(res == 0); - } - - assert(!m_locked); - m_locked = true; - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); -} - -void timed_mutex::do_unlock(cv_state& state) -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - assert(m_locked); - m_locked = false; - - res = pthread_cond_signal(&m_condition); - assert(res == 0); - - state.pmutex = &m_mutex; -} - -#elif defined(BOOST_HAS_MPTASKS) - -using threads::mac::detail::safe_enter_critical_region; - -mutex::mutex() -{ -} - -mutex::~mutex() -{ -} - -void mutex::do_lock() -{ - OSStatus lStatus = noErr; - lStatus = safe_enter_critical_region(m_mutex, kDurationForever, - m_mutex_mutex); - assert(lStatus == noErr); -} - -void mutex::do_unlock() -{ - OSStatus lStatus = noErr; - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); -} - -void mutex::do_lock(cv_state& /*state*/) -{ - do_lock(); -} - -void mutex::do_unlock(cv_state& /*state*/) -{ - do_unlock(); -} - -try_mutex::try_mutex() -{ -} - -try_mutex::~try_mutex() -{ -} - -void try_mutex::do_lock() -{ - OSStatus lStatus = noErr; - lStatus = safe_enter_critical_region(m_mutex, kDurationForever, - m_mutex_mutex); - assert(lStatus == noErr); -} - -bool try_mutex::do_trylock() -{ - OSStatus lStatus = noErr; - lStatus = MPEnterCriticalRegion(m_mutex, kDurationImmediate); - assert(lStatus == noErr || lStatus == kMPTimeoutErr); - return lStatus == noErr; -} - -void try_mutex::do_unlock() -{ - OSStatus lStatus = noErr; - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); -} - -void try_mutex::do_lock(cv_state& /*state*/) -{ - do_lock(); -} - -void try_mutex::do_unlock(cv_state& /*state*/) -{ - do_unlock(); -} - -timed_mutex::timed_mutex() -{ -} - -timed_mutex::~timed_mutex() -{ -} - -void timed_mutex::do_lock() -{ - OSStatus lStatus = noErr; - lStatus = safe_enter_critical_region(m_mutex, kDurationForever, - m_mutex_mutex); - assert(lStatus == noErr); -} - -bool timed_mutex::do_trylock() -{ - OSStatus lStatus = noErr; - lStatus = MPEnterCriticalRegion(m_mutex, kDurationImmediate); - assert(lStatus == noErr || lStatus == kMPTimeoutErr); - return(lStatus == noErr); -} - -bool timed_mutex::do_timedlock(const xtime& xt) -{ - int microseconds; - to_microduration(xt, microseconds); - Duration lDuration = kDurationMicrosecond * microseconds; - - OSStatus lStatus = noErr; - lStatus = safe_enter_critical_region(m_mutex, lDuration, m_mutex_mutex); - assert(lStatus == noErr || lStatus == kMPTimeoutErr); - - return(lStatus == noErr); -} - -void timed_mutex::do_unlock() -{ - OSStatus lStatus = noErr; - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); -} - -void timed_mutex::do_lock(cv_state& /*state*/) -{ - do_lock(); -} - -void timed_mutex::do_unlock(cv_state& /*state*/) -{ - do_unlock(); -} - -#endif - -} // namespace boost - -// Change Log: -// 8 Feb 01 WEKEMPF Initial version. diff --git a/src/mutex.inl b/src/mutex.inl deleted file mode 100644 index b2c625e0..00000000 --- a/src/mutex.inl +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (C) 2001-2003 -// William E. Kempf -// -// 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) -// boostinspect:nounnamed - -namespace { - -#if defined(BOOST_HAS_WINTHREADS) -//:PREVENT THIS FROM BEING DUPLICATED -typedef BOOL (WINAPI* TryEnterCriticalSection_type)(LPCRITICAL_SECTION lpCriticalSection); -TryEnterCriticalSection_type g_TryEnterCriticalSection = 0; -boost::once_flag once_init_TryEnterCriticalSection = BOOST_ONCE_INIT; - -void init_TryEnterCriticalSection() -{ - //TryEnterCriticalSection is only available on WinNT 4.0 or later; - //it is not available on Win9x. - - OSVERSIONINFO version_info = {sizeof(OSVERSIONINFO)}; - ::GetVersionEx(&version_info); - if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT && - version_info.dwMajorVersion >= 4) - { - if (HMODULE kernel_module = GetModuleHandle(TEXT("KERNEL32.DLL"))) - { - g_TryEnterCriticalSection = reinterpret_cast( -#if defined(BOOST_NO_ANSI_APIS) - GetProcAddressW(kernel_module, L"TryEnterCriticalSection") -#else - GetProcAddress(kernel_module, "TryEnterCriticalSection") -#endif - ); - } - } -} - -inline bool has_TryEnterCriticalSection() -{ - boost::call_once(once_init_TryEnterCriticalSection, init_TryEnterCriticalSection); - return g_TryEnterCriticalSection != 0; -} - -inline HANDLE mutex_cast(void* p) -{ - return reinterpret_cast(p); -} - -inline LPCRITICAL_SECTION critical_section_cast(void* p) -{ - return reinterpret_cast(p); -} - -inline void* new_critical_section() -{ - try - { - LPCRITICAL_SECTION critical_section = new CRITICAL_SECTION; - if (critical_section == 0) throw boost::thread_resource_error(); - InitializeCriticalSection(critical_section); - return critical_section; - } - catch(...) - { - throw boost::thread_resource_error(); - } -} - -inline void* new_mutex(const char* name) -{ -#if defined(BOOST_NO_ANSI_APIS) - int const num_wide_chars = ::MultiByteToWideChar(CP_ACP, 0, name, -1, 0, 0); - LPWSTR const wide_name = (LPWSTR)_alloca( (num_wide_chars+1) * 2 ); - int const res=::MultiByteToWideChar(CP_ACP, 0, name, -1, wide_name, num_wide_chars); - if(!res) - throw boost::thread_resource_error(); - HANDLE mutex = CreateMutexW(0, 0, wide_name); -#else - HANDLE mutex = CreateMutexA(0, 0, name); -#endif - if (mutex == 0 || mutex == INVALID_HANDLE_VALUE) //:xxx (check for both values?) - throw boost::thread_resource_error(); - return reinterpret_cast(mutex); -} - -inline void delete_critical_section(void* mutex) -{ - DeleteCriticalSection(critical_section_cast(mutex)); - delete critical_section_cast(mutex); -} - -inline void delete_mutex(void* mutex) -{ - int res = 0; - res = CloseHandle(mutex_cast(mutex)); - assert(res); -} - -inline void wait_critical_section_infinite(void* mutex) -{ - EnterCriticalSection(critical_section_cast(mutex)); //:xxx Can throw an exception under low memory conditions -} - -inline bool wait_critical_section_try(void* mutex) -{ - BOOL res = g_TryEnterCriticalSection(critical_section_cast(mutex)); - return res != 0; -} - -inline int wait_mutex(void* mutex, int time) -{ - unsigned int res = 0; - res = WaitForSingleObject(mutex_cast(mutex), time); -//:xxx assert(res != WAIT_FAILED && res != WAIT_ABANDONED); - return res; -} - -inline void release_critical_section(void* mutex) -{ - LeaveCriticalSection(critical_section_cast(mutex)); -} - -inline void release_mutex(void* mutex) -{ - BOOL res = FALSE; - res = ReleaseMutex(mutex_cast(mutex)); - assert(res); -} -#endif - -} diff --git a/src/once.cpp b/src/once.cpp deleted file mode 100644 index 0215995f..00000000 --- a/src/once.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2001-2003 -// William E. Kempf -// Copyright (C) 2007 Anthony Williams -// -// 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) - -#include -#ifdef BOOST_HAS_MPTASKS - -#include - -#include -#include -#include - -#include - -namespace { -void *remote_call_proxy(void *pData) -{ - std::pair &rData( - *reinterpret_cast *>(pData)); - - if(*rData.second == false) - { - rData.first(); - *rData.second = true; - } - - return(NULL); -} -} - -namespace boost { - - void call_once(once_flag& flag, void (*func)()) -{ - if(flag == false) - { - // all we do here is make a remote call to blue, as blue is not - // reentrant. - std::pair sData(func, &flag); - MPRemoteCall(remote_call_proxy, &sData, kMPOwningProcessRemoteContext); - assert(flag == true); - } -} - -} -#endif diff --git a/src/recursive_mutex.cpp b/src/recursive_mutex.cpp deleted file mode 100644 index ebe17c1a..00000000 --- a/src/recursive_mutex.cpp +++ /dev/null @@ -1,1040 +0,0 @@ -// Copyright (C) 2001-2003 -// William E. Kempf -// -// 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) - -#include - -#include -#include -#include -#include -#include -#include -#include -#include "timeconv.inl" - -#if defined(BOOST_HAS_WINTHREADS) -# include -# include -# include -# include -# include "mutex.inl" -#elif defined(BOOST_HAS_PTHREADS) -# include -#elif defined(BOOST_HAS_MPTASKS) -# include -# include "safe.hpp" -#endif - -namespace boost { - -#if defined(BOOST_HAS_WINTHREADS) - -recursive_mutex::recursive_mutex() - : m_mutex(0) - , m_critical_section(false) - , m_count(0) -{ - m_critical_section = true; - if (m_critical_section) - m_mutex = new_critical_section(); - else - m_mutex = new_mutex(0); -} - -recursive_mutex::~recursive_mutex() -{ - if (m_critical_section) - delete_critical_section(m_mutex); - else - delete_mutex(m_mutex); -} - -void recursive_mutex::do_lock() -{ - if (m_critical_section) - wait_critical_section_infinite(m_mutex); - else - wait_mutex(m_mutex, INFINITE); - - if (++m_count > 1) - { - if (m_critical_section) - release_critical_section(m_mutex); - else - release_mutex(m_mutex); - } -} - -void recursive_mutex::do_unlock() -{ - if (--m_count == 0) - { - if (m_critical_section) - release_critical_section(m_mutex); - else - release_mutex(m_mutex); - } -} - -void recursive_mutex::do_lock(cv_state& state) -{ - if (m_critical_section) - wait_critical_section_infinite(m_mutex); - else - wait_mutex(m_mutex, INFINITE); - - m_count = state; -} - -void recursive_mutex::do_unlock(cv_state& state) -{ - state = m_count; - m_count = 0; - - if (m_critical_section) - release_critical_section(m_mutex); - else - release_mutex(m_mutex); -} - -recursive_try_mutex::recursive_try_mutex() - : m_mutex(0) - , m_critical_section(false) - , m_count(0) -{ - m_critical_section = has_TryEnterCriticalSection(); - if (m_critical_section) - m_mutex = new_critical_section(); - else - m_mutex = new_mutex(0); -} - -recursive_try_mutex::~recursive_try_mutex() -{ - if (m_critical_section) - delete_critical_section(m_mutex); - else - delete_mutex(m_mutex); -} - -void recursive_try_mutex::do_lock() -{ - if (m_critical_section) - wait_critical_section_infinite(m_mutex); - else - wait_mutex(m_mutex, INFINITE); - - if (++m_count > 1) - { - if (m_critical_section) - release_critical_section(m_mutex); - else - release_mutex(m_mutex); - } -} - -bool recursive_try_mutex::do_trylock() -{ - bool res = false; - if (m_critical_section) - res = wait_critical_section_try(m_mutex); - else - res = wait_mutex(m_mutex, 0) == WAIT_OBJECT_0; - - if (res) - { - if (++m_count > 1) - { - if (m_critical_section) - release_critical_section(m_mutex); - else - release_mutex(m_mutex); - } - return true; - } - return false; -} - -void recursive_try_mutex::do_unlock() -{ - if (--m_count == 0) - { - if (m_critical_section) - release_critical_section(m_mutex); - else - release_mutex(m_mutex); - } -} - -void recursive_try_mutex::do_lock(cv_state& state) -{ - if (m_critical_section) - wait_critical_section_infinite(m_mutex); - else - wait_mutex(m_mutex, INFINITE); - - m_count = state; -} - -void recursive_try_mutex::do_unlock(cv_state& state) -{ - state = m_count; - m_count = 0; - - if (m_critical_section) - release_critical_section(m_mutex); - else - release_mutex(m_mutex); -} - -recursive_timed_mutex::recursive_timed_mutex() - : m_mutex(0) - , m_count(0) -{ - m_mutex = new_mutex(0); -} - -recursive_timed_mutex::~recursive_timed_mutex() -{ - delete_mutex(m_mutex); -} - -void recursive_timed_mutex::do_lock() -{ - wait_mutex(m_mutex, INFINITE); - - if (++m_count > 1) - release_mutex(m_mutex); -} - -bool recursive_timed_mutex::do_trylock() -{ - bool res = wait_mutex(m_mutex, 0) == WAIT_OBJECT_0; - - if (res) - { - if (++m_count > 1) - release_mutex(m_mutex); - return true; - } - return false; -} - -bool recursive_timed_mutex::do_timedlock(const xtime& xt) -{ - for (;;) - { - int milliseconds; - to_duration(xt, milliseconds); - - unsigned int res = wait_mutex(m_mutex, milliseconds); - - if (res == WAIT_TIMEOUT) - { - xtime cur; - xtime_get(&cur, TIME_UTC); - if (xtime_cmp(xt, cur) > 0) - continue; - } - - if (res == WAIT_OBJECT_0) - { - if (++m_count > 1) - release_mutex(m_mutex); - return true; - } - - return false; - } -} - -void recursive_timed_mutex::do_unlock() -{ - if (--m_count == 0) - release_mutex(m_mutex); -} - -void recursive_timed_mutex::do_lock(cv_state& state) -{ - wait_mutex(m_mutex, INFINITE); - - m_count = state; -} - -void recursive_timed_mutex::do_unlock(cv_state& state) -{ - state = m_count; - m_count = 0; - - release_mutex(m_mutex); -} - -#elif defined(BOOST_HAS_PTHREADS) - -recursive_mutex::recursive_mutex() - : m_count(0) -# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - , m_valid_id(false) -# endif -{ - pthread_mutexattr_t attr; - int res = pthread_mutexattr_init(&attr); - assert(res == 0); - -# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - assert(res == 0); -# endif - - res = pthread_mutex_init(&m_mutex, &attr); - { - int res = 0; - res = pthread_mutexattr_destroy(&attr); - assert(res == 0); - } - if (res != 0) - throw thread_resource_error(); - -# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - res = pthread_cond_init(&m_unlocked, 0); - if (res != 0) - { - pthread_mutex_destroy(&m_mutex); - throw thread_resource_error(); - } -# endif -} - -recursive_mutex::~recursive_mutex() -{ - int res = 0; - res = pthread_mutex_destroy(&m_mutex); - assert(res == 0); - -# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - res = pthread_cond_destroy(&m_unlocked); - assert(res == 0); -# endif -} - -void recursive_mutex::do_lock() -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - -# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - if (++m_count > 1) - { - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - } -# else - pthread_t tid = pthread_self(); - if (m_valid_id && pthread_equal(m_thread_id, tid)) - ++m_count; - else - { - while (m_valid_id) - { - res = pthread_cond_wait(&m_unlocked, &m_mutex); - assert(res == 0); - } - - m_thread_id = tid; - m_valid_id = true; - m_count = 1; - } - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); -# endif -} - -void recursive_mutex::do_unlock() -{ -# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - if (--m_count == 0) - { - int res = 0; - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - } -# else - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - pthread_t tid = pthread_self(); - if (m_valid_id && !pthread_equal(m_thread_id, tid)) - { - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - throw lock_error(); - } - - if (--m_count == 0) - { - assert(m_valid_id); - m_valid_id = false; - - res = pthread_cond_signal(&m_unlocked); - assert(res == 0); - } - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); -# endif -} - -void recursive_mutex::do_lock(cv_state& state) -{ -# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - m_count = state.count; -# else - int res = 0; - - while (m_valid_id) - { - res = pthread_cond_wait(&m_unlocked, &m_mutex); - assert(res == 0); - } - - m_thread_id = pthread_self(); - m_valid_id = true; - m_count = state.count; - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); -# endif -} - -void recursive_mutex::do_unlock(cv_state& state) -{ -# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - assert(m_valid_id); - m_valid_id = false; - - res = pthread_cond_signal(&m_unlocked); - assert(res == 0); -# endif - - state.pmutex = &m_mutex; - state.count = m_count; - m_count = 0; -} - -recursive_try_mutex::recursive_try_mutex() - : m_count(0) -# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - , m_valid_id(false) -# endif -{ - pthread_mutexattr_t attr; - int res = pthread_mutexattr_init(&attr); - assert(res == 0); - -# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - assert(res == 0); -# endif - - res = pthread_mutex_init(&m_mutex, &attr); - { - int res = 0; - res = pthread_mutexattr_destroy(&attr); - assert(res == 0); - } - if (res != 0) - throw thread_resource_error(); - -# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - res = pthread_cond_init(&m_unlocked, 0); - if (res != 0) - { - pthread_mutex_destroy(&m_mutex); - throw thread_resource_error(); - } -# endif -} - -recursive_try_mutex::~recursive_try_mutex() -{ - int res = 0; - res = pthread_mutex_destroy(&m_mutex); - assert(res == 0); - -# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - res = pthread_cond_destroy(&m_unlocked); - assert(res == 0); -# endif -} - -void recursive_try_mutex::do_lock() -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - -# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - if (++m_count > 1) - { - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - } -# else - pthread_t tid = pthread_self(); - if (m_valid_id && pthread_equal(m_thread_id, tid)) - ++m_count; - else - { - while (m_valid_id) - { - res = pthread_cond_wait(&m_unlocked, &m_mutex); - assert(res == 0); - } - - m_thread_id = tid; - m_valid_id = true; - m_count = 1; - } - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); -# endif -} - -bool recursive_try_mutex::do_trylock() -{ -# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - int res = 0; - res = pthread_mutex_trylock(&m_mutex); - assert(res == 0 || res == EBUSY); - - if (res == 0) - { - if (++m_count > 1) - { - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - } - return true; - } - - return false; -# else - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - bool ret = false; - pthread_t tid = pthread_self(); - if (m_valid_id && pthread_equal(m_thread_id, tid)) - { - ++m_count; - ret = true; - } - else if (!m_valid_id) - { - m_thread_id = tid; - m_valid_id = true; - m_count = 1; - ret = true; - } - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - return ret; -# endif -} - -void recursive_try_mutex::do_unlock() -{ -# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - if (--m_count == 0) - { - int res = 0; - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - } -# else - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - pthread_t tid = pthread_self(); - if (m_valid_id && !pthread_equal(m_thread_id, tid)) - { - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - throw lock_error(); - } - - if (--m_count == 0) - { - assert(m_valid_id); - m_valid_id = false; - - res = pthread_cond_signal(&m_unlocked); - assert(res == 0); - } - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); -# endif -} - -void recursive_try_mutex::do_lock(cv_state& state) -{ -# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - m_count = state.count; -# else - int res = 0; - - while (m_valid_id) - { - res = pthread_cond_wait(&m_unlocked, &m_mutex); - assert(res == 0); - } - - m_thread_id = pthread_self(); - m_valid_id = true; - m_count = state.count; - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); -# endif -} - -void recursive_try_mutex::do_unlock(cv_state& state) -{ -# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - assert(m_valid_id); - m_valid_id = false; - - res = pthread_cond_signal(&m_unlocked); - assert(res == 0); -# endif - - state.pmutex = &m_mutex; - state.count = m_count; - m_count = 0; -} - -recursive_timed_mutex::recursive_timed_mutex() - : m_valid_id(false), m_count(0) -{ - int res = 0; - res = pthread_mutex_init(&m_mutex, 0); - if (res != 0) - throw thread_resource_error(); - - res = pthread_cond_init(&m_unlocked, 0); - if (res != 0) - { - pthread_mutex_destroy(&m_mutex); - throw thread_resource_error(); - } -} - -recursive_timed_mutex::~recursive_timed_mutex() -{ - int res = 0; - res = pthread_mutex_destroy(&m_mutex); - assert(res == 0); - - res = pthread_cond_destroy(&m_unlocked); - assert(res == 0); -} - -void recursive_timed_mutex::do_lock() -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - pthread_t tid = pthread_self(); - if (m_valid_id && pthread_equal(m_thread_id, tid)) - ++m_count; - else - { - while (m_valid_id) - { - res = pthread_cond_wait(&m_unlocked, &m_mutex); - assert(res == 0); - } - - m_thread_id = tid; - m_valid_id = true; - m_count = 1; - } - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); -} - -bool recursive_timed_mutex::do_trylock() -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - bool ret = false; - pthread_t tid = pthread_self(); - if (m_valid_id && pthread_equal(m_thread_id, tid)) - { - ++m_count; - ret = true; - } - else if (!m_valid_id) - { - m_thread_id = tid; - m_valid_id = true; - m_count = 1; - ret = true; - } - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - return ret; -} - -bool recursive_timed_mutex::do_timedlock(const xtime& xt) -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - bool ret = false; - pthread_t tid = pthread_self(); - if (m_valid_id && pthread_equal(m_thread_id, tid)) - { - ++m_count; - ret = true; - } - else - { - timespec ts; - to_timespec(xt, ts); - - while (m_valid_id) - { - res = pthread_cond_timedwait(&m_unlocked, &m_mutex, &ts); - if (res == ETIMEDOUT) - break; - assert(res == 0); - } - - if (!m_valid_id) - { - m_thread_id = tid; - m_valid_id = true; - m_count = 1; - ret = true; - } - } - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - return ret; -} - -void recursive_timed_mutex::do_unlock() -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - pthread_t tid = pthread_self(); - if (m_valid_id && !pthread_equal(m_thread_id, tid)) - { - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - throw lock_error(); - } - - if (--m_count == 0) - { - assert(m_valid_id); - m_valid_id = false; - - res = pthread_cond_signal(&m_unlocked); - assert(res == 0); - } - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); -} - -void recursive_timed_mutex::do_lock(cv_state& state) -{ - int res = 0; - - while (m_valid_id) - { - res = pthread_cond_wait(&m_unlocked, &m_mutex); - assert(res == 0); - } - - m_thread_id = pthread_self(); - m_valid_id = true; - m_count = state.count; - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); -} - -void recursive_timed_mutex::do_unlock(cv_state& state) -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - assert(m_valid_id); - m_valid_id = false; - - res = pthread_cond_signal(&m_unlocked); - assert(res == 0); - - state.pmutex = &m_mutex; - state.count = m_count; - m_count = 0; -} -#elif defined(BOOST_HAS_MPTASKS) - -using threads::mac::detail::safe_enter_critical_region; - - -recursive_mutex::recursive_mutex() - : m_count(0) -{ -} - -recursive_mutex::~recursive_mutex() -{ -} - -void recursive_mutex::do_lock() -{ - OSStatus lStatus = noErr; - lStatus = safe_enter_critical_region(m_mutex, kDurationForever, - m_mutex_mutex); - assert(lStatus == noErr); - - if (++m_count > 1) - { - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); - } -} - -void recursive_mutex::do_unlock() -{ - if (--m_count == 0) - { - OSStatus lStatus = noErr; - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); - } -} - -void recursive_mutex::do_lock(cv_state& state) -{ - OSStatus lStatus = noErr; - lStatus = safe_enter_critical_region(m_mutex, kDurationForever, - m_mutex_mutex); - assert(lStatus == noErr); - - m_count = state; -} - -void recursive_mutex::do_unlock(cv_state& state) -{ - state = m_count; - m_count = 0; - - OSStatus lStatus = noErr; - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); -} - -recursive_try_mutex::recursive_try_mutex() - : m_count(0) -{ -} - -recursive_try_mutex::~recursive_try_mutex() -{ -} - -void recursive_try_mutex::do_lock() -{ - OSStatus lStatus = noErr; - lStatus = safe_enter_critical_region(m_mutex, kDurationForever, - m_mutex_mutex); - assert(lStatus == noErr); - - if (++m_count > 1) - { - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); - } -} - -bool recursive_try_mutex::do_trylock() -{ - OSStatus lStatus = noErr; - lStatus = MPEnterCriticalRegion(m_mutex, kDurationImmediate); - assert(lStatus == noErr || lStatus == kMPTimeoutErr); - - if (lStatus == noErr) - { - if (++m_count > 1) - { - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); - } - return true; - } - return false; -} - -void recursive_try_mutex::do_unlock() -{ - if (--m_count == 0) - { - OSStatus lStatus = noErr; - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); - } -} - -void recursive_try_mutex::do_lock(cv_state& state) -{ - OSStatus lStatus = noErr; - lStatus = safe_enter_critical_region(m_mutex, kDurationForever, - m_mutex_mutex); - assert(lStatus == noErr); - - m_count = state; -} - -void recursive_try_mutex::do_unlock(cv_state& state) -{ - state = m_count; - m_count = 0; - - OSStatus lStatus = noErr; - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); -} - -recursive_timed_mutex::recursive_timed_mutex() - : m_count(0) -{ -} - -recursive_timed_mutex::~recursive_timed_mutex() -{ -} - -void recursive_timed_mutex::do_lock() -{ - OSStatus lStatus = noErr; - lStatus = safe_enter_critical_region(m_mutex, kDurationForever, - m_mutex_mutex); - assert(lStatus == noErr); - - if (++m_count > 1) - { - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); - } -} - -bool recursive_timed_mutex::do_trylock() -{ - OSStatus lStatus = noErr; - lStatus = MPEnterCriticalRegion(m_mutex, kDurationImmediate); - assert(lStatus == noErr || lStatus == kMPTimeoutErr); - - if (lStatus == noErr) - { - if (++m_count > 1) - { - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); - } - return true; - } - return false; -} - -bool recursive_timed_mutex::do_timedlock(const xtime& xt) -{ - int microseconds; - to_microduration(xt, microseconds); - Duration lDuration = kDurationMicrosecond * microseconds; - - OSStatus lStatus = noErr; - lStatus = safe_enter_critical_region(m_mutex, lDuration, m_mutex_mutex); - assert(lStatus == noErr || lStatus == kMPTimeoutErr); - - if (lStatus == noErr) - { - if (++m_count > 1) - { - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); - } - return true; - } - return false; -} - -void recursive_timed_mutex::do_unlock() -{ - if (--m_count == 0) - { - OSStatus lStatus = noErr; - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); - } -} - -void recursive_timed_mutex::do_lock(cv_state& state) -{ - OSStatus lStatus = noErr; - lStatus = safe_enter_critical_region(m_mutex, kDurationForever, - m_mutex_mutex); - assert(lStatus == noErr); - - m_count = state; -} - -void recursive_timed_mutex::do_unlock(cv_state& state) -{ - state = m_count; - m_count = 0; - - OSStatus lStatus = noErr; - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); -} -#endif - -} // namespace boost - -// Change Log: -// 8 Feb 01 WEKEMPF Initial version. diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index f3605d5b..209e6b07 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -40,6 +40,7 @@ rule thread-run ( sources ) [ thread-run test_once.cpp ] [ thread-run test_xtime.cpp ] [ thread-run test_barrier.cpp ] -# [ thread-run test_read_write_mutex.cpp ] + [ thread-run test_shared_mutex.cpp ] + [ thread-run test_lock_concept.cpp ] ; } diff --git a/test/test_lock_concept.cpp b/test/test_lock_concept.cpp new file mode 100644 index 00000000..b0f5dc72 --- /dev/null +++ b/test/test_lock_concept.cpp @@ -0,0 +1,173 @@ +// (C) Copyright 2006-7 Anthony Williams +// 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) + +#include +#include +#include +#include +#include + +template +struct test_initially_locked +{ + void operator()() const + { + Mutex m; + Lock lock(m); + + BOOST_CHECK(lock); + BOOST_CHECK(lock.owns_lock()); + } +}; + +template +struct test_initially_unlocked_with_defer_lock_parameter +{ + void operator()() const + { + Mutex m; + Lock lock(m,boost::defer_lock); + + BOOST_CHECK(!lock); + BOOST_CHECK(!lock.owns_lock()); + } +}; + +template +struct test_initially_locked_with_adopt_lock_parameter +{ + void operator()() const + { + Mutex m; + m.lock(); + Lock lock(m,boost::adopt_lock); + + BOOST_CHECK(lock); + BOOST_CHECK(lock.owns_lock()); + } +}; + + +template +struct test_unlocked_after_unlock_called +{ + void operator()() const + { + Mutex m; + Lock lock(m); + lock.unlock(); + BOOST_CHECK(!lock); + BOOST_CHECK(!lock.owns_lock()); + } +}; + +template +struct test_locked_after_lock_called +{ + void operator()() const + { + Mutex m; + Lock lock(m,boost::defer_lock); + lock.lock(); + BOOST_CHECK(lock); + BOOST_CHECK(lock.owns_lock()); + } +}; + +template +struct test_locked_after_try_lock_called +{ + void operator()() const + { + Mutex m; + Lock lock(m,boost::defer_lock); + lock.try_lock(); + BOOST_CHECK(lock); + BOOST_CHECK(lock.owns_lock()); + } +}; + +template +struct test_throws_if_lock_called_when_already_locked +{ + void operator()() const + { + Mutex m; + Lock lock(m); + + BOOST_CHECK_THROW( lock.lock(), boost::lock_error ); + } +}; + +template +struct test_throws_if_try_lock_called_when_already_locked +{ + void operator()() const + { + Mutex m; + Lock lock(m); + + BOOST_CHECK_THROW( lock.try_lock(), boost::lock_error ); + } +}; + +template +struct test_throws_if_unlock_called_when_already_unlocked +{ + void operator()() const + { + Mutex m; + Lock lock(m); + lock.unlock(); + + BOOST_CHECK_THROW( lock.unlock(), boost::lock_error ); + } +}; + +BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_lock_concept,Mutex) +{ + typedef typename Mutex::scoped_lock Lock; + + test_initially_locked()(); + test_initially_unlocked_with_defer_lock_parameter()(); + test_initially_locked_with_adopt_lock_parameter()(); + test_unlocked_after_unlock_called()(); + test_locked_after_lock_called()(); + test_throws_if_lock_called_when_already_locked()(); + test_throws_if_unlock_called_when_already_unlocked()(); +} + +BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_try_lock_concept,Mutex) +{ + typedef typename Mutex::scoped_try_lock Lock; + + test_initially_locked()(); + test_initially_unlocked_with_defer_lock_parameter()(); + test_initially_locked_with_adopt_lock_parameter()(); + test_unlocked_after_unlock_called()(); + test_locked_after_lock_called()(); + test_locked_after_try_lock_called()(); + test_throws_if_lock_called_when_already_locked()(); + test_throws_if_try_lock_called_when_already_locked()(); + test_throws_if_unlock_called_when_already_unlocked()(); +} + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: lock concept test suite"); + + typedef boost::mpl::vector mutex_types; + + test->add(BOOST_TEST_CASE_TEMPLATE(test_scoped_lock_concept,mutex_types)); + + typedef boost::mpl::vector try_mutex_types; + + test->add(BOOST_TEST_CASE_TEMPLATE(test_scoped_try_lock_concept,try_mutex_types)); + + return test; +} diff --git a/test/test_shared_mutex.cpp b/test/test_shared_mutex.cpp new file mode 100644 index 00000000..763758d3 --- /dev/null +++ b/test/test_shared_mutex.cpp @@ -0,0 +1,460 @@ +// (C) Copyright 2006-7 Anthony Williams +// 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) + +#include +#include +#include +#include +#include +#include "util.inl" + +#define CHECK_LOCKED_VALUE_EQUAL(mutex_name,value,expected_value) \ + { \ + boost::mutex::scoped_lock lock(mutex_name); \ + BOOST_CHECK_EQUAL(value,expected_value); \ + } + + +namespace +{ + template + class locking_thread + { + boost::shared_mutex& rw_mutex; + unsigned& unblocked_count; + unsigned& simultaneous_running_count; + unsigned& max_simultaneous_running; + boost::mutex& unblocked_count_mutex; + boost::mutex& finish_mutex; + public: + locking_thread(boost::shared_mutex& rw_mutex_, + unsigned& unblocked_count_, + boost::mutex& unblocked_count_mutex_, + boost::mutex& finish_mutex_, + unsigned& simultaneous_running_count_, + unsigned& max_simultaneous_running_): + rw_mutex(rw_mutex_), + unblocked_count(unblocked_count_), + unblocked_count_mutex(unblocked_count_mutex_), + finish_mutex(finish_mutex_), + simultaneous_running_count(simultaneous_running_count_), + max_simultaneous_running(max_simultaneous_running_) + {} + + void operator()() + { + // acquire lock + lock_type lock(rw_mutex); + + // increment count to show we're unblocked + { + boost::mutex::scoped_lock ublock(unblocked_count_mutex); + ++unblocked_count; + ++simultaneous_running_count; + if(simultaneous_running_count>max_simultaneous_running) + { + max_simultaneous_running=simultaneous_running_count; + } + } + + // wait to finish + boost::mutex::scoped_lock finish_lock(finish_mutex); + { + boost::mutex::scoped_lock ublock(unblocked_count_mutex); + --simultaneous_running_count; + } + } + }; + +} + + +void test_multiple_readers() +{ + unsigned const number_of_threads=100; + + boost::thread_group pool; + + boost::shared_mutex rw_mutex; + unsigned unblocked_count=0; + unsigned simultaneous_running_count=0; + unsigned max_simultaneous_running=0; + boost::mutex unblocked_count_mutex; + boost::mutex finish_mutex; + boost::mutex::scoped_lock finish_lock(finish_mutex); + + for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); + } + + boost::thread::sleep(delay(1)); + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,number_of_threads); + + finish_lock.unlock(); + + pool.join_all(); + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,number_of_threads); +} + +void test_only_one_writer_permitted() +{ + unsigned const number_of_threads=100; + + boost::thread_group pool; + + boost::shared_mutex rw_mutex; + unsigned unblocked_count=0; + unsigned simultaneous_running_count=0; + unsigned max_simultaneous_running=0; + boost::mutex unblocked_count_mutex; + boost::mutex finish_mutex; + boost::mutex::scoped_lock finish_lock(finish_mutex); + + for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); + } + + boost::thread::sleep(delay(1)); + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); + + finish_lock.unlock(); + + pool.join_all(); + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,number_of_threads); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1); +} + +void test_reader_blocks_writer() +{ + boost::thread_group pool; + + boost::shared_mutex rw_mutex; + unsigned unblocked_count=0; + unsigned simultaneous_running_count=0; + unsigned max_simultaneous_running=0; + boost::mutex unblocked_count_mutex; + boost::mutex finish_mutex; + boost::mutex::scoped_lock finish_lock(finish_mutex); + + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); + + finish_lock.unlock(); + + pool.join_all(); + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,2U); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1); +} + +void test_unlocking_writer_unblocks_all_readers() +{ + boost::thread_group pool; + + boost::shared_mutex rw_mutex; + boost::unique_lock write_lock(rw_mutex); + unsigned unblocked_count=0; + unsigned simultaneous_running_count=0; + unsigned max_simultaneous_running=0; + boost::mutex unblocked_count_mutex; + boost::mutex finish_mutex; + boost::mutex::scoped_lock finish_lock(finish_mutex); + + unsigned const reader_count=100; + + for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); + } + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,0U); + + write_lock.unlock(); + + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count); + + finish_lock.unlock(); + pool.join_all(); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,reader_count); +} + +void test_unlocking_last_reader_only_unblocks_one_writer() +{ + boost::thread_group pool; + + boost::shared_mutex rw_mutex; + unsigned unblocked_count=0; + unsigned simultaneous_running_readers=0; + unsigned max_simultaneous_readers=0; + unsigned simultaneous_running_writers=0; + unsigned max_simultaneous_writers=0; + boost::mutex unblocked_count_mutex; + boost::mutex finish_reading_mutex; + boost::mutex::scoped_lock finish_reading_lock(finish_reading_mutex); + boost::mutex finish_writing_mutex; + boost::mutex::scoped_lock finish_writing_lock(finish_writing_mutex); + + unsigned const reader_count=100; + unsigned const writer_count=100; + + for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_reading_mutex,simultaneous_running_readers,max_simultaneous_readers)); + } + for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_writing_mutex,simultaneous_running_writers,max_simultaneous_writers)); + } + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count); + + finish_reading_lock.unlock(); + + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); + + finish_writing_lock.unlock(); + pool.join_all(); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+writer_count); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_readers,reader_count); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_writers,1); +} + +void test_only_one_upgrade_lock_permitted() +{ + unsigned const number_of_threads=100; + + boost::thread_group pool; + + boost::shared_mutex rw_mutex; + unsigned unblocked_count=0; + unsigned simultaneous_running_count=0; + unsigned max_simultaneous_running=0; + boost::mutex unblocked_count_mutex; + boost::mutex finish_mutex; + boost::mutex::scoped_lock finish_lock(finish_mutex); + + for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); + } + + boost::thread::sleep(delay(1)); + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); + + finish_lock.unlock(); + + pool.join_all(); + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,number_of_threads); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1); +} + +void test_can_lock_upgrade_if_currently_locked_shared() +{ + boost::thread_group pool; + + boost::shared_mutex rw_mutex; + unsigned unblocked_count=0; + unsigned simultaneous_running_count=0; + unsigned max_simultaneous_running=0; + boost::mutex unblocked_count_mutex; + boost::mutex finish_mutex; + boost::mutex::scoped_lock finish_lock(finish_mutex); + + unsigned const reader_count=100; + + for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); + } + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); + + finish_lock.unlock(); + pool.join_all(); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,reader_count+1); +} + +namespace +{ + class simple_writing_thread + { + boost::shared_mutex& rwm; + boost::mutex& finish_mutex; + boost::mutex& unblocked_mutex; + unsigned& unblocked_count; + + public: + simple_writing_thread(boost::shared_mutex& rwm_, + boost::mutex& finish_mutex_, + boost::mutex& unblocked_mutex_, + unsigned& unblocked_count_): + rwm(rwm_),finish_mutex(finish_mutex_), + unblocked_mutex(unblocked_mutex_),unblocked_count(unblocked_count_) + {} + + void operator()() + { + boost::unique_lock lk(rwm); + + { + boost::mutex::scoped_lock ulk(unblocked_mutex); + ++unblocked_count; + } + + boost::mutex::scoped_lock flk(finish_mutex); + } + }; +} + +void test_if_other_thread_has_write_lock_try_lock_shared_returns_false() +{ + + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + unsigned unblocked_count=0; + boost::mutex::scoped_lock finish_lock(finish_mutex); + boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1); + + bool const try_succeeded=rw_mutex.try_lock_shared(); + BOOST_CHECK(!try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_shared(); + } + + finish_lock.unlock(); + writer.join(); +} + +void test_if_no_thread_has_lock_try_lock_shared_returns_true() +{ + boost::shared_mutex rw_mutex; + bool const try_succeeded=rw_mutex.try_lock_shared(); + BOOST_CHECK(try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_shared(); + } +} + +namespace +{ + class simple_reading_thread + { + boost::shared_mutex& rwm; + boost::mutex& finish_mutex; + boost::mutex& unblocked_mutex; + unsigned& unblocked_count; + + public: + simple_reading_thread(boost::shared_mutex& rwm_, + boost::mutex& finish_mutex_, + boost::mutex& unblocked_mutex_, + unsigned& unblocked_count_): + rwm(rwm_),finish_mutex(finish_mutex_), + unblocked_mutex(unblocked_mutex_),unblocked_count(unblocked_count_) + {} + + void operator()() + { + boost::shared_lock lk(rwm); + + { + boost::mutex::scoped_lock ulk(unblocked_mutex); + ++unblocked_count; + } + + boost::mutex::scoped_lock flk(finish_mutex); + } + }; +} + +void test_if_other_thread_has_shared_lock_try_lock_shared_returns_true() +{ + + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + unsigned unblocked_count=0; + boost::mutex::scoped_lock finish_lock(finish_mutex); + boost::thread writer(simple_reading_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1); + + bool const try_succeeded=rw_mutex.try_lock_shared(); + BOOST_CHECK(try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_shared(); + } + + finish_lock.unlock(); + writer.join(); +} + +void test_timed_lock_shared_times_out_if_write_lock_held() +{ + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + unsigned unblocked_count=0; + boost::mutex::scoped_lock finish_lock(finish_mutex); + boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1); + + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+boost::posix_time::milliseconds(100); + bool const timed_lock_succeeded=rw_mutex.timed_lock_shared(timeout); + BOOST_CHECK(timeout<=boost::get_system_time()); + BOOST_CHECK(!timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock_shared(); + } + + finish_lock.unlock(); + writer.join(); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: shared_mutex test suite"); + + test->add(BOOST_TEST_CASE(&test_multiple_readers)); + test->add(BOOST_TEST_CASE(&test_only_one_writer_permitted)); + test->add(BOOST_TEST_CASE(&test_reader_blocks_writer)); + test->add(BOOST_TEST_CASE(&test_unlocking_writer_unblocks_all_readers)); + test->add(BOOST_TEST_CASE(&test_unlocking_last_reader_only_unblocks_one_writer)); + test->add(BOOST_TEST_CASE(&test_only_one_upgrade_lock_permitted)); + test->add(BOOST_TEST_CASE(&test_can_lock_upgrade_if_currently_locked_shared)); + test->add(BOOST_TEST_CASE(&test_if_other_thread_has_write_lock_try_lock_shared_returns_false)); + test->add(BOOST_TEST_CASE(&test_if_no_thread_has_lock_try_lock_shared_returns_true)); + test->add(BOOST_TEST_CASE(&test_if_other_thread_has_shared_lock_try_lock_shared_returns_true)); + test->add(BOOST_TEST_CASE(&test_timed_lock_shared_times_out_if_write_lock_held)); + + return test; +} From b0b2b1790828bf4b75b748de5e88415d1e29c4bd Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 15 Oct 2007 09:18:32 +0000 Subject: [PATCH 012/214] added missing include to basic_timed_mutex.hpp [SVN r40041] --- include/boost/thread/win32/basic_timed_mutex.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/boost/thread/win32/basic_timed_mutex.hpp b/include/boost/thread/win32/basic_timed_mutex.hpp index bc2b1af0..155962cb 100644 --- a/include/boost/thread/win32/basic_timed_mutex.hpp +++ b/include/boost/thread/win32/basic_timed_mutex.hpp @@ -13,6 +13,7 @@ #include "thread_primitives.hpp" #include "interlocked_read.hpp" #include +#include namespace boost { From c37cdeec9f5c1c1b090e19bb138c6ca295f6a98f Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 16 Oct 2007 11:08:17 +0000 Subject: [PATCH 013/214] removed lock_ops as no longer needed [SVN r40080] --- include/boost/thread/condition.hpp | 1 - include/boost/thread/detail/lock.hpp | 209 ---- .../boost/thread/detail/read_write_lock.hpp | 1106 ----------------- 3 files changed, 1316 deletions(-) delete mode 100644 include/boost/thread/detail/lock.hpp delete mode 100644 include/boost/thread/detail/read_write_lock.hpp diff --git a/include/boost/thread/condition.hpp b/include/boost/thread/condition.hpp index 64889d56..f7ac7194 100644 --- a/include/boost/thread/condition.hpp +++ b/include/boost/thread/condition.hpp @@ -12,7 +12,6 @@ #include #include -#include #if defined(BOOST_HAS_PTHREADS) # include diff --git a/include/boost/thread/detail/lock.hpp b/include/boost/thread/detail/lock.hpp deleted file mode 100644 index d2974ea0..00000000 --- a/include/boost/thread/detail/lock.hpp +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright (C) 2001-2003 -// William E. Kempf -// -// 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) - -#ifndef BOOST_XLOCK_WEK070601_HPP -#define BOOST_XLOCK_WEK070601_HPP - -#include - -#include -#include - -namespace boost { - -class condition; -struct xtime; - -namespace detail { namespace thread { - -template -class lock_ops : private noncopyable -{ -private: - lock_ops() { } - -public: - typedef typename Mutex::cv_state lock_state; - - static void lock(Mutex& m) - { - m.do_lock(); - } - static bool trylock(Mutex& m) - { - return m.do_trylock(); - } - static bool timedlock(Mutex& m, const xtime& xt) - { - return m.do_timedlock(xt); - } - static void unlock(Mutex& m) - { - m.do_unlock(); - } - static void lock(Mutex& m, lock_state& state) - { - m.do_lock(state); - } - static void unlock(Mutex& m, lock_state& state) - { - m.do_unlock(state); - } -}; - -template -class scoped_lock : private noncopyable -{ -public: - typedef Mutex mutex_type; - - explicit scoped_lock(Mutex& mx, bool initially_locked=true) - : m_mutex(mx), m_locked(false) - { - if (initially_locked) lock(); - } - ~scoped_lock() - { - if (m_locked) unlock(); - } - - void lock() - { - if (m_locked) throw lock_error(); - lock_ops::lock(m_mutex); - m_locked = true; - } - void unlock() - { - if (!m_locked) throw lock_error(); - lock_ops::unlock(m_mutex); - m_locked = false; - } - - bool locked() const { return m_locked; } - operator const void*() const { return m_locked ? this : 0; } - -private: - friend class boost::condition; - - Mutex& m_mutex; - bool m_locked; -}; - -template -class scoped_try_lock : private noncopyable -{ -public: - typedef TryMutex mutex_type; - - explicit scoped_try_lock(TryMutex& mx) - : m_mutex(mx), m_locked(false) - { - try_lock(); - } - scoped_try_lock(TryMutex& mx, bool initially_locked) - : m_mutex(mx), m_locked(false) - { - if (initially_locked) lock(); - } - ~scoped_try_lock() - { - if (m_locked) unlock(); - } - - void lock() - { - if (m_locked) throw lock_error(); - lock_ops::lock(m_mutex); - m_locked = true; - } - bool try_lock() - { - if (m_locked) throw lock_error(); - return (m_locked = lock_ops::trylock(m_mutex)); - } - void unlock() - { - if (!m_locked) throw lock_error(); - lock_ops::unlock(m_mutex); - m_locked = false; - } - - bool locked() const { return m_locked; } - operator const void*() const { return m_locked ? this : 0; } - -private: - friend class boost::condition; - - TryMutex& m_mutex; - bool m_locked; -}; - -template -class scoped_timed_lock : private noncopyable -{ -public: - typedef TimedMutex mutex_type; - - scoped_timed_lock(TimedMutex& mx, const xtime& xt) - : m_mutex(mx), m_locked(false) - { - timed_lock(xt); - } - scoped_timed_lock(TimedMutex& mx, bool initially_locked) - : m_mutex(mx), m_locked(false) - { - if (initially_locked) lock(); - } - ~scoped_timed_lock() - { - if (m_locked) unlock(); - } - - void lock() - { - if (m_locked) throw lock_error(); - lock_ops::lock(m_mutex); - m_locked = true; - } - bool try_lock() - { - if (m_locked) throw lock_error(); - return (m_locked = lock_ops::trylock(m_mutex)); - } - bool timed_lock(const xtime& xt) - { - if (m_locked) throw lock_error(); - return (m_locked = lock_ops::timedlock(m_mutex, xt)); - } - void unlock() - { - if (!m_locked) throw lock_error(); - lock_ops::unlock(m_mutex); - m_locked = false; - } - - bool locked() const { return m_locked; } - operator const void*() const { return m_locked ? this : 0; } - -private: - friend class boost::condition; - - TimedMutex& m_mutex; - bool m_locked; -}; - -} // namespace thread -} // namespace detail -} // namespace boost - -#endif // BOOST_XLOCK_WEK070601_HPP - -// Change Log: -// 8 Feb 01 WEKEMPF Initial version. -// 22 May 01 WEKEMPF Modified to use xtime for time outs. -// 30 Jul 01 WEKEMPF Moved lock types into boost::detail::thread. Renamed -// some types. Added locked() methods. diff --git a/include/boost/thread/detail/read_write_lock.hpp b/include/boost/thread/detail/read_write_lock.hpp deleted file mode 100644 index c596df78..00000000 --- a/include/boost/thread/detail/read_write_lock.hpp +++ /dev/null @@ -1,1106 +0,0 @@ -// Copyright (C) 2002-2003 -// David Moore, Michael Glassford -// -// Original scoped_lock implementation -// Copyright (C) 2001 -// William E. Kempf -// -// 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) - -#ifndef BOOST_READ_WRITE_LOCK_JDM031002_HPP -#define BOOST_READ_WRITE_LOCK_JDM031002_HPP - -#include - -#include -#include -#include - -namespace boost { - -struct xtime; - -namespace read_write_lock_state { - enum read_write_lock_state_enum - { - unlocked=0, - read_locked=1, - write_locked=2 - }; -} //namespace read_write_lock_state - -namespace detail { -namespace thread { - -template -class read_write_lock_ops : private noncopyable -{ -private: - - read_write_lock_ops() { } - ~read_write_lock_ops() { } - -public: - - typedef Mutex mutex_type; - - static void write_lock(Mutex& m) - { - m.do_write_lock(); - } - static void read_lock(Mutex& m) - { - m.do_read_lock(); - } - static void write_unlock(Mutex& m) - { - m.do_write_unlock(); - } - static void read_unlock(Mutex &m) - { - m.do_read_unlock(); - } - static bool try_write_lock(Mutex &m) - { - return m.do_try_write_lock(); - } - static bool try_read_lock(Mutex &m) - { - return m.do_try_read_lock(); - } - - static bool timed_write_lock(Mutex &m,const xtime &xt) - { - return m.do_timed_write_lock(xt); - } - static bool timed_read_lock(Mutex &m,const xtime &xt) - { - return m.do_timed_read_lock(xt); - } - - static void demote(Mutex & m) - { - m.do_demote_to_read_lock(); - } - static bool try_demote(Mutex & m) - { - return m.do_try_demote_to_read_lock(); - } - static bool timed_demote(Mutex & m,const xtime &xt) - { - return m.do_timed_demote_to_read_lock(xt); - } - - static void promote(Mutex & m) - { - m.do_promote_to_write_lock(); - } - static bool try_promote(Mutex & m) - { - return m.do_try_promote_to_write_lock(); - } - static bool timed_promote(Mutex & m,const xtime &xt) - { - return m.do_timed_promote_to_write_lock(xt); - } -}; - -template -class scoped_read_write_lock : private noncopyable -{ -public: - - typedef ReadWriteMutex mutex_type; - - scoped_read_write_lock( - ReadWriteMutex& mx, - read_write_lock_state::read_write_lock_state_enum initial_state) - : m_mutex(mx), m_state(read_write_lock_state::unlocked) - { - if (initial_state == read_write_lock_state::read_locked) - read_lock(); - else if (initial_state == read_write_lock_state::write_locked) - write_lock(); - } - - ~scoped_read_write_lock() - { - if (m_state != read_write_lock_state::unlocked) - unlock(); - } - - void read_lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - read_write_lock_ops::read_lock(m_mutex); - m_state = read_write_lock_state::read_locked; - } - - void write_lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - read_write_lock_ops::write_lock(m_mutex); - m_state = read_write_lock_state::write_locked; - } - - void unlock() - { - if (m_state == read_write_lock_state::unlocked) throw lock_error(); - if (m_state == read_write_lock_state::read_locked) - read_write_lock_ops::read_unlock(m_mutex); - else //(m_state == read_write_lock_state::write_locked) - read_write_lock_ops::write_unlock(m_mutex); - - m_state = read_write_lock_state::unlocked; - } - - void demote(void) - { - if (m_state != read_write_lock_state::write_locked) throw lock_error(); - read_write_lock_ops::demote(m_mutex); - m_state = read_write_lock_state::read_locked; - } - - void promote(void) - { - if (m_state != read_write_lock_state::read_locked) throw lock_error(); - read_write_lock_ops::promote(m_mutex); - m_state = read_write_lock_state::write_locked; - } - - //If allow_unlock = true, set_lock always succeeds and - //the function result indicates whether an unlock was required. - //If allow_unlock = false, set_lock may fail; - //the function result indicates whether it succeeded. - bool set_lock(read_write_lock_state::read_write_lock_state_enum ls, bool allow_unlock = true) - { - bool result = !allow_unlock; - - if (m_state != ls) - { - if (m_state == read_write_lock_state::unlocked) - { - if (ls == read_write_lock_state::read_locked) - read_lock(); - else //(ls == read_write_lock_state::write_locked) - write_lock(); - } - else //(m_state == read_write_lock_state::read_locked || m_state == read_write_lock_state::write_locked) - { - if (ls == read_write_lock_state::read_locked) - demote(); - else if (ls == read_write_lock_state::write_locked) - { - if (allow_unlock) - { - result = true; - unlock(); - write_lock(); - } - else - result = false; - } - else //(ls == read_write_lock_state::unlocked) - unlock(); - } - } - - return result; - } - - bool locked() const - { - return m_state != read_write_lock_state::unlocked; - } - - bool read_locked() const - { - return m_state == read_write_lock_state::read_locked; - } - - bool write_locked() const - { - return m_state != read_write_lock_state::write_locked; - } - - operator const void*() const - { - return (m_state != read_write_lock_state::unlocked) ? this : 0; - } - - read_write_lock_state::read_write_lock_state_enum state() const - { - return m_state; - } - -private: - - ReadWriteMutex& m_mutex; - read_write_lock_state::read_write_lock_state_enum m_state; -}; - -template -class scoped_read_lock : private noncopyable -{ -public: - - typedef ReadWriteMutex mutex_type; - - explicit scoped_read_lock( - ReadWriteMutex& mx, - bool initially_locked = true) - : m_mutex(mx), m_state(read_write_lock_state::unlocked) - { - if (initially_locked) - lock(); - } - - ~scoped_read_lock() - { - if (m_state != read_write_lock_state::unlocked) - unlock(); - } - - void lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - read_write_lock_ops::read_lock(m_mutex); - m_state = read_write_lock_state::read_locked; - } - - void unlock() - { - if (m_state != read_write_lock_state::read_locked) throw lock_error(); - read_write_lock_ops::read_unlock(m_mutex); - - m_state = read_write_lock_state::unlocked; - } - - bool locked() const - { - return m_state != read_write_lock_state::unlocked; - } - - operator const void*() const - { - return (m_state != read_write_lock_state::unlocked) ? this : 0; - } - -private: - - ReadWriteMutex& m_mutex; - read_write_lock_state::read_write_lock_state_enum m_state; -}; - -template -class scoped_write_lock : private noncopyable -{ -public: - - typedef ReadWriteMutex mutex_type; - - explicit scoped_write_lock( - ReadWriteMutex& mx, - bool initially_locked = true) - : m_mutex(mx), m_state(read_write_lock_state::unlocked) - { - if (initially_locked) - lock(); - } - - ~scoped_write_lock() - { - if (m_state != read_write_lock_state::unlocked) - unlock(); - } - - void lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - read_write_lock_ops::write_lock(m_mutex); - m_state = read_write_lock_state::write_locked; - } - - void unlock() - { - if (m_state != read_write_lock_state::write_locked) throw lock_error(); - read_write_lock_ops::write_unlock(m_mutex); - - m_state = read_write_lock_state::unlocked; - } - - bool locked() const - { - return m_state != read_write_lock_state::unlocked; - } - - operator const void*() const - { - return (m_state != read_write_lock_state::unlocked) ? this : 0; - } - -private: - - ReadWriteMutex& m_mutex; - read_write_lock_state::read_write_lock_state_enum m_state; -}; - -template -class scoped_try_read_write_lock : private noncopyable -{ -public: - - typedef TryReadWriteMutex mutex_type; - - scoped_try_read_write_lock( - TryReadWriteMutex& mx, - read_write_lock_state::read_write_lock_state_enum initial_state) - : m_mutex(mx), m_state(read_write_lock_state::unlocked) - { - if (initial_state == read_write_lock_state::read_locked) - read_lock(); - else if (initial_state == read_write_lock_state::write_locked) - write_lock(); - } - - ~scoped_try_read_write_lock() - { - if (m_state != read_write_lock_state::unlocked) - unlock(); - } - - void read_lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - read_write_lock_ops::read_lock(m_mutex); - m_state = read_write_lock_state::read_locked; - } - - bool try_read_lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - if(read_write_lock_ops::try_read_lock(m_mutex)) - { - m_state = read_write_lock_state::read_locked; - return true; - } - return false; - } - - void write_lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - read_write_lock_ops::write_lock(m_mutex); - m_state = read_write_lock_state::write_locked; - } - - bool try_write_lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - if(read_write_lock_ops::try_write_lock(m_mutex)) - { - m_state = read_write_lock_state::write_locked; - return true; - } - return false; - } - - void unlock() - { - if (m_state == read_write_lock_state::unlocked) throw lock_error(); - if (m_state == read_write_lock_state::read_locked) - read_write_lock_ops::read_unlock(m_mutex); - else //(m_state == read_write_lock_state::write_locked) - read_write_lock_ops::write_unlock(m_mutex); - - m_state = read_write_lock_state::unlocked; - } - - void demote(void) - { - if (m_state != read_write_lock_state::write_locked) throw lock_error(); - read_write_lock_ops::demote(m_mutex); - m_state = read_write_lock_state::read_locked; - } - - bool try_demote(void) - { - if (m_state != read_write_lock_state::write_locked) throw lock_error(); - return read_write_lock_ops::try_demote(m_mutex) ? (m_state = read_write_lock_state::read_locked, true) : false; - } - - void promote(void) - { - if (m_state != read_write_lock_state::read_locked) throw lock_error(); - read_write_lock_ops::promote(m_mutex); - m_state = read_write_lock_state::write_locked; - } - - bool try_promote(void) - { - if (m_state != read_write_lock_state::read_locked) throw lock_error(); - return read_write_lock_ops::try_promote(m_mutex) ? (m_state = read_write_lock_state::write_locked, true) : false; - } - - //If allow_unlock = true, set_lock always succeeds and - //the function result indicates whether an unlock was required. - //If allow_unlock = false, set_lock may fail; - //the function result indicates whether it succeeded. - bool set_lock(read_write_lock_state::read_write_lock_state_enum ls, bool allow_unlock = true) - { - bool result = !allow_unlock; - - if (m_state != ls) - { - if (m_state == read_write_lock_state::unlocked) - { - if (ls == read_write_lock_state::read_locked) - read_lock(); - else //(ls == read_write_lock_state::write_locked) - write_lock(); - } - else //(m_state == read_write_lock_state::read_locked || m_state == read_write_lock_state::write_locked) - { - if (ls == read_write_lock_state::read_locked) - demote(); - else if (ls == read_write_lock_state::write_locked) - { - if (!try_promote()) - { - if (allow_unlock) - { - result = true; - unlock(); - write_lock(); - } - else - result = false; - } - } - else //(ls == read_write_lock_state::unlocked) - unlock(); - } - } - - return result; - } - - bool try_set_lock(read_write_lock_state::read_write_lock_state_enum ls) - { - if (m_state != ls) - { - if (m_state == read_write_lock_state::unlocked) - { - if (ls == read_write_lock_state::read_locked) - return try_read_lock(); - else // (ls == read_write_lock_state::write_locked) - return try_write_lock(); - } - else //(m_state == read_write_lock_state::read_locked || m_state == read_write_lock_state::write_locked) - { - if (ls == read_write_lock_state::read_locked) - return try_demote(); - else if (ls == read_write_lock_state::write_locked) - return try_promote(); - else //(ls == read_write_lock_state::unlocked) - return unlock(), true; - } - } - else //(m_state == ls) - return true; - } - - bool locked() const - { - return m_state != read_write_lock_state::unlocked; - } - - bool read_locked() const - { - return m_state == read_write_lock_state::read_locked; - } - - bool write_locked() const - { - return m_state != read_write_lock_state::write_locked; - } - - operator const void*() const - { - return (m_state != read_write_lock_state::unlocked) ? this : 0; - } - - read_write_lock_state::read_write_lock_state_enum state() const - { - return m_state; - } - -private: - - TryReadWriteMutex& m_mutex; - read_write_lock_state::read_write_lock_state_enum m_state; -}; - -template -class scoped_try_read_lock : private noncopyable -{ -public: - - typedef TryReadWriteMutex mutex_type; - - explicit scoped_try_read_lock( - TryReadWriteMutex& mx, - bool initially_locked = true) - : m_mutex(mx), m_state(read_write_lock_state::unlocked) - { - if (initially_locked) - lock(); - } - - ~scoped_try_read_lock() - { - if (m_state != read_write_lock_state::unlocked) - unlock(); - } - - void lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - read_write_lock_ops::read_lock(m_mutex); - m_state = read_write_lock_state::read_locked; - } - - bool try_lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - if(read_write_lock_ops::try_read_lock(m_mutex)) - { - m_state = read_write_lock_state::read_locked; - return true; - } - return false; - } - - void unlock() - { - if (m_state != read_write_lock_state::read_locked) throw lock_error(); - read_write_lock_ops::read_unlock(m_mutex); - - m_state = read_write_lock_state::unlocked; - } - - bool locked() const - { - return m_state != read_write_lock_state::unlocked; - } - - operator const void*() const - { - return (m_state != read_write_lock_state::unlocked) ? this : 0; - } - -private: - - TryReadWriteMutex& m_mutex; - read_write_lock_state::read_write_lock_state_enum m_state; -}; - -template -class scoped_try_write_lock : private noncopyable -{ -public: - - typedef TryReadWriteMutex mutex_type; - - explicit scoped_try_write_lock( - TryReadWriteMutex& mx, - bool initially_locked = true) - : m_mutex(mx), m_state(read_write_lock_state::unlocked) - { - if (initially_locked) - lock(); - } - - ~scoped_try_write_lock() - { - if (m_state != read_write_lock_state::unlocked) - unlock(); - } - - void lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - read_write_lock_ops::write_lock(m_mutex); - m_state = read_write_lock_state::write_locked; - } - - bool try_lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - if(read_write_lock_ops::try_write_lock(m_mutex)) - { - m_state = read_write_lock_state::write_locked; - return true; - } - return false; - } - - void unlock() - { - if (m_state != read_write_lock_state::write_locked) throw lock_error(); - read_write_lock_ops::write_unlock(m_mutex); - - m_state = read_write_lock_state::unlocked; - } - - bool locked() const - { - return m_state != read_write_lock_state::unlocked; - } - - operator const void*() const - { - return (m_state != read_write_lock_state::unlocked) ? this : 0; - } - -private: - - TryReadWriteMutex& m_mutex; - read_write_lock_state::read_write_lock_state_enum m_state; -}; - -template -class scoped_timed_read_write_lock : private noncopyable -{ -public: - - typedef TimedReadWriteMutex mutex_type; - - scoped_timed_read_write_lock( - TimedReadWriteMutex& mx, - read_write_lock_state::read_write_lock_state_enum initial_state) - : m_mutex(mx), m_state(read_write_lock_state::unlocked) - { - if (initial_state == read_write_lock_state::read_locked) - read_lock(); - else if (initial_state == read_write_lock_state::write_locked) - write_lock(); - } - - ~scoped_timed_read_write_lock() - { - if (m_state != read_write_lock_state::unlocked) - unlock(); - } - - void read_lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - read_write_lock_ops::read_lock(m_mutex); - m_state = read_write_lock_state::read_locked; - } - - bool try_read_lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - if(read_write_lock_ops::try_read_lock(m_mutex)) - { - m_state = read_write_lock_state::read_locked; - return true; - } - return false; - } - - bool timed_read_lock(const xtime &xt) - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - if(read_write_lock_ops::timed_read_lock(m_mutex,xt)) - { - m_state = read_write_lock_state::read_locked; - return true; - } - return false; - } - - void write_lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - read_write_lock_ops::write_lock(m_mutex); - m_state = read_write_lock_state::write_locked; - } - - bool try_write_lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - if(read_write_lock_ops::try_write_lock(m_mutex)) - { - m_state = read_write_lock_state::write_locked; - return true; - } - return false; - } - - bool timed_write_lock(const xtime &xt) - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - if(read_write_lock_ops::timed_write_lock(m_mutex,xt)) - { - m_state = read_write_lock_state::write_locked; - return true; - } - return false; - } - - void unlock() - { - if (m_state == read_write_lock_state::unlocked) throw lock_error(); - if (m_state == read_write_lock_state::read_locked) - read_write_lock_ops::read_unlock(m_mutex); - else //(m_state == read_write_lock_state::write_locked) - read_write_lock_ops::write_unlock(m_mutex); - - m_state = read_write_lock_state::unlocked; - } - - void demote(void) - { - if (m_state != read_write_lock_state::write_locked) throw lock_error(); - read_write_lock_ops::demote(m_mutex); - m_state = read_write_lock_state::read_locked; - } - - bool try_demote(void) - { - if (m_state != read_write_lock_state::write_locked) throw lock_error(); - return read_write_lock_ops::try_demote(m_mutex) ? (m_state = read_write_lock_state::read_locked, true) : false; - } - - bool timed_demote(const xtime &xt) - { - if (m_state != read_write_lock_state::write_locked) throw lock_error(); - return read_write_lock_ops::timed_demote(m_mutex, xt) ? (m_state = read_write_lock_state::read_locked, true) : false; - } - - void promote(void) - { - if (m_state != read_write_lock_state::read_locked) throw lock_error(); - read_write_lock_ops::promote(m_mutex); - m_state = read_write_lock_state::write_locked; - } - - bool try_promote(void) - { - if (m_state != read_write_lock_state::read_locked) throw lock_error(); - return read_write_lock_ops::try_promote(m_mutex) ? (m_state = read_write_lock_state::write_locked, true) : false; - } - - bool timed_promote(const xtime &xt) - { - if (m_state != read_write_lock_state::read_locked) throw lock_error(); - return read_write_lock_ops::timed_promote(m_mutex, xt) ? (m_state = read_write_lock_state::write_locked, true) : false; - } - - //If allow_unlock = true, set_lock always succeeds and - //the function result indicates whether an unlock was required. - //If allow_unlock = false, set_lock may fail; - //the function result indicates whether it succeeded. - bool set_lock(read_write_lock_state::read_write_lock_state_enum ls, bool allow_unlock = true) - { - bool result = !allow_unlock; - - if (m_state != ls) - { - if (m_state == read_write_lock_state::unlocked) - { - if (ls == read_write_lock_state::read_locked) - read_lock(); - else //(ls == read_write_lock_state::write_locked) - write_lock(); - } - else //(m_state == read_write_lock_state::read_locked || m_state == read_write_lock_state::write_locked) - { - if (ls == read_write_lock_state::read_locked) - demote(); - else if (ls == read_write_lock_state::write_locked) - { - if (!try_promote()) - { - if (allow_unlock) - { - result = true; - unlock(); - write_lock(); - } - else - result = false; - } - } - else //(ls == read_write_lock_state::unlocked) - unlock(); - } - } - - return result; - } - - bool try_set_lock(read_write_lock_state::read_write_lock_state_enum ls) - { - if (m_state != ls) - { - if (m_state == read_write_lock_state::unlocked) - { - if (ls == read_write_lock_state::read_locked) - return try_read_lock(); - else // (ls == read_write_lock_state::write_locked) - return try_write_lock(); - } - else //(m_state == read_write_lock_state::read_locked || m_state == read_write_lock_state::write_locked) - { - if (ls == read_write_lock_state::read_locked) - return try_demote(); - else if (ls == read_write_lock_state::write_locked) - return try_promote(); - else //(ls == read_write_lock_state::unlocked) - return unlock(), true; - } - } - else //(m_state == ls) - return true; - } - - bool timed_set_lock(read_write_lock_state::read_write_lock_state_enum ls, const xtime &xt) - { - if (m_state != ls) - { - if (m_state == read_write_lock_state::unlocked) - { - if (ls == read_write_lock_state::read_locked) - return timed_read_lock(xt); - else // (ls == read_write_lock_state::write_locked) - return timed_write_lock(xt); - } - else //(m_state == read_write_lock_state::read_locked || m_state == read_write_lock_state::write_locked) - { - if (ls == read_write_lock_state::read_locked) - return timed_demote(xt); - else if (ls == read_write_lock_state::write_locked) - return timed_promote(xt); - else //(ls == read_write_lock_state::unlocked) - return unlock(), true; - } - } - else //(m_state == ls) - return true; - } - - bool locked() const - { - return m_state != read_write_lock_state::unlocked; - } - - bool read_locked() const - { - return m_state == read_write_lock_state::read_locked; - } - - bool write_locked() const - { - return m_state != read_write_lock_state::write_locked; - } - - operator const void*() const - { - return (m_state != read_write_lock_state::unlocked) ? this : 0; - } - - read_write_lock_state::read_write_lock_state_enum state() const - { - return m_state; - } - -private: - - TimedReadWriteMutex& m_mutex; - read_write_lock_state::read_write_lock_state_enum m_state; -}; - -template -class scoped_timed_read_lock : private noncopyable -{ -public: - - typedef TimedReadWriteMutex mutex_type; - - explicit scoped_timed_read_lock( - TimedReadWriteMutex& mx, - bool initially_locked = true) - : m_mutex(mx), m_state(read_write_lock_state::unlocked) - { - if (initially_locked) - lock(); - } - - ~scoped_timed_read_lock() - { - if (m_state != read_write_lock_state::unlocked) - unlock(); - } - - void lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - read_write_lock_ops::read_lock(m_mutex); - m_state = read_write_lock_state::read_locked; - } - - bool try_lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - if(read_write_lock_ops::try_read_lock(m_mutex)) - { - m_state = read_write_lock_state::read_locked; - return true; - } - return false; - } - - bool timed_lock(const xtime &xt) - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - if(read_write_lock_ops::timed_read_lock(m_mutex,xt)) - { - m_state = read_write_lock_state::read_locked; - return true; - } - return false; - } - - void unlock() - { - if (m_state != read_write_lock_state::read_locked) throw lock_error(); - read_write_lock_ops::read_unlock(m_mutex); - - m_state = read_write_lock_state::unlocked; - } - - bool locked() const - { - return m_state != read_write_lock_state::unlocked; - } - - operator const void*() const - { - return (m_state != read_write_lock_state::unlocked) ? this : 0; - } - - read_write_lock_state::read_write_lock_state_enum state() const - { - return m_state; - } - -private: - - TimedReadWriteMutex& m_mutex; - read_write_lock_state::read_write_lock_state_enum m_state; -}; - -template -class scoped_timed_write_lock : private noncopyable -{ -public: - - typedef TimedReadWriteMutex mutex_type; - - explicit scoped_timed_write_lock( - TimedReadWriteMutex& mx, - bool initially_locked = true) - : m_mutex(mx), m_state(read_write_lock_state::unlocked) - { - if (initially_locked) - lock(); - } - - ~scoped_timed_write_lock() - { - if (m_state != read_write_lock_state::unlocked) - unlock(); - } - - void lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - read_write_lock_ops::write_lock(m_mutex); - m_state = read_write_lock_state::write_locked; - } - - bool try_lock() - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - if(read_write_lock_ops::try_write_lock(m_mutex)) - { - m_state = read_write_lock_state::write_locked; - return true; - } - return false; - } - - bool timed_lock(const xtime &xt) - { - if (m_state != read_write_lock_state::unlocked) throw lock_error(); - if(read_write_lock_ops::timed_write_lock(m_mutex,xt)) - { - m_state = read_write_lock_state::write_locked; - return true; - } - return false; - } - - void unlock() - { - if (m_state != read_write_lock_state::write_locked) throw lock_error(); - read_write_lock_ops::write_unlock(m_mutex); - - m_state = read_write_lock_state::unlocked; - } - - bool locked() const - { - return m_state != read_write_lock_state::unlocked; - } - - operator const void*() const - { - return (m_state != read_write_lock_state::unlocked) ? this : 0; - } - - read_write_lock_state::read_write_lock_state_enum state() const - { - return m_state; - } - -private: - - TimedReadWriteMutex& m_mutex; - read_write_lock_state::read_write_lock_state_enum m_state; -}; - -} // namespace thread -} // namespace detail -} // namespace boost - -#endif - -// Change Log: -// 10 Mar 02 -// Original version. -// 4 May 04 GlassfordM -// Implement lock promotion and demotion (add member functions demote(), -// try_demote(), timed_demote(), try_promote(), timed_promote(); note -// that there is intentionally no promote() member function). -// Add set_lock() member function. -// Change try lock & timed lock constructor parameters for consistency. -// Rename to improve consistency and eliminate abbreviations: -// Use "read" and "write" instead of "shared" and "exclusive". -// Change "rd" to "read", "wr" to "write", "rw" to "read_write". -// Add mutex_type typdef. From 1836ee854fbdedf212f47807954fcc1d5749e4c8 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 19 Oct 2007 14:52:52 +0000 Subject: [PATCH 014/214] small changes to reduce warnings; extracted pthread_mutex_scoped_lock to its own file [SVN r40187] --- include/boost/thread/pthread/mutex.hpp | 28 ++++------------- .../pthread/pthread_mutex_scoped_lock.hpp | 30 +++++++++++++++++++ .../boost/thread/pthread/recursive_mutex.hpp | 28 ++++------------- include/boost/thread/thread_time.hpp | 4 +-- .../boost/thread/win32/basic_timed_mutex.hpp | 2 +- 5 files changed, 43 insertions(+), 49 deletions(-) create mode 100644 include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index b8b9c670..e75088e0 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -14,6 +14,7 @@ #include #include #include "timespec.hpp" +#include "pthread_mutex_scoped_lock.hpp" #ifdef _POSIX_TIMEOUTS #if _POSIX_TIMEOUTS >= 0 @@ -75,25 +76,6 @@ namespace boost #ifndef BOOST_PTHREAD_HAS_TIMEDLOCK pthread_cond_t cond; bool is_locked; - - struct pthread_mutex_scoped_lock - { - pthread_mutex_t* m; - explicit pthread_mutex_scoped_lock(pthread_mutex_t* m_): - m(m_) - { - int const res=pthread_mutex_lock(m); - BOOST_ASSERT(!res); - } - ~pthread_mutex_scoped_lock() - { - int const res=pthread_mutex_unlock(m); - BOOST_ASSERT(!res); - } - - }; - - #endif public: timed_mutex() @@ -159,7 +141,7 @@ namespace boost #else void lock() { - pthread_mutex_scoped_lock const _(&m); + boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); while(is_locked) { int const cond_res=pthread_cond_wait(&cond,&m); @@ -170,7 +152,7 @@ namespace boost void unlock() { - pthread_mutex_scoped_lock const _(&m); + boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); is_locked=false; int const res=pthread_cond_signal(&cond); BOOST_ASSERT(!res); @@ -178,7 +160,7 @@ namespace boost bool try_lock() { - pthread_mutex_scoped_lock const _(&m); + boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); if(is_locked) { return false; @@ -190,7 +172,7 @@ namespace boost bool timed_lock(system_time const & abs_time) { struct timespec const timeout=detail::get_timespec(abs_time); - pthread_mutex_scoped_lock const _(&m); + boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); while(is_locked) { int const cond_res=pthread_cond_timedwait(&cond,&m,&timeout); diff --git a/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp b/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp new file mode 100644 index 00000000..a691284a --- /dev/null +++ b/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp @@ -0,0 +1,30 @@ +#ifndef BOOST_PTHREAD_MUTEX_SCOPED_LOCK_HPP +#define BOOST_PTHREAD_MUTEX_SCOPED_LOCK_HPP +#include +#include + +namespace boost +{ + namespace pthread + { + class pthread_mutex_scoped_lock + { + pthread_mutex_t* m; + public: + explicit pthread_mutex_scoped_lock(pthread_mutex_t* m_): + m(m_) + { + int const res=pthread_mutex_lock(m); + BOOST_ASSERT(!res); + } + ~pthread_mutex_scoped_lock() + { + int const res=pthread_mutex_unlock(m); + BOOST_ASSERT(!res); + } + + }; + } +} + +#endif diff --git a/include/boost/thread/pthread/recursive_mutex.hpp b/include/boost/thread/pthread/recursive_mutex.hpp index 92002600..21395b4d 100644 --- a/include/boost/thread/pthread/recursive_mutex.hpp +++ b/include/boost/thread/pthread/recursive_mutex.hpp @@ -15,6 +15,7 @@ #include #include #include "timespec.hpp" +#include "pthread_mutex_scoped_lock.hpp" #ifdef _POSIX_TIMEOUTS #if _POSIX_TIMEOUTS >= 0 @@ -93,25 +94,6 @@ namespace boost bool is_locked; pthread_t owner; unsigned count; - - struct pthread_mutex_scoped_lock - { - pthread_mutex_t* m; - explicit pthread_mutex_scoped_lock(pthread_mutex_t* m_): - m(m_) - { - int const res=pthread_mutex_lock(m); - BOOST_ASSERT(!res); - } - ~pthread_mutex_scoped_lock() - { - int const res=pthread_mutex_unlock(m); - BOOST_ASSERT(!res); - } - - }; - - #endif public: recursive_timed_mutex() @@ -201,7 +183,7 @@ namespace boost #else void lock() { - pthread_mutex_scoped_lock const _(&m); + boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); if(is_locked && owner==pthread_self()) { ++count; @@ -220,7 +202,7 @@ namespace boost void unlock() { - pthread_mutex_scoped_lock const _(&m); + boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); if(!--count) { is_locked=false; @@ -231,7 +213,7 @@ namespace boost bool try_lock() { - pthread_mutex_scoped_lock const _(&m); + boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); if(is_locked && owner!=pthread_self()) { return false; @@ -245,7 +227,7 @@ namespace boost bool timed_lock(system_time const & abs_time) { struct timespec const timeout=detail::get_timespec(abs_time); - pthread_mutex_scoped_lock const _(&m); + boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); if(is_locked && owner==pthread_self()) { ++count; diff --git a/include/boost/thread/thread_time.hpp b/include/boost/thread/thread_time.hpp index 017a112f..73443701 100644 --- a/include/boost/thread/thread_time.hpp +++ b/include/boost/thread/thread_time.hpp @@ -19,11 +19,11 @@ namespace boost return system_time(boost::posix_time::pos_infin); } - inline unsigned get_milliseconds_until(system_time const& target_time) + inline system_time::time_duration_type::tick_type get_milliseconds_until(system_time const& target_time) { if(target_time.is_pos_infinity()) { - return ~0u; + return ~(system_time::time_duration_type::tick_type)0; } system_time const now=get_system_time(); if(target_time<=now) diff --git a/include/boost/thread/win32/basic_timed_mutex.hpp b/include/boost/thread/win32/basic_timed_mutex.hpp index 155962cb..6fdd40ab 100644 --- a/include/boost/thread/win32/basic_timed_mutex.hpp +++ b/include/boost/thread/win32/basic_timed_mutex.hpp @@ -113,7 +113,7 @@ namespace boost void unlock() { long const offset=lock_flag_value+1; - long old_count=BOOST_INTERLOCKED_EXCHANGE_ADD(&active_count,-offset); + long old_count=BOOST_INTERLOCKED_EXCHANGE_ADD(&active_count,(~offset)+1); if(old_count>offset) { From f86156ad105b5188f1e4dd07a40465092ce0d25c Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 19 Oct 2007 15:31:35 +0000 Subject: [PATCH 015/214] more tweaks to remove warnings [SVN r40189] --- include/boost/thread/thread_time.hpp | 6 +++--- include/boost/thread/xtime.hpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/boost/thread/thread_time.hpp b/include/boost/thread/thread_time.hpp index 73443701..dd0c3a86 100644 --- a/include/boost/thread/thread_time.hpp +++ b/include/boost/thread/thread_time.hpp @@ -19,18 +19,18 @@ namespace boost return system_time(boost::posix_time::pos_infin); } - inline system_time::time_duration_type::tick_type get_milliseconds_until(system_time const& target_time) + inline unsigned long get_milliseconds_until(system_time const& target_time) { if(target_time.is_pos_infinity()) { - return ~(system_time::time_duration_type::tick_type)0; + return ~(unsigned long)0; } system_time const now=get_system_time(); if(target_time<=now) { return 0; } - return (target_time-now).total_milliseconds()+1; + return static_cast((target_time-now).total_milliseconds()+1); } } diff --git a/include/boost/thread/xtime.hpp b/include/boost/thread/xtime.hpp index a8fa1c9f..99c1f16a 100644 --- a/include/boost/thread/xtime.hpp +++ b/include/boost/thread/xtime.hpp @@ -56,8 +56,8 @@ inline xtime get_xtime(boost::system_time const& abs_time) xtime res={0}; boost::posix_time::time_duration const time_since_epoch=abs_time-boost::posix_time::from_time_t(0); - res.sec=time_since_epoch.total_seconds(); - res.nsec=time_since_epoch.fractional_seconds()*(1000000000/time_since_epoch.ticks_per_second()); + res.sec=static_cast(time_since_epoch.total_seconds()); + res.nsec=static_cast(time_since_epoch.fractional_seconds()*(1000000000/time_since_epoch.ticks_per_second())); return res; } From d4b0a977c91ccee6285a0ee099cb2a9b619ef26a Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 19 Oct 2007 17:40:04 +0000 Subject: [PATCH 016/214] New condition_variable and condition_variable_any as per proposed C++0x interface [SVN r40191] --- build/Jamfile.v2 | 4 +- include/boost/thread/barrier.hpp | 61 +- include/boost/thread/condition.hpp | 198 +---- include/boost/thread/condition_variable.hpp | 15 + include/boost/thread/exceptions.hpp | 12 + .../thread/pthread/condition_variable.hpp | 194 +++++ include/boost/thread/pthread/mutex.hpp | 7 + include/boost/thread/pthread/shared_mutex.hpp | 7 +- include/boost/thread/thread.hpp | 1 - .../boost/thread/win32/condition_variable.hpp | 309 ++++++++ .../boost/thread/win32/interlocked_read.hpp | 19 + include/boost/thread/xtime.hpp | 13 + src/barrier.cpp | 42 -- src/condition.cpp | 705 ------------------ 14 files changed, 618 insertions(+), 969 deletions(-) create mode 100644 include/boost/thread/condition_variable.hpp create mode 100644 include/boost/thread/pthread/condition_variable.hpp create mode 100644 include/boost/thread/win32/condition_variable.hpp delete mode 100644 src/barrier.cpp delete mode 100644 src/condition.cpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 961e01e8..560161b5 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -14,8 +14,8 @@ project boost/thread ; CPP_SOURCES = - barrier - condition +# barrier +# condition exceptions # mutex # once diff --git a/include/boost/thread/barrier.hpp b/include/boost/thread/barrier.hpp index 8048784c..79a22e66 100644 --- a/include/boost/thread/barrier.hpp +++ b/include/boost/thread/barrier.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2002-2003 // David Moore, William E. Kempf +// Copyright (C) 2007 Anthony Williams // // 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) @@ -11,33 +12,47 @@ #include #include +#include +#include -namespace boost { - -class BOOST_THREAD_DECL barrier +namespace boost { -public: - barrier(unsigned int count); - ~barrier(); - bool wait(); + class barrier + { + public: + barrier(unsigned int count) + : m_threshold(count), m_count(count), m_generation(0) + { + if (count == 0) + throw std::invalid_argument("count cannot be zero."); + } + + bool wait() + { + boost::mutex::scoped_lock lock(m_mutex); + unsigned int gen = m_generation; + + if (--m_count == 0) + { + m_generation++; + m_count = m_threshold; + m_cond.notify_all(); + return true; + } -private: - mutex m_mutex; -// disable warnings about non dll import -// see: http://www.boost.org/more/separate_compilation.html#dlls -#ifdef BOOST_MSVC -# pragma warning(push) -# pragma warning(disable: 4251 4231 4660 4275) -#endif - condition m_cond; -#ifdef BOOST_MSVC -# pragma warning(pop) -#endif - unsigned int m_threshold; - unsigned int m_count; - unsigned int m_generation; -}; + while (gen == m_generation) + m_cond.wait(lock); + return false; + } + + private: + mutex m_mutex; + condition m_cond; + unsigned int m_threshold; + unsigned int m_count; + unsigned int m_generation; + }; } // namespace boost diff --git a/include/boost/thread/condition.hpp b/include/boost/thread/condition.hpp index f7ac7194..3f2be26d 100644 --- a/include/boost/thread/condition.hpp +++ b/include/boost/thread/condition.hpp @@ -1,196 +1,10 @@ -// Copyright (C) 2001-2003 -// William E. Kempf -// Copyright (C) 2007 Anthony Williams -// -// 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) +#ifndef BOOST_THREAD_CONDITION_HPP +#define BOOST_THREAD_CONDITION_HPP +#include -#ifndef BOOST_CONDITION_WEK070601_HPP -#define BOOST_CONDITION_WEK070601_HPP - -#include - -#include -#include - -#if defined(BOOST_HAS_PTHREADS) -# include -#elif defined(BOOST_HAS_MPTASKS) -# include "scoped_critical_region.hpp" -#endif - -namespace boost { - -struct xtime; -// disable warnings about non dll import -// see: http://www.boost.org/more/separate_compilation.html#dlls -#ifdef BOOST_MSVC -# pragma warning(push) -# pragma warning(disable: 4251 4231 4660 4275) -#endif - -namespace detail { - -class BOOST_THREAD_DECL condition_impl : private noncopyable +namespace boost { - friend class condition; + typedef condition_variable_any condition; +} -public: - condition_impl(); - ~condition_impl(); - - void notify_one(); - void notify_all(); - -#if (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS)) - void enter_wait(); - void do_wait(); - bool do_timed_wait(const xtime& xt); -#elif defined(BOOST_HAS_PTHREADS) - void do_wait(pthread_mutex_t* pmutex); - bool do_timed_wait(const xtime& xt, pthread_mutex_t* pmutex); #endif - -#if defined(BOOST_HAS_WINTHREADS) - void* m_gate; - void* m_queue; - void* m_mutex; - unsigned m_gone; // # threads that timed out and never made it to m_queue - unsigned long m_blocked; // # threads blocked on the condition - unsigned m_waiting; // # threads no longer waiting for the condition but - // still waiting to be removed from m_queue -#elif defined(BOOST_HAS_PTHREADS) - pthread_cond_t m_condition; - pthread_mutex_t m_mutex; -#elif defined(BOOST_HAS_MPTASKS) - MPSemaphoreID m_gate; - MPSemaphoreID m_queue; - threads::mac::detail::scoped_critical_region m_mutex; - threads::mac::detail::scoped_critical_region m_mutex_mutex; - unsigned m_gone; // # threads that timed out and never made it to m_queue - unsigned long m_blocked; // # threads blocked on the condition - unsigned m_waiting; // # threads no longer waiting for the condition but - // still waiting to be removed from m_queue -#endif -}; - -} // namespace detail - -class condition : private noncopyable -{ -public: - condition() { } - ~condition() { } - - void notify_one() { m_impl.notify_one(); } - void notify_all() { m_impl.notify_all(); } - - template - void wait(L& lock) - { - if (!lock) - throw lock_error(); - - do_wait(*lock.mutex()); - } - - template - void wait(L& lock, Pr pred) - { - if (!lock) - throw lock_error(); - - while (!pred()) - do_wait(*lock.mutex()); - } - - template - bool timed_wait(L& lock, const xtime& xt) - { - if (!lock) - throw lock_error(); - - return do_timed_wait(*lock.mutex(), xt); - } - - template - bool timed_wait(L& lock, const xtime& xt, Pr pred) - { - if (!lock) - throw lock_error(); - - while (!pred()) - { - if (!do_timed_wait(*lock.mutex(), xt)) - return false; - } - - return true; - } - -private: - detail::condition_impl m_impl; - - template - void do_wait(M& mutex) - { -#if (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS)) - m_impl.enter_wait(); -#else - pthread_mutex_lock(&m_impl.m_mutex); -#endif - - mutex.unlock(); - -#if defined(BOOST_HAS_PTHREADS) - m_impl.do_wait(&m_impl.m_mutex); -#elif (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS)) - m_impl.do_wait(); -#endif - -#if defined(BOOST_HAS_PTHREADS) - pthread_mutex_unlock(&m_impl.m_mutex); -#endif - mutex.lock(); - } - - template - bool do_timed_wait(M& mutex, const xtime& xt) - { -#if (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS)) - m_impl.enter_wait(); -#else - pthread_mutex_lock(&m_impl.m_mutex); -#endif - - mutex.unlock(); - - bool ret = false; - -#if defined(BOOST_HAS_PTHREADS) - ret = m_impl.do_timed_wait(xt, &m_impl.m_mutex); -#elif (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS)) - ret = m_impl.do_timed_wait(xt); -#endif - -#if defined(BOOST_HAS_PTHREADS) - pthread_mutex_unlock(&m_impl.m_mutex); -#endif - mutex.lock(); - - return ret; - } -}; -#ifdef BOOST_MSVC -# pragma warning(pop) -#endif -} // namespace boost - -// Change Log: -// 8 Feb 01 WEKEMPF Initial version. -// 22 May 01 WEKEMPF Modified to use xtime for time outs. -// 23 May 01 WEKEMPF Removed "duration" timed_waits, as they are too -// difficult to use with spurious wakeups. -// 3 Jan 03 WEKEMPF Modified for DLL implementation. - -#endif // BOOST_CONDITION_WEK070601_HPP diff --git a/include/boost/thread/condition_variable.hpp b/include/boost/thread/condition_variable.hpp new file mode 100644 index 00000000..2be3cd5a --- /dev/null +++ b/include/boost/thread/condition_variable.hpp @@ -0,0 +1,15 @@ +#ifndef BOOST_THREAD_CONDITION_VARIABLE_HPP +#define BOOST_THREAD_CONDITION_VARIABLE_HPP + +// condition_variable.hpp +// +// (C) Copyright 2007 Anthony Williams +// +// 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) + +#include +#include BOOST_THREAD_PLATFORM(condition_variable.hpp) + +#endif diff --git a/include/boost/thread/exceptions.hpp b/include/boost/thread/exceptions.hpp index 9b27d312..e3ff59a5 100644 --- a/include/boost/thread/exceptions.hpp +++ b/include/boost/thread/exceptions.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf +// Copyright (C) 2007 Anthony Williams // // 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) @@ -35,6 +36,17 @@ private: int m_sys_err; }; + class condition_error: + public std::exception + { + public: + const char* what() const throw() + { + return "Condition error"; + } + }; + + class BOOST_THREAD_DECL lock_error : public thread_exception { public: diff --git a/include/boost/thread/pthread/condition_variable.hpp b/include/boost/thread/pthread/condition_variable.hpp new file mode 100644 index 00000000..9f90f085 --- /dev/null +++ b/include/boost/thread/pthread/condition_variable.hpp @@ -0,0 +1,194 @@ +#ifndef BOOST_THREAD_CONDITION_VARIABLE_WIN32_HPP +#define BOOST_THREAD_CONDITION_VARIABLE_WIN32_HPP +// 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) +// (C) Copyright 2007 Anthony Williams + +#include +#include +#include +#include +#include +#include +#include +#include "timespec.hpp" +#include "pthread_mutex_scoped_lock.hpp" + +namespace boost +{ + class condition_variable + { + private: + pthread_cond_t cond; + + condition_variable(condition_variable&); + condition_variable& operator=(condition_variable&); + public: + condition_variable() + { + int const res=pthread_cond_init(&cond,NULL); + if(res) + { + throw thread_resource_error(); + } + } + ~condition_variable() + { + int const res=pthread_cond_destroy(&cond); + BOOST_ASSERT(!res); + } + + void wait(unique_lock& m) + { + int const cond_res=pthread_cond_wait(&cond,m.mutex()->native_handle()); + BOOST_ASSERT(!cond_res); + } + + template + void wait(unique_lock& m,predicate_type pred) + { + while(!pred()) wait(m); + } + + bool timed_wait(unique_lock& m,boost::system_time const& wait_until) + { + struct timespec const timeout=detail::get_timespec(wait_until); + int const cond_res=pthread_cond_timedwait(&cond,m.mutex()->native_handle(),&timeout); + if(cond_res==ETIMEDOUT) + { + return false; + } + BOOST_ASSERT(!cond_res); + return true; + } + + template + bool timed_wait(unique_lock& m,boost::system_time const& wait_until,predicate_type pred) + { + while (!pred()) + { + if(!timed_wait(m, wait_until)) + return false; + } + return true; + } + + void notify_one() + { + int const res=pthread_cond_signal(&cond); + BOOST_ASSERT(!res); + } + + void notify_all() + { + int const res=pthread_cond_broadcast(&cond); + BOOST_ASSERT(!res); + } + }; + + class condition_variable_any + { + pthread_mutex_t internal_mutex; + pthread_cond_t cond; + + condition_variable_any(condition_variable&); + condition_variable_any& operator=(condition_variable&); + + public: + condition_variable_any() + { + int const res=pthread_mutex_init(&internal_mutex,NULL); + if(res) + { + throw thread_resource_error(); + } + int const res2=pthread_cond_init(&cond,NULL); + if(res2) + { + int const destroy_res=pthread_mutex_destroy(&internal_mutex); + BOOST_ASSERT(!destroy_res); + throw thread_resource_error(); + } + } + ~condition_variable_any() + { + int const res=pthread_mutex_destroy(&internal_mutex); + BOOST_ASSERT(!res); + int const res2=pthread_cond_destroy(&cond); + BOOST_ASSERT(!res2); + } + + template + void wait(lock_type& m) + { + int res=0; + { + boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); + m.unlock(); + res=pthread_cond_wait(&cond,&internal_mutex); + } + m.lock(); + if(res) + { + throw condition_error(); + } + } + + template + void wait(lock_type& m,predicate_type pred) + { + while(!pred()) wait(m); + } + + template + bool timed_wait(lock_type& m,boost::system_time const& wait_until) + { + struct timespec const timeout=detail::get_timespec(wait_until); + int res=0; + { + boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); + m.unlock(); + res=pthread_cond_timedwait(&cond,&internal_mutex,&timeout); + } + m.lock(); + if(res==ETIMEDOUT) + { + return false; + } + if(res) + { + throw condition_error(); + } + return true; + } + + template + bool timed_wait(lock_type& m,boost::system_time const& wait_until,predicate_type pred) + { + while (!pred()) + { + if(!timed_wait(m, wait_until)) + return false; + } + return true; + } + + void notify_one() + { + boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); + int const res=pthread_cond_signal(&cond); + BOOST_ASSERT(!res); + } + + void notify_all() + { + boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); + int const res=pthread_cond_broadcast(&cond); + BOOST_ASSERT(!res); + } + }; + +} + +#endif diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index e75088e0..21a2671f 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -62,6 +62,13 @@ namespace boost BOOST_ASSERT(!res || res==EBUSY); return !res; } + + typedef pthread_mutex_t* native_handle_type; + native_handle_type native_handle() const + { + return &m; + } + typedef unique_lock scoped_lock; typedef scoped_lock scoped_try_lock; }; diff --git a/include/boost/thread/pthread/shared_mutex.hpp b/include/boost/thread/pthread/shared_mutex.hpp index 673cfc00..300d008d 100644 --- a/include/boost/thread/pthread/shared_mutex.hpp +++ b/include/boost/thread/pthread/shared_mutex.hpp @@ -11,7 +11,6 @@ #include #include #include -#include namespace boost { @@ -95,7 +94,7 @@ namespace boost return true; } - if(!shared_cond.timed_wait(lock,get_xtime(timeout))) + if(!shared_cond.timed_wait(lock,timeout)) { return false; } @@ -157,7 +156,7 @@ namespace boost state.exclusive=true; return true; } - if(!exclusive_cond.timed_wait(lock,get_xtime(timeout))) + if(!exclusive_cond.timed_wait(lock,timeout)) { return false; } @@ -216,7 +215,7 @@ namespace boost return true; } - if(!shared_cond.timed_wait(lock,get_xtime(timeout))) + if(!shared_cond.timed_wait(lock,timeout)) { return false; } diff --git a/include/boost/thread/thread.hpp b/include/boost/thread/thread.hpp index 64ab35c7..0f410eb9 100644 --- a/include/boost/thread/thread.hpp +++ b/include/boost/thread/thread.hpp @@ -17,7 +17,6 @@ #if defined(BOOST_HAS_PTHREADS) # include -# include #elif defined(BOOST_HAS_MPTASKS) # include #endif diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp new file mode 100644 index 00000000..38ad587e --- /dev/null +++ b/include/boost/thread/win32/condition_variable.hpp @@ -0,0 +1,309 @@ +#ifndef BOOST_THREAD_CONDITION_VARIABLE_WIN32_HPP +#define BOOST_THREAD_CONDITION_VARIABLE_WIN32_HPP +// 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) +// (C) Copyright 2007 Anthony Williams + +#include +#include "thread_primitives.hpp" +#include +#include +#include +#include +#include +#include "interlocked_read.hpp" + +namespace boost +{ + namespace detail + { + class basic_condition_variable + { + boost::mutex internal_mutex; + long total_count; + unsigned active_generation_count; + + struct list_entry + { + detail::win32::handle semaphore; + long count; + bool notified; + }; + + BOOST_STATIC_CONSTANT(unsigned,generation_count=3); + + list_entry generations[generation_count]; + detail::win32::handle wake_sem; + + static bool no_waiters(list_entry const& entry) + { + return entry.count==0; + } + + void shift_generations_down() + { + list_entry* const last_active_entry=std::remove_if(generations,generations+generation_count,no_waiters); + if(last_active_entry==generations+generation_count) + { + broadcast_entry(generations[generation_count-1],false); + } + else + { + active_generation_count=(last_active_entry-generations)+1; + } + + std::copy_backward(generations,generations+active_generation_count-1,generations+active_generation_count); + generations[0]=list_entry(); + } + + void broadcast_entry(list_entry& entry,bool wake) + { + long const count_to_wake=entry.count; + detail::interlocked_write_release(&total_count,total_count-count_to_wake); + if(wake) + { + detail::win32::ReleaseSemaphore(wake_sem,count_to_wake,0); + } + detail::win32::ReleaseSemaphore(entry.semaphore,count_to_wake,0); + entry.count=0; + dispose_entry(entry); + } + + + void dispose_entry(list_entry& entry) + { + if(entry.semaphore) + { + unsigned long const close_result=detail::win32::CloseHandle(entry.semaphore); + BOOST_ASSERT(close_result); + entry.semaphore=0; + } + entry.notified=false; + } + + template + struct relocker + { + lock_type& lock; + bool unlocked; + + relocker(lock_type& lock_): + lock(lock_),unlocked(false) + {} + void unlock() + { + lock.unlock(); + unlocked=true; + } + ~relocker() + { + if(unlocked) + { + lock.lock(); + } + + } + }; + + + protected: + template + bool do_wait(lock_type& lock,::boost::system_time const& wait_until) + { + detail::win32::handle_manager local_wake_sem; + detail::win32::handle_manager sem; + bool first_loop=true; + bool woken=false; + + relocker locker(lock); + + while(!woken) + { + { + boost::mutex::scoped_lock internal_lock(internal_mutex); + detail::interlocked_write_release(&total_count,total_count+1); + if(first_loop) + { + locker.unlock(); + if(!wake_sem) + { + wake_sem=detail::win32::create_anonymous_semaphore(0,LONG_MAX); + BOOST_ASSERT(wake_sem); + } + local_wake_sem=detail::win32::duplicate_handle(wake_sem); + + if(generations[0].notified) + { + shift_generations_down(); + } + else if(!active_generation_count) + { + active_generation_count=1; + } + + first_loop=false; + } + if(!generations[0].semaphore) + { + generations[0].semaphore=detail::win32::create_anonymous_semaphore(0,LONG_MAX); + BOOST_ASSERT(generations[0].semaphore); + } + ++generations[0].count; + sem=detail::win32::duplicate_handle(generations[0].semaphore); + } + unsigned long const wait_result=detail::win32::WaitForSingleObject(sem,::boost::detail::get_milliseconds_until(wait_until)); + + if(wait_result==detail::win32::timeout) + { + break; + } + BOOST_ASSERT(!wait_result); + + unsigned long const woken_result=detail::win32::WaitForSingleObject(local_wake_sem,0); + BOOST_ASSERT(woken_result==detail::win32::timeout || woken_result==0); + + woken=(woken_result==0); + } + return woken; + } + + basic_condition_variable(const basic_condition_variable& other); + basic_condition_variable& operator=(const basic_condition_variable& other); + public: + basic_condition_variable(): + total_count(0),active_generation_count(0),wake_sem(0) + { + for(unsigned i=0;i& m) + { + do_wait(m,::boost::detail::get_system_time_sentinel()); + } + + template + void wait(unique_lock& m,predicate_type pred) + { + while(!pred()) wait(m); + } + + + bool timed_wait(unique_lock& m,boost::system_time const& wait_until) + { + return do_wait(m,wait_until); + } + + template + bool timed_wait(unique_lock& m,boost::system_time const& wait_until,predicate_type pred) + { + while (!pred()) + { + if(!timed_wait(m, wait_until)) + return false; + } + return true; + } + }; + + class condition_variable_any: + public detail::basic_condition_variable + { + public: + template + void wait(lock_type& m) + { + do_wait(m,::boost::detail::get_system_time_sentinel()); + } + + template + void wait(lock_type& m,predicate_type pred) + { + while(!pred()) wait(m); + } + + template + bool timed_wait(lock_type& m,boost::system_time const& wait_until) + { + return do_wait(m,wait_until); + } + + template + bool timed_wait(lock_type& m,boost::system_time const& wait_until,predicate_type pred) + { + while (!pred()) + { + if(!timed_wait(m, wait_until)) + return false; + } + return true; + } + }; + +} + +#endif diff --git a/include/boost/thread/win32/interlocked_read.hpp b/include/boost/thread/win32/interlocked_read.hpp index 77a333ba..72315f60 100644 --- a/include/boost/thread/win32/interlocked_read.hpp +++ b/include/boost/thread/win32/interlocked_read.hpp @@ -30,6 +30,17 @@ namespace boost _ReadWriteBarrier(); return res; } + + inline void interlocked_write_release(long volatile* x,long value) + { + _ReadWriteBarrier(); + *x=value; + } + inline void interlocked_write_release(void* volatile* x,void* value) + { + _ReadWriteBarrier(); + *x=value; + } } } @@ -49,6 +60,14 @@ namespace boost { return BOOST_INTERLOCKED_COMPARE_EXCHANGE_POINTER(x,0,0); } + inline void interlocked_write_release(long volatile* x,long value) + { + BOOST_INTERLOCKED_EXCHANGE(x,value); + } + inline void interlocked_write_release(void* volatile* x,void* value) + { + BOOST_INTERLOCKED_EXCHANGE_POINTER(x,value); + } } } diff --git a/include/boost/thread/xtime.hpp b/include/boost/thread/xtime.hpp index 99c1f16a..8f2f4230 100644 --- a/include/boost/thread/xtime.hpp +++ b/include/boost/thread/xtime.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf +// Copyright (C) 2007 Anthony Williams // // 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) @@ -39,6 +40,18 @@ struct xtime xtime_sec_t sec; xtime_nsec_t nsec; + + operator system_time() const + { + return boost::posix_time::from_time_t(0)+ + boost::posix_time::seconds(static_cast(sec))+ +#ifdef BOOST_DATE_TIME_HAS_NANOSECONDS + boost::posix_time::nanoseconds(nsec); +#else + boost::posix_time::microseconds((nsec+500)/1000); +#endif + } + }; int BOOST_THREAD_DECL xtime_get(struct xtime* xtp, int clock_type); diff --git a/src/barrier.cpp b/src/barrier.cpp deleted file mode 100644 index e0c94930..00000000 --- a/src/barrier.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2002-2003 -// David Moore, William E. Kempf -// -// 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) - -#include -#include -#include // see http://article.gmane.org/gmane.comp.lib.boost.devel/106981 - -namespace boost { - -barrier::barrier(unsigned int count) - : m_threshold(count), m_count(count), m_generation(0) -{ - if (count == 0) - throw std::invalid_argument("count cannot be zero."); -} - -barrier::~barrier() -{ -} - -bool barrier::wait() -{ - boost::mutex::scoped_lock lock(m_mutex); - unsigned int gen = m_generation; - - if (--m_count == 0) - { - m_generation++; - m_count = m_threshold; - m_cond.notify_all(); - return true; - } - - while (gen == m_generation) - m_cond.wait(lock); - return false; -} - -} // namespace boost diff --git a/src/condition.cpp b/src/condition.cpp deleted file mode 100644 index 18ca0ef8..00000000 --- a/src/condition.cpp +++ /dev/null @@ -1,705 +0,0 @@ -// Copyright (C) 2001-2003 -// William E. Kempf -// -// 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) - -#include - -#include -#include -#include -#include -#include -#include -#include "timeconv.inl" - -#if defined(BOOST_HAS_WINTHREADS) -# ifndef NOMINMAX -# define NOMINMAX -# endif -# include -#elif defined(BOOST_HAS_PTHREADS) -# include -#elif defined(BOOST_HAS_MPTASKS) -# include -# include "mac/init.hpp" -# include "mac/safe.hpp" -#endif - -// The following include can be removed after the bug on QNX -// has been tracked down. I need this only for debugging -//#if !defined(NDEBUG) && defined(BOOST_HAS_PTHREADS) -#include -//#endif - -namespace boost { - -namespace detail { - -#if defined(BOOST_HAS_WINTHREADS) -condition_impl::condition_impl() - : m_gone(0), m_blocked(0), m_waiting(0) -{ - m_gate = reinterpret_cast(CreateSemaphore(0, 1, 1, 0)); - m_queue = reinterpret_cast( - CreateSemaphore(0, 0, (std::numeric_limits::max)(), 0)); - m_mutex = reinterpret_cast(CreateMutex(0, 0, 0)); - - if (!m_gate || !m_queue || !m_mutex) - { - int res = 0; - if (m_gate) - { - res = CloseHandle(reinterpret_cast(m_gate)); - assert(res); - } - if (m_queue) - { - res = CloseHandle(reinterpret_cast(m_queue)); - assert(res); - } - if (m_mutex) - { - res = CloseHandle(reinterpret_cast(m_mutex)); - assert(res); - } - - throw thread_resource_error(); - } -} - -condition_impl::~condition_impl() -{ - int res = 0; - res = CloseHandle(reinterpret_cast(m_gate)); - assert(res); - res = CloseHandle(reinterpret_cast(m_queue)); - assert(res); - res = CloseHandle(reinterpret_cast(m_mutex)); - assert(res); -} - -void condition_impl::notify_one() -{ - unsigned signals = 0; - - int res = 0; - res = WaitForSingleObject(reinterpret_cast(m_mutex), INFINITE); - assert(res == WAIT_OBJECT_0); - - if (m_waiting != 0) // the m_gate is already closed - { - if (m_blocked == 0) - { - res = ReleaseMutex(reinterpret_cast(m_mutex)); - assert(res); - return; - } - - ++m_waiting; - --m_blocked; - signals = 1; - } - else - { - res = WaitForSingleObject(reinterpret_cast(m_gate), INFINITE); - assert(res == WAIT_OBJECT_0); - if (m_blocked > m_gone) - { - if (m_gone != 0) - { - m_blocked -= m_gone; - m_gone = 0; - } - signals = m_waiting = 1; - --m_blocked; - } - else - { - res = ReleaseSemaphore(reinterpret_cast(m_gate), 1, 0); - assert(res); - } - } - - res = ReleaseMutex(reinterpret_cast(m_mutex)); - assert(res); - - if (signals) - { - res = ReleaseSemaphore(reinterpret_cast(m_queue), signals, 0); - assert(res); - } -} - -void condition_impl::notify_all() -{ - unsigned signals = 0; - - int res = 0; - res = WaitForSingleObject(reinterpret_cast(m_mutex), INFINITE); - assert(res == WAIT_OBJECT_0); - - if (m_waiting != 0) // the m_gate is already closed - { - if (m_blocked == 0) - { - res = ReleaseMutex(reinterpret_cast(m_mutex)); - assert(res); - return; - } - - m_waiting += (signals = m_blocked); - m_blocked = 0; - } - else - { - res = WaitForSingleObject(reinterpret_cast(m_gate), INFINITE); - assert(res == WAIT_OBJECT_0); - if (m_blocked > m_gone) - { - if (m_gone != 0) - { - m_blocked -= m_gone; - m_gone = 0; - } - signals = m_waiting = m_blocked; - m_blocked = 0; - } - else - { - res = ReleaseSemaphore(reinterpret_cast(m_gate), 1, 0); - assert(res); - } - } - - res = ReleaseMutex(reinterpret_cast(m_mutex)); - assert(res); - - if (signals) - { - res = ReleaseSemaphore(reinterpret_cast(m_queue), signals, 0); - assert(res); - } -} - -void condition_impl::enter_wait() -{ - int res = 0; - res = WaitForSingleObject(reinterpret_cast(m_gate), INFINITE); - assert(res == WAIT_OBJECT_0); - ++m_blocked; - res = ReleaseSemaphore(reinterpret_cast(m_gate), 1, 0); - assert(res); -} - -void condition_impl::do_wait() -{ - int res = 0; - res = WaitForSingleObject(reinterpret_cast(m_queue), INFINITE); - assert(res == WAIT_OBJECT_0); - - unsigned was_waiting=0; - unsigned was_gone=0; - - res = WaitForSingleObject(reinterpret_cast(m_mutex), INFINITE); - assert(res == WAIT_OBJECT_0); - was_waiting = m_waiting; - was_gone = m_gone; - if (was_waiting != 0) - { - if (--m_waiting == 0) - { - if (m_blocked != 0) - { - res = ReleaseSemaphore(reinterpret_cast(m_gate), 1, - 0); // open m_gate - assert(res); - was_waiting = 0; - } - else if (m_gone != 0) - m_gone = 0; - } - } - else if (++m_gone == ((std::numeric_limits::max)() / 2)) - { - // timeout occured, normalize the m_gone count - // this may occur if many calls to wait with a timeout are made and - // no call to notify_* is made - res = WaitForSingleObject(reinterpret_cast(m_gate), INFINITE); - assert(res == WAIT_OBJECT_0); - m_blocked -= m_gone; - res = ReleaseSemaphore(reinterpret_cast(m_gate), 1, 0); - assert(res); - m_gone = 0; - } - res = ReleaseMutex(reinterpret_cast(m_mutex)); - assert(res); - - if (was_waiting == 1) - { - for (/**/ ; was_gone; --was_gone) - { - // better now than spurious later - res = WaitForSingleObject(reinterpret_cast(m_queue), - INFINITE); - assert(res == WAIT_OBJECT_0); - } - res = ReleaseSemaphore(reinterpret_cast(m_gate), 1, 0); - assert(res); - } -} - -bool condition_impl::do_timed_wait(const xtime& xt) -{ - bool ret = false; - unsigned int res = 0; - - for (;;) - { - int milliseconds; - to_duration(xt, milliseconds); - - res = WaitForSingleObject(reinterpret_cast(m_queue), - milliseconds); - assert(res != WAIT_FAILED && res != WAIT_ABANDONED); - ret = (res == WAIT_OBJECT_0); - - if (res == WAIT_TIMEOUT) - { - xtime cur; - xtime_get(&cur, TIME_UTC); - if (xtime_cmp(xt, cur) > 0) - continue; - } - - break; - } - - unsigned was_waiting=0; - unsigned was_gone=0; - - res = WaitForSingleObject(reinterpret_cast(m_mutex), INFINITE); - assert(res == WAIT_OBJECT_0); - was_waiting = m_waiting; - was_gone = m_gone; - if (was_waiting != 0) - { - if (!ret) // timeout - { - if (m_blocked != 0) - --m_blocked; - else - ++m_gone; // count spurious wakeups - } - if (--m_waiting == 0) - { - if (m_blocked != 0) - { - res = ReleaseSemaphore(reinterpret_cast(m_gate), 1, - 0); // open m_gate - assert(res); - was_waiting = 0; - } - else if (m_gone != 0) - m_gone = 0; - } - } - else if (++m_gone == ((std::numeric_limits::max)() / 2)) - { - // timeout occured, normalize the m_gone count - // this may occur if many calls to wait with a timeout are made and - // no call to notify_* is made - res = WaitForSingleObject(reinterpret_cast(m_gate), INFINITE); - assert(res == WAIT_OBJECT_0); - m_blocked -= m_gone; - res = ReleaseSemaphore(reinterpret_cast(m_gate), 1, 0); - assert(res); - m_gone = 0; - } - res = ReleaseMutex(reinterpret_cast(m_mutex)); - assert(res); - - if (was_waiting == 1) - { - for (/**/ ; was_gone; --was_gone) - { - // better now than spurious later - res = WaitForSingleObject(reinterpret_cast(m_queue), - INFINITE); - assert(res == WAIT_OBJECT_0); - } - res = ReleaseSemaphore(reinterpret_cast(m_gate), 1, 0); - assert(res); - } - - return ret; -} -#elif defined(BOOST_HAS_PTHREADS) -condition_impl::condition_impl() -{ - int res = 0; - res = pthread_cond_init(&m_condition, 0); - if (res != 0) - throw thread_resource_error(); - res = pthread_mutex_init(&m_mutex, 0); - if (res != 0) - throw thread_resource_error(); -} - -condition_impl::~condition_impl() -{ - int res = 0; - res = pthread_cond_destroy(&m_condition); - assert(res == 0); - res = pthread_mutex_destroy(&m_mutex); - assert(res == 0); -} - -void condition_impl::notify_one() -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - res = pthread_cond_signal(&m_condition); - assert(res == 0); - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); -} - -void condition_impl::notify_all() -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - res = pthread_cond_broadcast(&m_condition); - assert(res == 0); - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); -} - -void condition_impl::do_wait(pthread_mutex_t* pmutex) -{ - int res = 0; - res = pthread_cond_wait(&m_condition, pmutex); - assert(res == 0); -} - -bool condition_impl::do_timed_wait(const xtime& xt, pthread_mutex_t* pmutex) -{ - timespec ts; - to_timespec(xt, ts); - - int res = 0; - res = pthread_cond_timedwait(&m_condition, pmutex, &ts); -// Test code for QNX debugging, to get information during regressions -#ifndef NDEBUG - if (res == EINVAL) { - boost::xtime now; - boost::xtime_get(&now, boost::TIME_UTC); - std::cerr << "now: " << now.sec << " " << now.nsec << std::endl; - std::cerr << "time: " << time(0) << std::endl; - std::cerr << "xtime: " << xt.sec << " " << xt.nsec << std::endl; - std::cerr << "ts: " << ts.tv_sec << " " << ts.tv_nsec << std::endl; - std::cerr << "pmutex: " << pmutex << std::endl; - std::cerr << "condition: " << &m_condition << std::endl; - assert(res != EINVAL); - } -#endif - assert(res == 0 || res == ETIMEDOUT); - - return res != ETIMEDOUT; -} -#elif defined(BOOST_HAS_MPTASKS) - -using threads::mac::detail::safe_enter_critical_region; -using threads::mac::detail::safe_wait_on_semaphore; - -condition_impl::condition_impl() - : m_gone(0), m_blocked(0), m_waiting(0) -{ - threads::mac::detail::thread_init(); - - OSStatus lStatus = noErr; - - lStatus = MPCreateSemaphore(1, 1, &m_gate); - if(lStatus == noErr) - lStatus = MPCreateSemaphore(ULONG_MAX, 0, &m_queue); - - if(lStatus != noErr || !m_gate || !m_queue) - { - if (m_gate) - { - lStatus = MPDeleteSemaphore(m_gate); - assert(lStatus == noErr); - } - if (m_queue) - { - lStatus = MPDeleteSemaphore(m_queue); - assert(lStatus == noErr); - } - - throw thread_resource_error(); - } -} - -condition_impl::~condition_impl() -{ - OSStatus lStatus = noErr; - lStatus = MPDeleteSemaphore(m_gate); - assert(lStatus == noErr); - lStatus = MPDeleteSemaphore(m_queue); - assert(lStatus == noErr); -} - -void condition_impl::notify_one() -{ - unsigned signals = 0; - - OSStatus lStatus = noErr; - lStatus = safe_enter_critical_region(m_mutex, kDurationForever, - m_mutex_mutex); - assert(lStatus == noErr); - - if (m_waiting != 0) // the m_gate is already closed - { - if (m_blocked == 0) - { - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); - return; - } - - ++m_waiting; - --m_blocked; - } - else - { - lStatus = safe_wait_on_semaphore(m_gate, kDurationForever); - assert(lStatus == noErr); - if (m_blocked > m_gone) - { - if (m_gone != 0) - { - m_blocked -= m_gone; - m_gone = 0; - } - signals = m_waiting = 1; - --m_blocked; - } - else - { - lStatus = MPSignalSemaphore(m_gate); - assert(lStatus == noErr); - } - - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); - - while (signals) - { - lStatus = MPSignalSemaphore(m_queue); - assert(lStatus == noErr); - --signals; - } - } -} - -void condition_impl::notify_all() -{ - unsigned signals = 0; - - OSStatus lStatus = noErr; - lStatus = safe_enter_critical_region(m_mutex, kDurationForever, - m_mutex_mutex); - assert(lStatus == noErr); - - if (m_waiting != 0) // the m_gate is already closed - { - if (m_blocked == 0) - { - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); - return; - } - - m_waiting += (signals = m_blocked); - m_blocked = 0; - } - else - { - lStatus = safe_wait_on_semaphore(m_gate, kDurationForever); - assert(lStatus == noErr); - if (m_blocked > m_gone) - { - if (m_gone != 0) - { - m_blocked -= m_gone; - m_gone = 0; - } - signals = m_waiting = m_blocked; - m_blocked = 0; - } - else - { - lStatus = MPSignalSemaphore(m_gate); - assert(lStatus == noErr); - } - - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); - - while (signals) - { - lStatus = MPSignalSemaphore(m_queue); - assert(lStatus == noErr); - --signals; - } - } -} - -void condition_impl::enter_wait() -{ - OSStatus lStatus = noErr; - lStatus = safe_wait_on_semaphore(m_gate, kDurationForever); - assert(lStatus == noErr); - ++m_blocked; - lStatus = MPSignalSemaphore(m_gate); - assert(lStatus == noErr); -} - -void condition_impl::do_wait() -{ - OSStatus lStatus = noErr; - lStatus = safe_wait_on_semaphore(m_queue, kDurationForever); - assert(lStatus == noErr); - - unsigned was_waiting=0; - unsigned was_gone=0; - - lStatus = safe_enter_critical_region(m_mutex, kDurationForever, - m_mutex_mutex); - assert(lStatus == noErr); - was_waiting = m_waiting; - was_gone = m_gone; - if (was_waiting != 0) - { - if (--m_waiting == 0) - { - if (m_blocked != 0) - { - lStatus = MPSignalSemaphore(m_gate); // open m_gate - assert(lStatus == noErr); - was_waiting = 0; - } - else if (m_gone != 0) - m_gone = 0; - } - } - else if (++m_gone == ((std::numeric_limits::max)() / 2)) - { - // timeout occured, normalize the m_gone count - // this may occur if many calls to wait with a timeout are made and - // no call to notify_* is made - lStatus = safe_wait_on_semaphore(m_gate, kDurationForever); - assert(lStatus == noErr); - m_blocked -= m_gone; - lStatus = MPSignalSemaphore(m_gate); - assert(lStatus == noErr); - m_gone = 0; - } - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); - - if (was_waiting == 1) - { - for (/**/ ; was_gone; --was_gone) - { - // better now than spurious later - lStatus = safe_wait_on_semaphore(m_queue, kDurationForever); - assert(lStatus == noErr); - } - lStatus = MPSignalSemaphore(m_gate); - assert(lStatus == noErr); - } -} - -bool condition_impl::do_timed_wait(const xtime& xt) -{ - int milliseconds; - to_duration(xt, milliseconds); - - OSStatus lStatus = noErr; - lStatus = safe_wait_on_semaphore(m_queue, milliseconds); - assert(lStatus == noErr || lStatus == kMPTimeoutErr); - - bool ret = (lStatus == noErr); - - unsigned was_waiting=0; - unsigned was_gone=0; - - lStatus = safe_enter_critical_region(m_mutex, kDurationForever, - m_mutex_mutex); - assert(lStatus == noErr); - was_waiting = m_waiting; - was_gone = m_gone; - if (was_waiting != 0) - { - if (!ret) // timeout - { - if (m_blocked != 0) - --m_blocked; - else - ++m_gone; // count spurious wakeups - } - if (--m_waiting == 0) - { - if (m_blocked != 0) - { - lStatus = MPSignalSemaphore(m_gate); // open m_gate - assert(lStatus == noErr); - was_waiting = 0; - } - else if (m_gone != 0) - m_gone = 0; - } - } - else if (++m_gone == ((std::numeric_limits::max)() / 2)) - { - // timeout occured, normalize the m_gone count - // this may occur if many calls to wait with a timeout are made and - // no call to notify_* is made - lStatus = safe_wait_on_semaphore(m_gate, kDurationForever); - assert(lStatus == noErr); - m_blocked -= m_gone; - lStatus = MPSignalSemaphore(m_gate); - assert(lStatus == noErr); - m_gone = 0; - } - lStatus = MPExitCriticalRegion(m_mutex); - assert(lStatus == noErr); - - if (was_waiting == 1) - { - for (/**/ ; was_gone; --was_gone) - { - // better now than spurious later - lStatus = safe_wait_on_semaphore(m_queue, kDurationForever); - assert(lStatus == noErr); - } - lStatus = MPSignalSemaphore(m_gate); - assert(lStatus == noErr); - } - - return ret; -} -#endif - -} // namespace detail - -} // namespace boost - -// Change Log: -// 8 Feb 01 WEKEMPF Initial version. -// 22 May 01 WEKEMPF Modified to use xtime for time outs. -// 3 Jan 03 WEKEMPF Modified for DLL implementation. From 60380afe15148e4a2ec753b1b601ae7cab5a6fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Sch=C3=B6pflin?= Date: Mon, 22 Oct 2007 07:54:08 +0000 Subject: [PATCH 017/214] Fix compilation. [SVN r40277] --- include/boost/thread/pthread/mutex.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index 21a2671f..4b9636df 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -64,7 +64,7 @@ namespace boost } typedef pthread_mutex_t* native_handle_type; - native_handle_type native_handle() const + native_handle_type native_handle() { return &m; } From a646153615131fa58b15d1d8bb81d52534f57d86 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 23 Oct 2007 08:57:17 +0000 Subject: [PATCH 018/214] platform split for pthread and win32 builds so can use pthread-win32 library on Windows with pthread feature; new C++0x-alike thread class interface on win32. [SVN r40348] --- build/Jamfile.v2 | 106 +++-- include/boost/thread/pthread/mutex.hpp | 2 + .../boost/thread/pthread/recursive_mutex.hpp | 2 + include/boost/thread/pthread/thread.hpp | 136 ++++++ include/boost/thread/thread.hpp | 95 +--- include/boost/thread/win32/thread.hpp | 413 ++++++++++++++++ .../boost/thread/win32/thread_heap_alloc.hpp | 105 +++++ .../boost/thread/win32/thread_primitives.hpp | 19 +- src/mac/debug_prefix.hpp | 8 - src/mac/delivery_man.cpp | 66 --- src/mac/delivery_man.hpp | 84 ---- src/mac/dt_scheduler.cpp | 93 ---- src/mac/dt_scheduler.hpp | 63 --- src/mac/execution_context.cpp | 60 --- src/mac/execution_context.hpp | 47 -- src/mac/init.cpp | 58 --- src/mac/init.hpp | 34 -- src/mac/msl_replacements/assert.cpp | 24 - src/mac/msl_replacements/console_io.cpp | 128 ----- src/mac/msl_replacements/malloc.cpp | 52 -- src/mac/msl_replacements/news_and_deletes.cpp | 99 ---- src/mac/msl_replacements/time.cpp | 150 ------ src/mac/os.cpp | 57 --- src/mac/os.hpp | 37 -- src/mac/ot_context.cpp | 46 -- src/mac/ot_context.hpp | 58 --- src/mac/package.hpp | 76 --- src/mac/periodical.hpp | 97 ---- src/mac/prefix.hpp | 9 - src/mac/remote_call_manager.cpp | 48 -- src/mac/remote_call_manager.hpp | 102 ---- src/mac/remote_calls.hpp | 157 ------- src/mac/safe.cpp | 210 --------- src/mac/safe.hpp | 41 -- src/mac/scoped_critical_region.cpp | 47 -- src/mac/scoped_critical_region.hpp | 63 --- src/mac/st_scheduler.cpp | 85 ---- src/mac/st_scheduler.hpp | 67 --- src/mac/thread_cleanup.cpp | 56 --- src/mac/thread_cleanup.hpp | 36 -- src/{ => pthread}/exceptions.cpp | 0 src/{ => pthread}/thread.cpp | 157 +------ src/{ => pthread}/timeconv.inl | 0 src/{ => pthread}/tss.cpp | 0 src/{ => pthread}/xtime.cpp | 0 src/win32/exceptions.cpp | 124 +++++ src/win32/thread.cpp | 443 ++++++++++++++++++ src/win32/timeconv.inl | 130 +++++ src/win32/tss.cpp | 251 ++++++++++ src/{ => win32}/tss_dll.cpp | 0 src/{ => win32}/tss_hooks.cpp | 0 src/{ => win32}/tss_pe.cpp | 0 src/win32/xtime.cpp | 158 +++++++ test/test_shared_mutex.cpp | 34 +- test/test_thread.cpp | 26 +- 55 files changed, 1911 insertions(+), 2548 deletions(-) create mode 100644 include/boost/thread/pthread/thread.hpp create mode 100644 include/boost/thread/win32/thread.hpp create mode 100644 include/boost/thread/win32/thread_heap_alloc.hpp delete mode 100644 src/mac/debug_prefix.hpp delete mode 100644 src/mac/delivery_man.cpp delete mode 100644 src/mac/delivery_man.hpp delete mode 100644 src/mac/dt_scheduler.cpp delete mode 100644 src/mac/dt_scheduler.hpp delete mode 100644 src/mac/execution_context.cpp delete mode 100644 src/mac/execution_context.hpp delete mode 100644 src/mac/init.cpp delete mode 100644 src/mac/init.hpp delete mode 100644 src/mac/msl_replacements/assert.cpp delete mode 100644 src/mac/msl_replacements/console_io.cpp delete mode 100644 src/mac/msl_replacements/malloc.cpp delete mode 100644 src/mac/msl_replacements/news_and_deletes.cpp delete mode 100644 src/mac/msl_replacements/time.cpp delete mode 100644 src/mac/os.cpp delete mode 100644 src/mac/os.hpp delete mode 100644 src/mac/ot_context.cpp delete mode 100644 src/mac/ot_context.hpp delete mode 100644 src/mac/package.hpp delete mode 100644 src/mac/periodical.hpp delete mode 100644 src/mac/prefix.hpp delete mode 100644 src/mac/remote_call_manager.cpp delete mode 100644 src/mac/remote_call_manager.hpp delete mode 100644 src/mac/remote_calls.hpp delete mode 100644 src/mac/safe.cpp delete mode 100644 src/mac/safe.hpp delete mode 100644 src/mac/scoped_critical_region.cpp delete mode 100644 src/mac/scoped_critical_region.hpp delete mode 100644 src/mac/st_scheduler.cpp delete mode 100644 src/mac/st_scheduler.hpp delete mode 100644 src/mac/thread_cleanup.cpp delete mode 100644 src/mac/thread_cleanup.hpp rename src/{ => pthread}/exceptions.cpp (100%) rename src/{ => pthread}/thread.cpp (52%) rename src/{ => pthread}/timeconv.inl (100%) rename src/{ => pthread}/tss.cpp (100%) rename src/{ => pthread}/xtime.cpp (100%) create mode 100644 src/win32/exceptions.cpp create mode 100644 src/win32/thread.cpp create mode 100644 src/win32/timeconv.inl create mode 100644 src/win32/tss.cpp rename src/{ => win32}/tss_dll.cpp (100%) rename src/{ => win32}/tss_hooks.cpp (100%) rename src/{ => win32}/tss_pe.cpp (100%) create mode 100644 src/win32/xtime.cpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 560161b5..b9a4f393 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -1,39 +1,83 @@ -# (C) Copyright Vladimir Prus, David Abrahams, Michael Stevens, Hartmut Kaiser, -# William E Kempf 2002-2006 -# Use, modification and distribution are subject to 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) +# Copyright 2006 Roland Schwarz. +# Copyright 2007 Anthony Williams +# 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) import os ; - +import feature ; project boost/thread - : source-location ../src - : requirements shared:BOOST_THREAD_BUILD_DLL=1 multi - : default-build multi + : source-location ../src + : requirements multi + static:BOOST_THREAD_BUILD_LIB=1 + shared:BOOST_THREAD_BUILD_DLL=1 + : default-build multi ; -CPP_SOURCES = -# barrier -# condition - exceptions -# mutex -# once -# recursive_mutex -# read_write_mutex - thread - tss_hooks - tss_dll - tss_pe - tss - xtime - ; +BOOST_PTHREAD_OPTS = BOOST_THREAD_POSIX ; +if [ os.name ] = "NT" +{ + AVAILABLE_THREAD_APIS = win32 ; + + local PTW32_INCLUDE = [ modules.peek : PTW32_INCLUDE ] ; + local PTW32_LIB = [ modules.peek : PTW32_LIB ] ; + if $(PTW32_INCLUDE) && $(PTW32_LIB) + { + BOOST_PTHREAD_OPTS += + BOOST_HAS_PTHREADS + $(PTW32_INCLUDE) + $(PTW32_LIB) ; + + AVAILABLE_THREAD_APIS += pthread ; + } + else + { + echo "******************************************************" ; + echo "Building Boost.Thread without optional pthread support" ; + echo "If you need pthread you should specify the paths." ; + echo "For example:" ; + echo "PTW32_INCLUDE=C:\\Program Files\\ptw32\\Pre-built2\\include" ; + echo "PTW32_LIB=C:\\Program Files\\ptw32\\Pre-built2\\lib\\pthreadVC2.lib" ; + echo "******************************************************" ; + BOOST_PTHREAD_OPTS += no ; + } +} +else +{ + AVAILABLE_THREAD_APIS = pthread ; + if [ os.name ] = CYGWIN + { + AVAILABLE_THREAD_APIS += win32 ; + } +} + +feature.feature thrd-api : $(AVAILABLE_THREAD_APIS) : symmetric propagated composite ; +feature.compose pthread : $(BOOST_PTHREAD_OPTS) ; + +lib boost_thread + : ## sources ## + win32/thread.cpp + win32/exceptions.cpp + win32/xtime.cpp + win32/tss.cpp + win32/tss_hooks.cpp + win32/tss_dll.cpp + win32/tss_pe.cpp + : ## requirements ## + win32 + ; + +# build the pthread based variant lib boost_thread - : $(CPP_SOURCES).cpp - : shared:BOOST_THREAD_BUILD_DLL=1 - static:BOOST_THREAD_BUILD_LIB=1 - : # default build - : shared:BOOST_THREAD_BUILD_DLL=1 - static:BOOST_THREAD_BUILD_LIB=1 - ; + : ## sources ## + pthread/thread.cpp + pthread/exceptions.cpp + pthread/xtime.cpp + pthread/tss.cpp + : ## requirements ## + pthread + $(BOOST_PTHREAD_OPTS) + ; + diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index 4b9636df..7483686d 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -11,7 +11,9 @@ #include #include #include +#ifndef WIN32 #include +#endif #include #include "timespec.hpp" #include "pthread_mutex_scoped_lock.hpp" diff --git a/include/boost/thread/pthread/recursive_mutex.hpp b/include/boost/thread/pthread/recursive_mutex.hpp index 21395b4d..f7ea9300 100644 --- a/include/boost/thread/pthread/recursive_mutex.hpp +++ b/include/boost/thread/pthread/recursive_mutex.hpp @@ -11,7 +11,9 @@ #include #include #include +#ifndef WIN32 #include +#endif #include #include #include "timespec.hpp" diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp new file mode 100644 index 00000000..622ef027 --- /dev/null +++ b/include/boost/thread/pthread/thread.hpp @@ -0,0 +1,136 @@ +// Copyright (C) 2001-2003 +// William E. Kempf +// Copyright (C) 2007 Anthony Williams +// +// 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) + +#ifndef BOOST_THREAD_WEK070601_HPP +#define BOOST_THREAD_WEK070601_HPP + +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace boost { + + class thread; + + namespace detail + { + class thread_id; + } + + namespace this_thread + { + detail::thread_id get_id(); + } + + namespace detail + { + class thread_id + { + boost::optional id; + + friend class boost::thread; + + friend thread_id this_thread::get_id(); + + thread_id(pthread_t id_): + id(id_) + {} + + public: + thread_id() + {} + + bool operator==(const thread_id& y) const + { + return (id && y.id) && (pthread_equal(*id,*y.id)!=0); + } + + bool operator!=(const thread_id& y) const + { + return !(*this==y); + } + + template + friend std::basic_ostream& + operator<<(std::basic_ostream& os, const thread_id& x) + { + if(x.id) + { + return os<<*x.id; + } + else + { + return os<<"{Not-any-thread}"; + } + } + + }; + } + + struct xtime; + class BOOST_THREAD_DECL thread : private noncopyable + { + public: + thread(); + explicit thread(const function0& threadfunc); + ~thread(); + + bool operator==(const thread& other) const; + bool operator!=(const thread& other) const; + + void join(); + + static void sleep(const xtime& xt); + static void yield(); + + typedef detail::thread_id id; + + id get_id() const + { + return m_id; + } + + private: + id m_id; + bool m_joinable; + }; + + namespace this_thread + { + inline thread::id get_id() + { + return thread::id(pthread_self()); + } + } + + class BOOST_THREAD_DECL thread_group : private noncopyable + { + public: + thread_group(); + ~thread_group(); + + thread* create_thread(const function0& threadfunc); + void add_thread(thread* thrd); + void remove_thread(thread* thrd); + void join_all(); + int size() const; + + private: + std::list m_threads; + mutex m_mutex; + }; +} // namespace boost + + +#endif // BOOST_THREAD_WEK070601_HPP diff --git a/include/boost/thread/thread.hpp b/include/boost/thread/thread.hpp index 0f410eb9..da4323f0 100644 --- a/include/boost/thread/thread.hpp +++ b/include/boost/thread/thread.hpp @@ -1,88 +1,15 @@ -// Copyright (C) 2001-2003 -// William E. Kempf +#ifndef BOOST_THREAD_THREAD_HPP +#define BOOST_THREAD_THREAD_HPP + +// thread.hpp // -// 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) +// (C) Copyright 2007 Anthony Williams +// +// 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) -#ifndef BOOST_THREAD_WEK070601_HPP -#define BOOST_THREAD_WEK070601_HPP +#include +#include BOOST_THREAD_PLATFORM(thread.hpp) -#include - -#include -#include -#include -#include -#include - -#if defined(BOOST_HAS_PTHREADS) -# include -#elif defined(BOOST_HAS_MPTASKS) -# include #endif - -namespace boost { - -struct xtime; -// disable warnings about non dll import -// see: http://www.boost.org/more/separate_compilation.html#dlls -#ifdef BOOST_MSVC -# pragma warning(push) -# pragma warning(disable: 4251 4231 4660 4275) -#endif -class BOOST_THREAD_DECL thread : private noncopyable -{ -public: - thread(); - explicit thread(const function0& threadfunc); - ~thread(); - - bool operator==(const thread& other) const; - bool operator!=(const thread& other) const; - - void join(); - - static void sleep(const xtime& xt); - static void yield(); - -private: -#if defined(BOOST_HAS_WINTHREADS) - void* m_thread; - unsigned int m_id; -#elif defined(BOOST_HAS_PTHREADS) -private: - pthread_t m_thread; -#elif defined(BOOST_HAS_MPTASKS) - MPQueueID m_pJoinQueueID; - MPTaskID m_pTaskID; -#endif - bool m_joinable; -}; - -class BOOST_THREAD_DECL thread_group : private noncopyable -{ -public: - thread_group(); - ~thread_group(); - - thread* create_thread(const function0& threadfunc); - void add_thread(thread* thrd); - void remove_thread(thread* thrd); - void join_all(); - int size() const; - -private: - std::list m_threads; - mutex m_mutex; -}; -#ifdef BOOST_MSVC -# pragma warning(pop) -#endif -} // namespace boost - -// Change Log: -// 8 Feb 01 WEKEMPF Initial version. -// 1 Jun 01 WEKEMPF Added boost::thread initial implementation. -// 3 Jul 01 WEKEMPF Redesigned boost::thread to be noncopyable. - -#endif // BOOST_THREAD_WEK070601_HPP diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp new file mode 100644 index 00000000..c5b99ced --- /dev/null +++ b/include/boost/thread/win32/thread.hpp @@ -0,0 +1,413 @@ +#ifndef BOOST_THREAD_THREAD_WIN32_HPP +#define BOOST_THREAD_THREAD_WIN32_HPP +// 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) +// (C) Copyright 2007 Anthony Williams + +#include +#include +#include +#include +#include +#include +#include +#include "thread_primitives.hpp" +#include "thread_heap_alloc.hpp" +#include +#include +#include + +namespace boost +{ + class thread_canceled + {}; + + namespace detail + { + struct thread_exit_callback_node; + + struct thread_data_base + { + long count; + detail::win32::handle_manager thread_handle; + detail::win32::handle_manager cancel_handle; + boost::detail::thread_exit_callback_node* thread_exit_callbacks; + bool cancel_enabled; + unsigned id; + + thread_data_base(): + count(0),thread_handle(detail::win32::invalid_handle_value), + cancel_handle(create_anonymous_event(detail::win32::manual_reset_event,detail::win32::event_initially_reset)), + thread_exit_callbacks(0), + cancel_enabled(true), + id(0) + {} + virtual ~thread_data_base() + {} + + friend void intrusive_ptr_add_ref(thread_data_base * p) + { + BOOST_INTERLOCKED_INCREMENT(&p->count); + } + + friend void intrusive_ptr_release(thread_data_base * p) + { + if(!BOOST_INTERLOCKED_DECREMENT(&p->count)) + { + detail::heap_delete(p); + } + } + + virtual void run()=0; + }; + } + + class BOOST_THREAD_DECL thread + { + private: + thread(thread&); + thread& operator=(thread&); + + void release_handle(); + + template + struct thread_data: + detail::thread_data_base + { + F f; + + thread_data(F f_): + f(f_) + {} + thread_data(boost::move_t f_): + f(f_) + {} + + void run() + { + f(); + } + }; + + mutable boost::mutex thread_info_mutex; + boost::intrusive_ptr thread_info; + + static unsigned __stdcall thread_start_function(void* param); + + void start_thread(); + + explicit thread(boost::intrusive_ptr data); + + boost::intrusive_ptr get_thread_info() const; + public: + thread(); + ~thread(); + + template + thread(F f): + thread_info(detail::heap_new >(f)) + { + start_thread(); + } + template + thread(boost::move_t f): + thread_info(detail::heap_new >(f)) + { + start_thread(); + } + + thread(boost::move_t x); + thread& operator=(boost::move_t x); + operator boost::move_t(); + boost::move_t move(); + + void swap(thread& x); + + class id; + id get_id() const; + + + bool joinable() const; + void join(); + void detach(); + + static unsigned hardware_concurrency(); + + typedef detail::win32::handle native_handle_type; + native_handle_type native_handle(); + + // backwards compatibility + bool operator==(const thread& other) const; + bool operator!=(const thread& other) const; + + static void yield(); + static void sleep(const system_time& xt); + + // extensions + class cancel_handle; + cancel_handle get_cancel_handle() const; + void cancel(); + bool cancellation_requested() const; + + static thread self(); + }; + + namespace this_thread + { + class disable_cancellation + { + disable_cancellation(const disable_cancellation&); + disable_cancellation& operator=(const disable_cancellation&); + + bool cancel_was_enabled; + friend class enable_cancellation; + public: + disable_cancellation(); + ~disable_cancellation(); + }; + + class enable_cancellation + { + enable_cancellation(const enable_cancellation&); + enable_cancellation& operator=(const enable_cancellation&); + public: + explicit enable_cancellation(disable_cancellation& d); + ~enable_cancellation(); + }; + + thread::id BOOST_THREAD_DECL get_id(); + + bool BOOST_THREAD_DECL cancellable_wait(detail::win32::handle handle_to_wait_for,unsigned long milliseconds); + + void BOOST_THREAD_DECL cancellation_point(); + bool BOOST_THREAD_DECL cancellation_enabled(); + bool BOOST_THREAD_DECL cancellation_requested(); + thread::cancel_handle BOOST_THREAD_DECL get_cancel_handle(); + + void BOOST_THREAD_DECL yield(); + template + void sleep(TimeDuration const& rel_time) + { + cancellable_wait(detail::win32::invalid_handle_value,static_cast(rel_time.total_milliseconds())); + } + } + + class thread::id + { + private: + unsigned thread_id; + + id(unsigned thread_id_): + thread_id(thread_id_) + {} + friend class thread; + friend id this_thread::get_id(); + public: + id(): + thread_id(0) + {} + + bool operator==(const id& y) const + { + return thread_id==y.thread_id; + } + + bool operator!=(const id& y) const + { + return thread_id!=y.thread_id; + } + + bool operator<(const id& y) const + { + return thread_id(const id& y) const + { + return thread_id>y.thread_id; + } + + bool operator<=(const id& y) const + { + return thread_id<=y.thread_id; + } + + bool operator>=(const id& y) const + { + return thread_id>=y.thread_id; + } + + template + friend std::basic_ostream& + operator<<(std::basic_ostream& os, const id& x) + { + return os< + struct thread_exit_function: + thread_exit_function_base + { + F f; + + thread_exit_function(F f_): + f(f_) + {} + + void operator()() const + { + f(); + } + }; + + void add_thread_exit_function(thread_exit_function_base*); + } + + namespace this_thread + { + template + void at_thread_exit(F f) + { + detail::thread_exit_function_base* const thread_exit_func=detail::heap_new >(f); + detail::add_thread_exit_function(thread_exit_func); + } + } + + class thread_group: + private noncopyable + { + public: + ~thread_group() + { + for(std::list::iterator it=threads.begin(),end=threads.end(); + it!=end; + ++it) + { + delete *it; + } + } + + template + thread* create_thread(F threadfunc) + { + boost::lock_guard guard(m); + thread* const new_thread=new thread(threadfunc); + threads.push_back(new_thread); + return new_thread; + } + + void add_thread(thread* thrd) + { + if(thrd) + { + boost::lock_guard guard(m); + threads.push_back(thrd); + } + } + + void remove_thread(thread* thrd) + { + boost::lock_guard guard(m); + std::list::iterator const it=std::find(threads.begin(),threads.end(),thrd); + if(it!=threads.end()) + { + threads.erase(it); + } + } + + void join_all() + { + boost::lock_guard guard(m); + + for(std::list::iterator it=threads.begin(),end=threads.end(); + it!=end; + ++it) + { + (*it)->join(); + } + } + + int size() const + { + boost::lock_guard guard(m); + return threads.size(); + } + + private: + std::list threads; + mutable mutex m; + }; +} + +#endif diff --git a/include/boost/thread/win32/thread_heap_alloc.hpp b/include/boost/thread/win32/thread_heap_alloc.hpp new file mode 100644 index 00000000..abf6e9ca --- /dev/null +++ b/include/boost/thread/win32/thread_heap_alloc.hpp @@ -0,0 +1,105 @@ +// 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) +// (C) Copyright 2007 Anthony Williams +#ifndef THREAD_HEAP_ALLOC_HPP +#define THREAD_HEAP_ALLOC_HPP +#include +#include "thread_primitives.hpp" + +#if defined( BOOST_USE_WINDOWS_H ) +# include + +namespace boost +{ + namespace detail + { + namespace win32 + { + using ::GetProcessHeap; + using ::HeapAlloc; + using ::HeapFree; + } + } +} + +#else + +namespace boost +{ + namespace detail + { + namespace win32 + { + extern "C" + { + __declspec(dllimport) handle __stdcall GetProcessHeap(); + __declspec(dllimport) void* __stdcall HeapAlloc(handle,unsigned long,ulong_ptr); + __declspec(dllimport) int __stdcall HeapFree(handle,unsigned long,void*); + } + } + } +} + +#endif + +namespace boost +{ + namespace detail + { + template + T* heap_new() + { + void* const heap_memory=detail::win32::HeapAlloc(detail::win32::GetProcessHeap(),0,sizeof(T)); + T* const data=new (heap_memory) T(); + return data; + } + + template + T* heap_new(A1 a1) + { + void* const heap_memory=detail::win32::HeapAlloc(detail::win32::GetProcessHeap(),0,sizeof(T)); + T* const data=new (heap_memory) T(a1); + return data; + } + + template + T* heap_new(A1 a1,A2 a2) + { + void* const heap_memory=detail::win32::HeapAlloc(detail::win32::GetProcessHeap(),0,sizeof(T)); + T* const data=new (heap_memory) T(a1,a2); + return data; + } + + template + void heap_delete(T* data) + { + data->~T(); + detail::win32::HeapFree(detail::win32::GetProcessHeap(),0,data); + } + + template + struct do_delete + { + T* data; + + do_delete(T* data_): + data(data_) + {} + + void operator()() const + { + detail::heap_delete(data); + } + }; + + template + do_delete make_heap_deleter(T* data) + { + return do_delete(data); + } + } +} + + +#endif diff --git a/include/boost/thread/win32/thread_primitives.hpp b/include/boost/thread/win32/thread_primitives.hpp index 604263e1..f8d1854e 100644 --- a/include/boost/thread/win32/thread_primitives.hpp +++ b/include/boost/thread/win32/thread_primitives.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #if defined( BOOST_USE_WINDOWS_H ) # include @@ -25,6 +26,7 @@ namespace boost typedef HANDLE handle; unsigned const infinite=INFINITE; unsigned const timeout=WAIT_TIMEOUT; + handle const invalid_handle_value=INVALID_HANDLE_VALUE; using ::CreateMutexA; using ::CreateEventA; @@ -42,6 +44,7 @@ namespace boost using ::GetCurrentProcess; using ::DuplicateHandle; using ::SleepEx; + using ::Sleep; using ::QueueUserAPC; } } @@ -62,6 +65,7 @@ namespace boost typedef void* handle; unsigned const infinite=~0U; unsigned const timeout=258U; + handle const invalid_handle_value=(handle)(-1); extern "C" { @@ -79,6 +83,7 @@ namespace boost __declspec(dllimport) void* __stdcall GetCurrentProcess(); __declspec(dllimport) int __stdcall DuplicateHandle(void*,void*,void*,void**,unsigned long,int,unsigned long); __declspec(dllimport) unsigned long __stdcall SleepEx(unsigned long,int); + __declspec(dllimport) void __stdcall Sleep(unsigned long); typedef void (__stdcall *queue_user_apc_callback_function)(ulong_ptr); __declspec(dllimport) unsigned long __stdcall QueueUserAPC(queue_user_apc_callback_function,void*,ulong_ptr); __declspec(dllimport) int __stdcall SetEvent(void*); @@ -158,9 +163,9 @@ namespace boost void cleanup() { - if(handle_to_manage) + if(handle_to_manage && handle_to_manage!=invalid_handle_value) { - unsigned long result=CloseHandle(handle_to_manage); + unsigned long const result=CloseHandle(handle_to_manage); BOOST_ASSERT(result); } } @@ -185,6 +190,16 @@ namespace boost return handle_to_manage; } + handle duplicate() const + { + return duplicate_handle(handle_to_manage); + } + + void swap(handle_manager& other) + { + std::swap(handle_to_manage,other.handle_to_manage); + } + handle release() { handle const res=handle_to_manage; diff --git a/src/mac/debug_prefix.hpp b/src/mac/debug_prefix.hpp deleted file mode 100644 index 08202289..00000000 --- a/src/mac/debug_prefix.hpp +++ /dev/null @@ -1,8 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#define TARGET_CARBON 1 diff --git a/src/mac/delivery_man.cpp b/src/mac/delivery_man.cpp deleted file mode 100644 index a1ab0381..00000000 --- a/src/mac/delivery_man.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#include "delivery_man.hpp" - -#include "os.hpp" -#include "execution_context.hpp" - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -delivery_man::delivery_man(): - m_pPackage(NULL), - m_pSemaphore(kInvalidID), - m_bPackageWaiting(false) -{ - assert(at_st()); - - OSStatus lStatus = MPCreateSemaphore(1UL, 0UL, &m_pSemaphore); -// TODO - throw on error here - assert(lStatus == noErr); -} - -delivery_man::~delivery_man() -{ - assert(m_bPackageWaiting == false); - - OSStatus lStatus = MPDeleteSemaphore(m_pSemaphore); - assert(lStatus == noErr); -} - - -void delivery_man::accept_deliveries() -{ - if(m_bPackageWaiting) - { - assert(m_pPackage != NULL); - m_pPackage->accept(); - m_pPackage = NULL; - m_bPackageWaiting = false; - - // signal to the thread making the call that we're done - OSStatus lStatus = MPSignalSemaphore(m_pSemaphore); - assert(lStatus == noErr); - } -} - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost diff --git a/src/mac/delivery_man.hpp b/src/mac/delivery_man.hpp deleted file mode 100644 index c03173ed..00000000 --- a/src/mac/delivery_man.hpp +++ /dev/null @@ -1,84 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#ifndef BOOST_DELIVERY_MAN_MJM012402_HPP -#define BOOST_DELIVERY_MAN_MJM012402_HPP - - -#include -#include - -#include - -#include "package.hpp" - -#include - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -// class delivery_man is intended to move boost::function objects from MP tasks to -// other execution contexts (such as deferred task time or system task time). - -class delivery_man: private noncopyable -{ - public: - delivery_man(); - ~delivery_man(); - - public: - template - R deliver(function &rFunctor); - - void accept_deliveries(); - - private: - base_package *m_pPackage; - mutex m_oMutex; - MPSemaphoreID m_pSemaphore; - bool m_bPackageWaiting; -}; - - -template -R delivery_man::deliver(function &rFunctor) -{ - assert(at_mp()); - -// lock our mutex - mutex::scoped_lock oLock(m_oMutex); - -// create a package and save it - package oPackage(rFunctor); - m_pPackage = &oPackage; - m_bPackageWaiting = true; - -// wait on the semaphore - OSStatus lStatus = MPWaitOnSemaphore(m_pSemaphore, kDurationForever); - assert(lStatus == noErr); - - return(oPackage.return_value()); -} - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost - - -#endif // BOOST_DELIVERY_MAN_MJM012402_HPP diff --git a/src/mac/dt_scheduler.cpp b/src/mac/dt_scheduler.cpp deleted file mode 100644 index 9c626a95..00000000 --- a/src/mac/dt_scheduler.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#include "dt_scheduler.hpp" - -#include "ot_context.hpp" - - -#include - -#include - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -const OTTimeout k_ulTimerTaskDelay = 1UL; - - -dt_scheduler::dt_scheduler(): - m_bReschedule(false), - m_uppTask(NULL), - m_lTask(0UL) -{ - using ::boost::detail::thread::singleton; - - ot_context &rContext(singleton::instance()); - - m_uppTask = NewOTProcessUPP(task_entry); - m_lTask = OTCreateTimerTaskInContext(m_uppTask, this, rContext.get_context()); -} - -dt_scheduler::~dt_scheduler() -{ - OTDestroyTimerTask(m_lTask); - m_lTask = 0UL; - DisposeOTProcessUPP(m_uppTask); - m_uppTask = NULL; -} - - -void dt_scheduler::start_polling() -{ - m_bReschedule = true; - schedule_task(); -} - -void dt_scheduler::stop_polling() -{ - m_bReschedule = false; -} - - -void dt_scheduler::schedule_task() -{ - if(m_bReschedule) - { - OTScheduleTimerTask(m_lTask, k_ulTimerTaskDelay); - } -} - - -/*static*/ pascal void dt_scheduler::task_entry(void *pRefCon) -{ - dt_scheduler *pThis = reinterpret_cast(pRefCon); - assert(pThis != NULL); - pThis->task(); -} - -void dt_scheduler::task() -{ - periodic_function(); - schedule_task(); -} - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost diff --git a/src/mac/dt_scheduler.hpp b/src/mac/dt_scheduler.hpp deleted file mode 100644 index 6d91b0be..00000000 --- a/src/mac/dt_scheduler.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#ifndef BOOST_DT_SCHEDULER_MJM012402_HPP -#define BOOST_DT_SCHEDULER_MJM012402_HPP - - -#include "periodical.hpp" - -#include - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -// class dt_scheduler calls its pure-virtual periodic_function method periodically at -// deferred task time. This is generally 1kHz under Mac OS 9. - -class dt_scheduler -{ - public: - dt_scheduler(); - virtual ~dt_scheduler(); - - protected: - void start_polling(); - void stop_polling(); - - private: - virtual void periodic_function() = 0; - - private: - void schedule_task(); - static pascal void task_entry(void *pRefCon); - void task(); - - private: - bool m_bReschedule; - OTProcessUPP m_uppTask; - long m_lTask; -}; - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost - - -#endif // BOOST_DT_SCHEDULER_MJM012402_HPP diff --git a/src/mac/execution_context.cpp b/src/mac/execution_context.cpp deleted file mode 100644 index 5bc81472..00000000 --- a/src/mac/execution_context.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#include -#include - -#include "execution_context.hpp" -#include "init.hpp" - - -namespace boost { - -namespace threads { - -namespace mac { - - -execution_context_t execution_context() -{ -// make sure that MP services are available the first time through - static bool bIgnored = detail::thread_init(); - -// first check if we're an MP task - if(MPTaskIsPreemptive(kInvalidID)) - { - return(k_eExecutionContextMPTask); - } - -#if TARGET_CARBON -// Carbon has TaskLevel - UInt32 ulLevel = TaskLevel(); - - if(ulLevel == 0UL) - { - return(k_eExecutionContextSystemTask); - } - - if(ulLevel & kInDeferredTaskMask) - { - return(k_eExecutionContextDeferredTask); - } - - return(k_eExecutionContextOther); -#else -// this can be implemented using TaskLevel if you don't mind linking against -// DebugLib (and therefore breaking Mac OS 8.6 support), or CurrentExecutionLevel. -# error execution_context unimplimented -#endif -} - - -} // namespace mac - -} // namespace threads - -} // namespace boost diff --git a/src/mac/execution_context.hpp b/src/mac/execution_context.hpp deleted file mode 100644 index 162a2a75..00000000 --- a/src/mac/execution_context.hpp +++ /dev/null @@ -1,47 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#ifndef BOOST_EXECUTION_CONTEXT_MJM012402_HPP -#define BOOST_EXECUTION_CONTEXT_MJM012402_HPP - -namespace boost { - -namespace threads { - -namespace mac { - - -// utility functions for figuring out what context your code is executing in. -// Bear in mind that at_mp and in_blue are the only functions guarenteed by -// Apple to work. There is simply no way of being sure that you will not get -// false readings about task level at interrupt time in blue. - -typedef enum { - k_eExecutionContextSystemTask, - k_eExecutionContextDeferredTask, - k_eExecutionContextMPTask, - k_eExecutionContextOther -} execution_context_t; - -execution_context_t execution_context(); - -inline bool at_st() - { return(execution_context() == k_eExecutionContextSystemTask); } - -inline bool at_mp() - { return(execution_context() == k_eExecutionContextMPTask); } -inline bool in_blue() - { return(!at_mp()); } - - -} // namespace mac - -} // namespace threads - -} // namespace boost - -#endif // BOOST_EXECUTION_CONTEXT_MJM012402_HPP diff --git a/src/mac/init.cpp b/src/mac/init.cpp deleted file mode 100644 index 82006ae3..00000000 --- a/src/mac/init.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#include "init.hpp" - -#include "remote_call_manager.hpp" - - -#include - -#include - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -namespace { - -// force these to get called by the end of static initialization time. -static bool g_bInitialized = (thread_init() && create_singletons()); - -} - - -bool thread_init() -{ - static bool bResult = MPLibraryIsLoaded(); - - return(bResult); -} - -bool create_singletons() -{ - using ::boost::detail::thread::singleton; - - singleton::instance(); - - return(true); -} - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost diff --git a/src/mac/init.hpp b/src/mac/init.hpp deleted file mode 100644 index 77a3211e..00000000 --- a/src/mac/init.hpp +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#ifndef BOOST_INIT_MJM012402_HPP -#define BOOST_INIT_MJM012402_HPP - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -bool thread_init(); -bool create_singletons(); - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost - - -#endif // BOOST_INIT_MJM012402_HPP diff --git a/src/mac/msl_replacements/assert.cpp b/src/mac/msl_replacements/assert.cpp deleted file mode 100644 index d34b4098..00000000 --- a/src/mac/msl_replacements/assert.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#include -#include - -#include - -#include "remote_calls.hpp" - -// this function will be called when an assertion fails. We redirect the assertion -// to DebugStr (MacsBug under Mac OS 1.x-9.x, Console under Mac OS X). -void __assertion_failed(char const *pszAssertion, char const *pszFile, int nLine) -{ - using std::snprintf; - unsigned char strlDebug[sizeof(Str255) + 1]; - char *pszDebug = reinterpret_cast(&strlDebug[1]); - strlDebug[0] = snprintf(pszDebug, sizeof(Str255), "assertion failed: \"%s\", %s, line %d", pszAssertion, pszFile, nLine); - boost::threads::mac::dt_remote_call(DebugStr, static_cast(strlDebug)); -} diff --git a/src/mac/msl_replacements/console_io.cpp b/src/mac/msl_replacements/console_io.cpp deleted file mode 100644 index 7ac82207..00000000 --- a/src/mac/msl_replacements/console_io.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -// -// includes -// - -#include -#include -#include -#include -#include - -#include "remote_calls.hpp" - - -// -// using declarations -// - -using std::__file_handle; -using std::__idle_proc; -using std::__io_error; -using std::__no_io_error; -using std::size_t; - -using boost::threads::mac::st_remote_call; - - -// -// prototypes -// - -static bool check_console(); -static int do_read_console(__file_handle ulHandle, unsigned char *pBuffer, size_t *pCount, __idle_proc pfnIdleProc); -static int do_write_console(__file_handle ulHandle, unsigned char *pBuffer, size_t *pCount, __idle_proc pfnIdleProc); - - -// -// MSL function replacements -// - -// these two functions are called by cin and cout, respectively, as well as by (all?) -// other functions in MSL that do console I/O. All that they do is as the remote -// call manager to ensure that their guts are called at system task time. -int __read_console(__file_handle handle, unsigned char * buffer, size_t * count, __idle_proc idle_proc) -{ - return(st_remote_call(do_read_console, handle, buffer, count, idle_proc)); -} - -int __write_console(__file_handle handle, unsigned char * buffer, size_t * count, __idle_proc idle_proc) -{ - return(st_remote_call(do_write_console, handle, buffer, count, idle_proc)); -} - - -// -// implementations -// - -static bool check_console() -{ - static bool s_bHaveConsole(false); - static bool s_bWontHaveConsole(false); - - if(s_bHaveConsole) - { - return(true); - } - - if(s_bWontHaveConsole == false) - { - __stdio_atexit(); - - if(InstallConsole(0) != 0) - { - s_bWontHaveConsole = true; - return(false); - } - __console_exit = RemoveConsole; - s_bHaveConsole = true; - return(true); - } - - return(false); -} - - -int do_read_console(__file_handle /*ulHandle*/, unsigned char *pBuffer, size_t *pCount, __idle_proc /*pfnIdleProc*/) -{ - assert(pCount != NULL); - assert(pBuffer != NULL || *pCount == 0UL); - - if(check_console() == false) - { - return(__io_error); - } - std::fflush(stdout); - long lCount = ReadCharsFromConsole(reinterpret_cast(pBuffer), static_cast(*pCount)); - *pCount = static_cast(lCount); - if(lCount == -1L) - { - return(__io_error); - } - - return(__no_io_error); -} - -int do_write_console(__file_handle /*ulHandle*/, unsigned char *pBuffer, size_t *pCount, __idle_proc /*pfnIdleProc*/) -{ - if(check_console() == false) - { - return(__io_error); - } - - long lCount = WriteCharsToConsole(reinterpret_cast(pBuffer), static_cast(*pCount)); - *pCount = static_cast(lCount); - if(lCount == -1L) - { - return(__io_error); - } - - return(__no_io_error); -} diff --git a/src/mac/msl_replacements/malloc.cpp b/src/mac/msl_replacements/malloc.cpp deleted file mode 100644 index da6d258a..00000000 --- a/src/mac/msl_replacements/malloc.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -// -// includes -// - -#include - -#include - - -// -// using declarations -// - -using std::size_t; - - -extern "C" { - -// -// prototypes -// - -void *malloc(size_t ulSize); -void free(void *pBlock); - -} - - -// -// MSL function replacements -// - -// all allocation/deallocation currently goes through MPAllocateAligned/MPFree. This -// solution is sub-optimal at best, but will have to do for now. -void *malloc(size_t ulSize) -{ - static bool bIgnored = MPLibraryIsLoaded(); - return(MPAllocateAligned(ulSize, kMPAllocateDefaultAligned, 0UL)); -} - -void free(void *pBlock) -{ - if(pBlock == NULL) return; - MPFree(pBlock); -} diff --git a/src/mac/msl_replacements/news_and_deletes.cpp b/src/mac/msl_replacements/news_and_deletes.cpp deleted file mode 100644 index ba14cd1e..00000000 --- a/src/mac/msl_replacements/news_and_deletes.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -// -// includes -// - -#include - -#include - - -// -// using declarations -// - -using std::size_t; -using std::bad_alloc; -using std::nothrow_t; -using std::nothrow; - - -// -// local utility functions -// - -// all allocation/deallocation currently goes through MPAllocateAligned/MPFree. This -// solution is sub-optimal at best, but will have to do for now. -inline static void *allocate(size_t ulSize, const nothrow_t &) -{ - static bool bIgnored = MPLibraryIsLoaded(); - return(MPAllocateAligned(ulSize, kMPAllocateDefaultAligned, 0UL)); -} - -inline static void *allocate(size_t ulSize) -{ - void *pBlock = allocate(ulSize, nothrow); - if(pBlock == NULL) - throw(bad_alloc()); - return(pBlock); -} - -inline static void deallocate(void *pBlock) -{ - if(pBlock == NULL) return; - MPFree(pBlock); -} - - -// -// global operators -// - -void *operator new(size_t ulSize) -{ - return(allocate(ulSize)); -} - -void *operator new[](size_t ulSize) -{ - return(allocate(ulSize)); -} - - -void *operator new(size_t ulSize, const nothrow_t &rNoThrow) -{ - return(allocate(ulSize, rNoThrow)); -} - -void *operator new[](size_t ulSize, const nothrow_t &rNoThrow) -{ - return(allocate(ulSize, rNoThrow)); -} - - -void operator delete(void *pBlock) -{ - deallocate(pBlock); -} - -void operator delete[](void *pBlock) -{ - deallocate(pBlock); -} - - -void operator delete(void *pBlock, const nothrow_t &) -{ - deallocate(pBlock); -} - -void operator delete[](void *pBlock, const nothrow_t &) -{ - deallocate(pBlock); -} diff --git a/src/mac/msl_replacements/time.cpp b/src/mac/msl_replacements/time.cpp deleted file mode 100644 index 9100a9c2..00000000 --- a/src/mac/msl_replacements/time.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#include -// we include timesize.mac.h to get whether or not __TIMESIZE_DOUBLE__ is -// defined. This is not safe, given that __TIMESIZE_DOUBLE__ affects MSL -// at MSL's compile time, not ours, so be forgiving if you have changed it -// since you have built MSL. -#include -#include - -#include -#include - -#include "execution_context.hpp" - -#include - - -extern "C" -{ - clock_t __get_clock(); - time_t __get_time(); - int __to_gm_time(time_t *pTime); - int __is_dst(); -} - - -static inline uint64_t get_nanoseconds() -{ - using boost::detail::thread::force_cast; - return(force_cast(AbsoluteToNanoseconds(UpTime()))); -} - - -#ifdef __TIMESIZE_DOUBLE__ - -// return number of microseconds since startup as a double -clock_t __get_clock() -{ - static const double k_dNanosecondsPerMicrosecond(1000.0); - - return(get_nanoseconds() / k_dNanosecondsPerMicrosecond); -} - -#else - -// return number of ticks (60th of a second) since startup as a long -clock_t __get_clock() -{ - static const uint64_t k_ullTicksPerSecond(60ULL); - static const uint64_t k_ullNanosecondsPerSecond(1000ULL * 1000ULL * 1000ULL); - static const uint64_t k_ullNanosecondsPerTick(k_ullNanosecondsPerSecond / k_ullTicksPerSecond); - - return(get_nanoseconds() / k_ullNanosecondsPerTick); -} - -#endif - -// return number of seconds elapsed since Jan 1, 1970 -time_t __get_time() -{ - boost::xtime sTime; - int nType = boost::xtime_get(&sTime, boost::TIME_UTC); - assert(nType == boost::TIME_UTC); - return(static_cast(sTime.sec)); -} - - -static inline MachineLocation &read_location() -{ - static MachineLocation s_sLocation; - assert(boost::threads::mac::at_st()); - ReadLocation(&s_sLocation); - return(s_sLocation); -} - -static inline MachineLocation &get_location() -{ - static MachineLocation &s_rLocation(read_location()); - return(s_rLocation); -} - - -// force the machine location to be cached at static initlialization -static MachineLocation &g_rIgnored(get_location()); - -static inline long calculate_delta() -{ - MachineLocation &rLocation(get_location()); - -// gmtDelta is a 24-bit, signed integer. We need to strip out the lower 24 bits, -// then sign-extend what we have. - long lDelta = rLocation.u.gmtDelta & 0x00ffffffL; - if((lDelta & 0x00800000L) != 0L) - { - lDelta |= 0xFF000000; - } - return(lDelta); -} - -static inline bool check_if_location_is_broken() -{ - MachineLocation &rLocation(get_location()); - if(rLocation.latitude == 0 && rLocation.longitude == 0 && rLocation.u.gmtDelta == 0) - return(true); - return(false); -} - -static inline bool location_is_broken() -{ - static bool s_bLocationIsBroken(check_if_location_is_broken()); - return(s_bLocationIsBroken); -} - - -// translate time to GMT -int __to_gm_time(time_t *pTime) -{ - if(location_is_broken()) - { - return(0); - } - - static long s_lDelta(calculate_delta()); - *pTime -= s_lDelta; - return(1); -} - - -static inline bool is_daylight_savings_time() -{ - MachineLocation &rLocation(get_location()); - return(rLocation.u.dlsDelta != 0); -} - -// check if we're in daylight savings time -int __is_dst() -{ - if(location_is_broken()) - { - return(-1); - } - static bool bIsDaylightSavingsTime(is_daylight_savings_time()); - return(static_cast(bIsDaylightSavingsTime)); -} diff --git a/src/mac/os.cpp b/src/mac/os.cpp deleted file mode 100644 index 6a47d178..00000000 --- a/src/mac/os.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#include "os.hpp" - - -#include - -#include - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace os { - - -// read the OS version from Gestalt -static inline long get_version() -{ - long lVersion; - OSErr nErr = Gestalt(gestaltSystemVersion, &lVersion); - assert(nErr == noErr); - return(lVersion); -} - - -// check if we're running under Mac OS X and cache that information -bool x() -{ - static bool bX = (version() >= 0x1000); - return(bX); -} - - -// read the OS version and cache it -long version() -{ - static long lVersion = get_version(); - return(lVersion); -} - - -} // namespace os - -} // namespace mac - -} // namespace threads - -} // namespace boost diff --git a/src/mac/os.hpp b/src/mac/os.hpp deleted file mode 100644 index c631ca1d..00000000 --- a/src/mac/os.hpp +++ /dev/null @@ -1,37 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#ifndef BOOST_OS_MJM012402_HPP -#define BOOST_OS_MJM012402_HPP - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace os { - - -// functions to determine the OS environment. With namespaces, you get a cute call: -// mac::os::x - -bool x(); -long version(); - - -} // namespace os - -} // namespace mac - -} // namespace threads - -} // namespace boost - - -#endif // BOOST_OS_MJM012402_HPP diff --git a/src/mac/ot_context.cpp b/src/mac/ot_context.cpp deleted file mode 100644 index de61e6f8..00000000 --- a/src/mac/ot_context.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#include "ot_context.hpp" - -#include "execution_context.hpp" - - -#include - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -ot_context::ot_context() -{ - assert(at_st()); - - OSStatus lStatus = InitOpenTransportInContext(0UL, &m_pContext); -// TODO - throw on error - assert(lStatus == noErr); -} - -ot_context::~ot_context() -{ - CloseOpenTransportInContext(m_pContext); -} - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost diff --git a/src/mac/ot_context.hpp b/src/mac/ot_context.hpp deleted file mode 100644 index 0d640105..00000000 --- a/src/mac/ot_context.hpp +++ /dev/null @@ -1,58 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#ifndef BOOST_OT_CONTEXT_MJM012402_HPP -#define BOOST_OT_CONTEXT_MJM012402_HPP - - -#include - -#include - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -// class ot_context is intended to be used only as a singleton. All that this class -// does is ask OpenTransport to create him an OTClientContextPtr, and then doles -// this out to anyone who wants it. ot_context should only be instantiated at -// system task time. - -class ot_context: private noncopyable -{ - protected: - ot_context(); - ~ot_context(); - - public: - OTClientContextPtr get_context(); - - private: - OTClientContextPtr m_pContext; -}; - - -inline OTClientContextPtr ot_context::get_context() - { return(m_pContext); } - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost - - -#endif // BOOST_OT_CONTEXT_MJM012402_HPP diff --git a/src/mac/package.hpp b/src/mac/package.hpp deleted file mode 100644 index a52d7a51..00000000 --- a/src/mac/package.hpp +++ /dev/null @@ -1,76 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#ifndef BOOST_PACKAGE_MJM012402_HPP -#define BOOST_PACKAGE_MJM012402_HPP - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -class base_package: private noncopyable -{ - public: - virtual void accept() = 0; -}; - -template -class package: public base_package -{ - public: - inline package(function &rFunctor): - m_rFunctor(rFunctor) - { /* no-op */ } - inline ~package() - { /* no-op */ } - - virtual void accept() - { m_oR = m_rFunctor(); } - inline R return_value() - { return(m_oR); } - - private: - function &m_rFunctor; - R m_oR; -}; - -template<> -class package: public base_package -{ - public: - inline package(function &rFunctor): - m_rFunctor(rFunctor) - { /* no-op */ } - inline ~package() - { /* no-op */ } - - virtual void accept() - { m_rFunctor(); } - inline void return_value() - { return; } - - private: - function &m_rFunctor; -}; - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost - - -#endif // BOOST_PACKAGE_MJM012402_HPP diff --git a/src/mac/periodical.hpp b/src/mac/periodical.hpp deleted file mode 100644 index 7eb3d552..00000000 --- a/src/mac/periodical.hpp +++ /dev/null @@ -1,97 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#ifndef BOOST_PERIODICAL_MJM012402_HPP -#define BOOST_PERIODICAL_MJM012402_HPP - - -#include -#include - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -// class periodical inherits from its template parameter, which should follow the -// pattern set by classes dt_scheduler and st_scheduler. periodical knows how to -// call a boost::function, where the xx_scheduler classes only know to to call a -// member periodically. - -template -class periodical: private noncopyable, private Scheduler -{ - public: - periodical(function &rFunction); - ~periodical(); - - public: - void start(); - void stop(); - - protected: - virtual void periodic_function(); - - private: - function m_oFunction; -}; - - -template -periodical::periodical(function &rFunction): - m_oFunction(rFunction) -{ -// no-op -} - -template -periodical::~periodical() -{ - stop(); -} - -template -void periodical::start() -{ - start_polling(); -} - -template -void periodical::stop() -{ - stop_polling(); -} - - -template -inline void periodical::periodic_function() -{ - try - { - m_oFunction(); - } - catch(...) - { - } -} - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost - - -#endif // BOOST_PERIODICAL_MJM012402_HPP diff --git a/src/mac/prefix.hpp b/src/mac/prefix.hpp deleted file mode 100644 index b96ce9c5..00000000 --- a/src/mac/prefix.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#define NDEBUG -#define TARGET_CARBON 1 diff --git a/src/mac/remote_call_manager.cpp b/src/mac/remote_call_manager.cpp deleted file mode 100644 index b7191eee..00000000 --- a/src/mac/remote_call_manager.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#include "remote_call_manager.hpp" - -#include - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -using detail::delivery_man; - - -remote_call_manager::remote_call_manager(): - m_oDTDeliveryMan(), - m_oSTDeliveryMan(), - m_oDTFunction(bind(&delivery_man::accept_deliveries, &m_oDTDeliveryMan)), - m_oSTFunction(bind(&delivery_man::accept_deliveries, &m_oSTDeliveryMan)), - m_oDTPeriodical(m_oDTFunction), - m_oSTPeriodical(m_oSTFunction) -{ - m_oDTPeriodical.start(); - m_oSTPeriodical.start(); -} - -remote_call_manager::~remote_call_manager() -{ -} - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost diff --git a/src/mac/remote_call_manager.hpp b/src/mac/remote_call_manager.hpp deleted file mode 100644 index 2f62c3d2..00000000 --- a/src/mac/remote_call_manager.hpp +++ /dev/null @@ -1,102 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#ifndef BOOST_REMOTE_CALL_MANAGER_MJM012402_HPP -#define BOOST_REMOTE_CALL_MANAGER_MJM012402_HPP - - -#include - -#include "delivery_man.hpp" -#include "dt_scheduler.hpp" -#include "periodical.hpp" -#include "execution_context.hpp" -#include "st_scheduler.hpp" - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -// class remote_call_manager is used by the remote call functions (dt_remote_call and -// st_remote_call) to execute functions in non-MP contexts. - -class remote_call_manager: private noncopyable -{ - protected: - remote_call_manager(); - ~remote_call_manager(); - - public: - template - R execute_at_dt(function &rFunctor); - template - R execute_at_st(function &rFunctor); - - private: - template - static R execute_now(function &rFunctor); - - private: - delivery_man m_oDTDeliveryMan; - delivery_man m_oSTDeliveryMan; - function m_oDTFunction; - function m_oSTFunction; - periodical m_oDTPeriodical; - periodical m_oSTPeriodical; -}; - - -template -/*static*/ inline R remote_call_manager::execute_now(function &rFunctor) -{ - return(rFunctor()); -} -template<> -/*static*/ inline void remote_call_manager::execute_now(function &rFunctor) -{ - rFunctor(); -} - - -template -inline R remote_call_manager::execute_at_dt(function &rFunctor) -{ - if(at_mp()) - { - return(m_oDTDeliveryMan.deliver(rFunctor)); - } - return(execute_now(rFunctor)); -} - -template -inline R remote_call_manager::execute_at_st(function &rFunctor) -{ - if(at_mp()) - { - return(m_oSTDeliveryMan.deliver(rFunctor)); - } - assert(at_st()); - return(execute_now(rFunctor)); -} - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost - - -#endif // BOOST_REMOTE_CALL_MANAGER_MJM012402_HPP diff --git a/src/mac/remote_calls.hpp b/src/mac/remote_calls.hpp deleted file mode 100644 index 41e67863..00000000 --- a/src/mac/remote_calls.hpp +++ /dev/null @@ -1,157 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#ifndef BOOST_REMOTE_CALLS_MJM012402_HPP -#define BOOST_REMOTE_CALLS_MJM012402_HPP - - -#include - -#include "remote_call_manager.hpp" -#include - - -// this file contains macros to generate functions with the signatures: -// ReturnType st_remote_call([pascal] ReturnType (*pfnFunction)( -// [Argument1Type[, Argument2Type[...]]]) -// [, Argument1Type oArgument1[, Argument2Type oArgument2[...]]]) -// and -// ReturnType dt_remote_call([pascal] ReturnType (*pfnFunction)( -// [Argument1Type[, Argument2Type[...]]]) -// [, Argument1Type oArgument1[, Argument2Type oArgument2[...]]]) -// in other words, identical to the function pointer versions of boost::bind, but -// with the return type returned. The purpose of these functions is to be able to -// request that a function be called at system task time or deferred task time, then -// sleep until it is called, and finally get back its return value. - -#define BOOST_REMOTE_CALL_CLASS_LIST_0 -#define BOOST_REMOTE_CALL_CLASS_LIST_1 BOOST_REMOTE_CALL_CLASS_LIST_0, class A1 -#define BOOST_REMOTE_CALL_CLASS_LIST_2 BOOST_REMOTE_CALL_CLASS_LIST_1, class A2 -#define BOOST_REMOTE_CALL_CLASS_LIST_3 BOOST_REMOTE_CALL_CLASS_LIST_2, class A3 -#define BOOST_REMOTE_CALL_CLASS_LIST_4 BOOST_REMOTE_CALL_CLASS_LIST_3, class A4 -#define BOOST_REMOTE_CALL_CLASS_LIST_5 BOOST_REMOTE_CALL_CLASS_LIST_4, class A5 -#define BOOST_REMOTE_CALL_CLASS_LIST_6 BOOST_REMOTE_CALL_CLASS_LIST_5, class A6 -#define BOOST_REMOTE_CALL_CLASS_LIST_7 BOOST_REMOTE_CALL_CLASS_LIST_6, class A7 -#define BOOST_REMOTE_CALL_CLASS_LIST_8 BOOST_REMOTE_CALL_CLASS_LIST_7, class A8 -#define BOOST_REMOTE_CALL_CLASS_LIST_9 BOOST_REMOTE_CALL_CLASS_LIST_8, class A9 - - -#define BOOST_REMOTE_CALL_ARGUMENT_LIST_0 -#define BOOST_REMOTE_CALL_ARGUMENT_LIST_1 BOOST_REMOTE_CALL_ARGUMENT_LIST_0 A1 oA1 -#define BOOST_REMOTE_CALL_ARGUMENT_LIST_2 BOOST_REMOTE_CALL_ARGUMENT_LIST_1, A2 oA2 -#define BOOST_REMOTE_CALL_ARGUMENT_LIST_3 BOOST_REMOTE_CALL_ARGUMENT_LIST_2, A3 oA3 -#define BOOST_REMOTE_CALL_ARGUMENT_LIST_4 BOOST_REMOTE_CALL_ARGUMENT_LIST_3, A4 oA4 -#define BOOST_REMOTE_CALL_ARGUMENT_LIST_5 BOOST_REMOTE_CALL_ARGUMENT_LIST_4, A5 oA5 -#define BOOST_REMOTE_CALL_ARGUMENT_LIST_6 BOOST_REMOTE_CALL_ARGUMENT_LIST_5, A6 oA6 -#define BOOST_REMOTE_CALL_ARGUMENT_LIST_7 BOOST_REMOTE_CALL_ARGUMENT_LIST_6, A7 oA7 -#define BOOST_REMOTE_CALL_ARGUMENT_LIST_8 BOOST_REMOTE_CALL_ARGUMENT_LIST_7, A8 oA8 -#define BOOST_REMOTE_CALL_ARGUMENT_LIST_9 BOOST_REMOTE_CALL_ARGUMENT_LIST_8, A9 oA9 - - -#define BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_0 -#define BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_1 BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_0, oA1 -#define BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_2 BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_1, oA2 -#define BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_3 BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_2, oA3 -#define BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_4 BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_3, oA4 -#define BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_5 BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_4, oA5 -#define BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_6 BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_5, oA6 -#define BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_7 BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_6, oA7 -#define BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_8 BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_7, oA8 -#define BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_9 BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_8, oA9 - - -#define BOOST_REMOTE_CALL_COMMA_0 -#define BOOST_REMOTE_CALL_COMMA_1 , -#define BOOST_REMOTE_CALL_COMMA_2 , -#define BOOST_REMOTE_CALL_COMMA_3 , -#define BOOST_REMOTE_CALL_COMMA_4 , -#define BOOST_REMOTE_CALL_COMMA_5 , -#define BOOST_REMOTE_CALL_COMMA_6 , -#define BOOST_REMOTE_CALL_COMMA_7 , -#define BOOST_REMOTE_CALL_COMMA_8 , -#define BOOST_REMOTE_CALL_COMMA_9 , - - -// this is the macro that ties it all together. From here, we generate all forms of -// dt_remote_call and st_remote_call. - -#define BOOST_REMOTE_CALL(context, stack, n) \ -template \ -inline R context ## _remote_call(stack R (*pfnF)( \ - BOOST_REMOTE_CALL_ARGUMENT_LIST_ ## n) \ - BOOST_REMOTE_CALL_COMMA_ ## n \ - BOOST_REMOTE_CALL_ARGUMENT_LIST_ ## n) \ -{ \ - using ::boost::detail::thread::singleton; \ - using detail::remote_call_manager; \ - function oFunc(bind(pfnF BOOST_REMOTE_CALL_FUNCTION_ARGUMENT_LIST_ ## n)); \ - remote_call_manager &rManager(singleton::instance()); \ - return(rManager.execute_at_ ## context(oFunc)); \ -} - - -namespace boost { - -namespace threads { - -namespace mac { - - -BOOST_REMOTE_CALL(st, , 0) -BOOST_REMOTE_CALL(st, , 1) -BOOST_REMOTE_CALL(st, , 2) -BOOST_REMOTE_CALL(st, , 3) -BOOST_REMOTE_CALL(st, , 4) -BOOST_REMOTE_CALL(st, , 5) -BOOST_REMOTE_CALL(st, , 6) -BOOST_REMOTE_CALL(st, , 7) -BOOST_REMOTE_CALL(st, , 8) -BOOST_REMOTE_CALL(st, , 9) - -BOOST_REMOTE_CALL(dt, , 0) -BOOST_REMOTE_CALL(dt, , 1) -BOOST_REMOTE_CALL(dt, , 2) -BOOST_REMOTE_CALL(dt, , 3) -BOOST_REMOTE_CALL(dt, , 4) -BOOST_REMOTE_CALL(dt, , 5) -BOOST_REMOTE_CALL(dt, , 6) -BOOST_REMOTE_CALL(dt, , 7) -BOOST_REMOTE_CALL(dt, , 8) -BOOST_REMOTE_CALL(dt, , 9) - - -BOOST_REMOTE_CALL(st, pascal, 0) -BOOST_REMOTE_CALL(st, pascal, 1) -BOOST_REMOTE_CALL(st, pascal, 2) -BOOST_REMOTE_CALL(st, pascal, 3) -BOOST_REMOTE_CALL(st, pascal, 4) -BOOST_REMOTE_CALL(st, pascal, 5) -BOOST_REMOTE_CALL(st, pascal, 6) -BOOST_REMOTE_CALL(st, pascal, 7) -BOOST_REMOTE_CALL(st, pascal, 8) -BOOST_REMOTE_CALL(st, pascal, 9) - -BOOST_REMOTE_CALL(dt, pascal, 0) -BOOST_REMOTE_CALL(dt, pascal, 1) -BOOST_REMOTE_CALL(dt, pascal, 2) -BOOST_REMOTE_CALL(dt, pascal, 3) -BOOST_REMOTE_CALL(dt, pascal, 4) -BOOST_REMOTE_CALL(dt, pascal, 5) -BOOST_REMOTE_CALL(dt, pascal, 6) -BOOST_REMOTE_CALL(dt, pascal, 7) -BOOST_REMOTE_CALL(dt, pascal, 8) -BOOST_REMOTE_CALL(dt, pascal, 9) - - -} // namespace mac - -} // namespace threads - -} // namespace boost - - -#endif // BOOST_REMOTE_CALLS_MJM012402_HPP diff --git a/src/mac/safe.cpp b/src/mac/safe.cpp deleted file mode 100644 index 8c2cfc51..00000000 --- a/src/mac/safe.cpp +++ /dev/null @@ -1,210 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include "execution_context.hpp" - - -using boost::detail::thread::force_cast; - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -static OSStatus safe_wait(function &rFunction, Duration lDuration); - - -// we call WNE to allow tasks that own the resource the blue is waiting on system -// task time, in case they are blocked on an ST remote call (or a memory allocation -// for that matter). -static void idle() -{ - if(at_st()) - { - EventRecord sEvent; - bool bEvent = WaitNextEvent(0U, &sEvent, 0UL, NULL); - } -} - - -OSStatus safe_wait_on_semaphore(MPSemaphoreID pSemaphoreID, Duration lDuration) -{ - function oWaitOnSemaphore; - oWaitOnSemaphore = bind(MPWaitOnSemaphore, pSemaphoreID, _1); - return(safe_wait(oWaitOnSemaphore, lDuration)); -} - - -OSStatus safe_enter_critical_region(MPCriticalRegionID pCriticalRegionID, Duration lDuration, MPCriticalRegionID pCriticalRegionCriticalRegionID/* = kInvalidID*/) -{ - if(pCriticalRegionCriticalRegionID != kInvalidID) - { - if(at_mp()) - { - // enter the critical region's critical region - OSStatus lStatus = noErr; - AbsoluteTime sExpiration; - if(lDuration != kDurationImmediate && lDuration != kDurationForever) - { - sExpiration = AddDurationToAbsolute(lDuration, UpTime()); - } - lStatus = MPEnterCriticalRegion(pCriticalRegionCriticalRegionID, lDuration); - assert(lStatus == noErr || lStatus == kMPTimeoutErr); - if(lStatus == noErr) - { - // calculate a new duration - if(lDuration != kDurationImmediate && lDuration != kDurationForever) - { - // check if we have any time left - AbsoluteTime sUpTime(UpTime()); - if(force_cast(sExpiration) > force_cast(sUpTime)) - { - // reset our duration to our remaining time - lDuration = AbsoluteDeltaToDuration(sExpiration, sUpTime); - } - else - { - // no time left - lDuration = kDurationImmediate; - } - } - // if we entered the critical region, exit it again - lStatus = MPExitCriticalRegion(pCriticalRegionCriticalRegionID); - assert(lStatus == noErr); - } - else - { - // otherwise, give up - return(lStatus); - } - } - else - { - // if we're at system task time, try to enter the critical region's critical - // region until we succeed. MP tasks will block on this until we let it go. - OSStatus lStatus; - do - { - lStatus = MPEnterCriticalRegion(pCriticalRegionCriticalRegionID, kDurationImmediate); - } while(lStatus == kMPTimeoutErr); - assert(lStatus == noErr); - } - } - -// try to enter the critical region - function oEnterCriticalRegion; - oEnterCriticalRegion = bind(MPEnterCriticalRegion, pCriticalRegionID, _1); - OSStatus lStatus = safe_wait(oEnterCriticalRegion, lDuration); - -// if we entered the critical region's critical region to get the critical region, -// exit the critical region's critical region. - if(pCriticalRegionCriticalRegionID != kInvalidID && at_mp() == false) - { - lStatus = MPExitCriticalRegion(pCriticalRegionCriticalRegionID); - assert(lStatus == noErr); - } - return(lStatus); -} - -OSStatus safe_wait_on_queue(MPQueueID pQueueID, void **pParam1, void **pParam2, void **pParam3, Duration lDuration) -{ - function oWaitOnQueue; - oWaitOnQueue = bind(MPWaitOnQueue, pQueueID, pParam1, pParam2, pParam3, _1); - return(safe_wait(oWaitOnQueue, lDuration)); -} - -OSStatus safe_delay_until(AbsoluteTime *pWakeUpTime) -{ - if(execution_context() == k_eExecutionContextMPTask) - { - return(MPDelayUntil(pWakeUpTime)); - } - else - { - uint64_t ullWakeUpTime = force_cast(*pWakeUpTime); - - while(force_cast(UpTime()) < ullWakeUpTime) - { - idle(); - } - - return(noErr); - } -} - - -OSStatus safe_wait(function &rFunction, Duration lDuration) -{ - if(execution_context() == k_eExecutionContextMPTask) - { - return(rFunction(lDuration)); - } - else - { - uint64_t ullExpiration = 0ULL; - - // get the expiration time in UpTime units - if(lDuration == kDurationForever) - { - ullExpiration = (::std::numeric_limits::max)(); - } - else if(lDuration == kDurationImmediate) - { - ullExpiration = force_cast(UpTime()); - } - else - { - AbsoluteTime sExpiration = AddDurationToAbsolute(lDuration, UpTime()); - ullExpiration = force_cast(sExpiration); - } - - OSStatus lStatus; - bool bExpired = false; - - do - { - lStatus = rFunction(kDurationImmediate); -// mm - "if" #if 0'd out to allow task time to threads blocked on I/O -#if 0 - if(lStatus == kMPTimeoutErr) -#endif - { - idle(); - } - if(lDuration != kDurationForever) - { - bExpired = (force_cast(UpTime()) < ullExpiration); - } - } while(lStatus == kMPTimeoutErr && bExpired == false); - - return(lStatus); - } -} - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost diff --git a/src/mac/safe.hpp b/src/mac/safe.hpp deleted file mode 100644 index c0409fcb..00000000 --- a/src/mac/safe.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#ifndef BOOST_SAFE_MJM012402_HPP -#define BOOST_SAFE_MJM012402_HPP - - -#include - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -// these functions are used to wain in an execution context-independent manor. All of these -// functions are both MP- and ST-safe. - -OSStatus safe_wait_on_semaphore(MPSemaphoreID pSemaphoreID, Duration lDuration); -OSStatus safe_enter_critical_region(MPCriticalRegionID pCriticalRegionID, Duration lDuration, MPCriticalRegionID pCriticalRegionCriticalRegionID = kInvalidID); -OSStatus safe_wait_on_queue(MPQueueID pQueueID, void **pParam1, void **pParam2, void **pParam3, Duration lDuration); -OSStatus safe_delay_until(AbsoluteTime *pWakeUpTime); - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost - -#endif // BOOST_SAFE_MJM012402_HPP diff --git a/src/mac/scoped_critical_region.cpp b/src/mac/scoped_critical_region.cpp deleted file mode 100644 index fa12b1d1..00000000 --- a/src/mac/scoped_critical_region.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#include "scoped_critical_region.hpp" - -#include "init.hpp" - - -#include - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -scoped_critical_region::scoped_critical_region(): - m_pCriticalRegionID(kInvalidID) -{ - static bool bIgnored = thread_init(); - OSStatus lStatus = MPCreateCriticalRegion(&m_pCriticalRegionID); - if(lStatus != noErr || m_pCriticalRegionID == kInvalidID) - throw(thread_resource_error()); -} - -scoped_critical_region::~scoped_critical_region() -{ - OSStatus lStatus = MPDeleteCriticalRegion(m_pCriticalRegionID); - assert(lStatus == noErr); -} - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost diff --git a/src/mac/scoped_critical_region.hpp b/src/mac/scoped_critical_region.hpp deleted file mode 100644 index 74ac9d83..00000000 --- a/src/mac/scoped_critical_region.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#ifndef BOOST_SCOPED_CRITICAL_REGION_MJM012402_HPP -#define BOOST_SCOPED_CRITICAL_REGION_MJM012402_HPP - - -#include - -#include - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -// class scoped_critical_region probably needs a new name. Although the current name -// is accurate, it can be read to mean that a critical region is entered for the -// current scope. In reality, a critical region is _created_ for the current scope. -// This class is intended as a replacement for MPCriticalRegionID that will -// automatically create and dispose of itself. - -class scoped_critical_region -{ - public: - scoped_critical_region(); - ~scoped_critical_region(); - - public: - operator const MPCriticalRegionID &() const; - const MPCriticalRegionID &get() const; - - private: - MPCriticalRegionID m_pCriticalRegionID; -}; - - -// these are inlined for speed. -inline scoped_critical_region::operator const MPCriticalRegionID &() const - { return(m_pCriticalRegionID); } -inline const MPCriticalRegionID &scoped_critical_region::get() const - { return(m_pCriticalRegionID); } - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost - - -#endif // BOOST_SCOPED_CRITICAL_REGION_MJM012402_HPP diff --git a/src/mac/st_scheduler.cpp b/src/mac/st_scheduler.cpp deleted file mode 100644 index 8dfc37ad..00000000 --- a/src/mac/st_scheduler.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#include "st_scheduler.hpp" - -#include - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -#if TARGET_CARBON - -st_scheduler::st_scheduler(): - m_uppTask(NULL), - m_pTimer(NULL) -{ - m_uppTask = NewEventLoopTimerUPP(task_entry); -// TODO - throw on error - assert(m_uppTask != NULL); -} - -st_scheduler::~st_scheduler() -{ - DisposeEventLoopTimerUPP(m_uppTask); - m_uppTask = NULL; -} - - -void st_scheduler::start_polling() -{ - assert(m_pTimer == NULL); - OSStatus lStatus = InstallEventLoopTimer(GetMainEventLoop(), - 0 * kEventDurationSecond, - kEventDurationMillisecond, - m_uppTask, - this, - &m_pTimer); -// TODO - throw on error - assert(lStatus == noErr); -} - -void st_scheduler::stop_polling() -{ - assert(m_pTimer != NULL); - OSStatus lStatus = RemoveEventLoopTimer(m_pTimer); - assert(lStatus == noErr); - m_pTimer = NULL; -} - - -/*static*/ pascal void st_scheduler::task_entry(EventLoopTimerRef /*pTimer*/, void *pRefCon) -{ - st_scheduler *pThis = reinterpret_cast(pRefCon); - assert(pThis != NULL); - pThis->task(); -} - -void st_scheduler::task() -{ - periodic_function(); -} - -#else -# error st_scheduler unimplemented! -#endif - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost diff --git a/src/mac/st_scheduler.hpp b/src/mac/st_scheduler.hpp deleted file mode 100644 index becf1c79..00000000 --- a/src/mac/st_scheduler.hpp +++ /dev/null @@ -1,67 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#ifndef BOOST_ST_SCHEDULER_MJM012402_HPP -#define BOOST_ST_SCHEDULER_MJM012402_HPP - - -#include - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -// class st_scheduler calls its pure-virtual periodic_function method periodically at -// system task time. This is generally 40Hz under Mac OS 9. - -class st_scheduler -{ - public: - st_scheduler(); - virtual ~st_scheduler(); - - protected: - void start_polling(); - void stop_polling(); - - private: - virtual void periodic_function() = 0; - -#if TARGET_CARBON -// use event loop timers under Carbon - private: - static pascal void task_entry(EventLoopTimerRef pTimer, void *pRefCon); - void task(); - - private: - EventLoopTimerUPP m_uppTask; - EventLoopTimerRef m_pTimer; -#else -// this can be implemented using OT system tasks. This would be mostly a copy-and- -// paste of the dt_scheduler code, replacing DeferredTask with SystemTask and DT -// with ST. -# error st_scheduler unimplemented! -#endif -}; - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost - - -#endif // BOOST_ST_SCHEDULER_MJM012402_HPP diff --git a/src/mac/thread_cleanup.cpp b/src/mac/thread_cleanup.cpp deleted file mode 100644 index 478b4fe7..00000000 --- a/src/mac/thread_cleanup.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#include "thread_cleanup.hpp" - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -namespace { - -TaskStorageIndex g_ulIndex(0UL); - -} // anonymous namespace - - -void do_thread_startup() -{ - if(g_ulIndex == 0UL) - { - OSStatus lStatus = MPAllocateTaskStorageIndex(&g_ulIndex); - assert(lStatus == noErr); - } - set_thread_cleanup_task(NULL); -} - -void do_thread_cleanup() -{ - void (*pfnTask)() = MPGetTaskValue(g_ulIndex) -} - - -void set_thread_cleanup_task(void (*pfnTask)()) -{ - lStatus = MPSetTaskValue(g_ulIndex, reinterpret_cast(pfnTask)); - assert(lStatus == noErr); -} - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost diff --git a/src/mac/thread_cleanup.hpp b/src/mac/thread_cleanup.hpp deleted file mode 100644 index c28763d4..00000000 --- a/src/mac/thread_cleanup.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// (C) Copyright Mac Murrett 2001. -// Use, modification and distribution are subject to 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) - -// See http://www.boost.org for most recent version. - -#ifndef BOOST_THREAD_CLEANUP_MJM012402_HPP -#define BOOST_THREAD_CLEANUP_MJM012402_HPP - - -namespace boost { - -namespace threads { - -namespace mac { - -namespace detail { - - -void do_thread_startup(); -void do_thread_cleanup(); - -void set_thread_cleanup_task(); - - -} // namespace detail - -} // namespace mac - -} // namespace threads - -} // namespace boost - - -#endif // BOOST_THREAD_CLEANUP_MJM012402_HPP diff --git a/src/exceptions.cpp b/src/pthread/exceptions.cpp similarity index 100% rename from src/exceptions.cpp rename to src/pthread/exceptions.cpp diff --git a/src/thread.cpp b/src/pthread/thread.cpp similarity index 52% rename from src/thread.cpp rename to src/pthread/thread.cpp index dc8ff078..c4757d29 100644 --- a/src/thread.cpp +++ b/src/pthread/thread.cpp @@ -13,58 +13,10 @@ #include #include -#if defined(BOOST_HAS_WINTHREADS) -# include -# if !defined(BOOST_NO_THREADEX) -# include -# endif -#elif defined(BOOST_HAS_MPTASKS) -# include - -# include "init.hpp" -# include "safe.hpp" -# include -#endif - #include "timeconv.inl" -#if defined(BOOST_HAS_WINTHREADS) -# include "boost/thread/detail/tss_hooks.hpp" -#endif - namespace { -#if defined(BOOST_HAS_WINTHREADS) && defined(BOOST_NO_THREADEX) -// Windows CE doesn't define _beginthreadex - -struct ThreadProxyData -{ - typedef unsigned (__stdcall* func)(void*); - func start_address_; - void* arglist_; - ThreadProxyData(func start_address,void* arglist) : start_address_(start_address), arglist_(arglist) {} -}; - -DWORD WINAPI ThreadProxy(LPVOID args) -{ - ThreadProxyData* data=reinterpret_cast(args); - DWORD ret=data->start_address_(data->arglist_); - delete data; - return ret; -} - -inline unsigned _beginthreadex(void* security, unsigned stack_size, unsigned (__stdcall* start_address)(void*), -void* arglist, unsigned initflag,unsigned* thrdaddr) -{ - DWORD threadID; - HANDLE hthread=CreateThread(static_cast(security),stack_size,ThreadProxy, - new ThreadProxyData(start_address,arglist),initflag,&threadID); - if (hthread!=0) - *thrdaddr=threadID; - return reinterpret_cast(hthread); -} -#endif - class thread_param { public: @@ -94,24 +46,12 @@ public: } // unnamed namespace extern "C" { -#if defined(BOOST_HAS_WINTHREADS) - unsigned __stdcall thread_proxy(void* param) -#elif defined(BOOST_HAS_PTHREADS) static void* thread_proxy(void* param) -#elif defined(BOOST_HAS_MPTASKS) - static OSStatus thread_proxy(void* param) -#endif { thread_param* p = static_cast(param); boost::function0 threadfunc = p->m_threadfunc; p->started(); threadfunc(); -#if defined(BOOST_HAS_WINTHREADS) - on_thread_exit(); -#endif -#if defined(BOOST_HAS_MPTASKS) - ::boost::detail::thread_cleanup(); -#endif return 0; } @@ -119,56 +59,20 @@ extern "C" { namespace boost { -thread::thread() - : m_joinable(false) -{ -#if defined(BOOST_HAS_WINTHREADS) - m_thread = reinterpret_cast(GetCurrentThread()); - m_id = GetCurrentThreadId(); -#elif defined(BOOST_HAS_PTHREADS) - m_thread = pthread_self(); -#elif defined(BOOST_HAS_MPTASKS) - threads::mac::detail::thread_init(); - threads::mac::detail::create_singletons(); - m_pTaskID = MPCurrentTaskID(); - m_pJoinQueueID = kInvalidID; -#endif -} +thread::thread(): + m_joinable(false) +{} thread::thread(const function0& threadfunc) : m_joinable(true) { thread_param param(threadfunc); -#if defined(BOOST_HAS_WINTHREADS) - m_thread = reinterpret_cast(_beginthreadex(0, 0, &thread_proxy, - ¶m, 0, &m_id)); - if (!m_thread) - throw thread_resource_error(); -#elif defined(BOOST_HAS_PTHREADS) int res = 0; + pthread_t m_thread; res = pthread_create(&m_thread, 0, &thread_proxy, ¶m); if (res != 0) throw thread_resource_error(); -#elif defined(BOOST_HAS_MPTASKS) - threads::mac::detail::thread_init(); - threads::mac::detail::create_singletons(); - OSStatus lStatus = noErr; - - m_pJoinQueueID = kInvalidID; - m_pTaskID = kInvalidID; - - lStatus = MPCreateQueue(&m_pJoinQueueID); - if (lStatus != noErr) throw thread_resource_error(); - - lStatus = MPCreateTask(&thread_proxy, ¶m, 0UL, m_pJoinQueueID, NULL, - NULL, 0UL, &m_pTaskID); - if (lStatus != noErr) - { - lStatus = MPDeleteQueue(m_pJoinQueueID); - assert(lStatus == noErr); - throw thread_resource_error(); - } -#endif + m_id=m_thread; param.wait(); } @@ -176,29 +80,13 @@ thread::~thread() { if (m_joinable) { -#if defined(BOOST_HAS_WINTHREADS) - int res = 0; - res = CloseHandle(reinterpret_cast(m_thread)); - assert(res); -#elif defined(BOOST_HAS_PTHREADS) - pthread_detach(m_thread); -#elif defined(BOOST_HAS_MPTASKS) - assert(m_pJoinQueueID != kInvalidID); - OSStatus lStatus = MPDeleteQueue(m_pJoinQueueID); - assert(lStatus == noErr); -#endif + pthread_detach(*m_id.id); } } bool thread::operator==(const thread& other) const { -#if defined(BOOST_HAS_WINTHREADS) - return other.m_id == m_id; -#elif defined(BOOST_HAS_PTHREADS) - return pthread_equal(m_thread, other.m_thread) != 0; -#elif defined(BOOST_HAS_MPTASKS) - return other.m_pTaskID == m_pTaskID; -#endif + return m_id==other.m_id; } bool thread::operator!=(const thread& other) const @@ -210,19 +98,8 @@ void thread::join() { assert(m_joinable); //See race condition comment below int res = 0; -#if defined(BOOST_HAS_WINTHREADS) - res = WaitForSingleObject(reinterpret_cast(m_thread), INFINITE); - assert(res == WAIT_OBJECT_0); - res = CloseHandle(reinterpret_cast(m_thread)); - assert(res); -#elif defined(BOOST_HAS_PTHREADS) - res = pthread_join(m_thread, 0); + res = pthread_join(*m_id.id, 0); assert(res == 0); -#elif defined(BOOST_HAS_MPTASKS) - OSStatus lStatus = threads::mac::detail::safe_wait_on_queue( - m_pJoinQueueID, NULL, NULL, NULL, kDurationForever); - assert(lStatus == noErr); -#endif // This isn't a race condition since any race that could occur would // have us in undefined behavior territory any way. m_joinable = false; @@ -232,11 +109,6 @@ void thread::sleep(const xtime& xt) { for (int foo=0; foo < 5; ++foo) { -#if defined(BOOST_HAS_WINTHREADS) - int milliseconds; - to_duration(xt, milliseconds); - Sleep(milliseconds); -#elif defined(BOOST_HAS_PTHREADS) # if defined(BOOST_HAS_PTHREAD_DELAY_NP) timespec ts; to_timespec_duration(xt, ts); @@ -256,13 +128,6 @@ void thread::sleep(const xtime& xt) condition cond; cond.timed_wait(lock, xt); # endif -#elif defined(BOOST_HAS_MPTASKS) - int microseconds; - to_microduration(xt, microseconds); - Duration lMicroseconds(kDurationMicrosecond * microseconds); - AbsoluteTime sWakeTime(DurationToAbsolute(lMicroseconds)); - threads::mac::detail::safe_delay_until(&sWakeTime); -#endif xtime cur; xtime_get(&cur, TIME_UTC); if (xtime_cmp(xt, cur) <= 0) @@ -272,9 +137,6 @@ void thread::sleep(const xtime& xt) void thread::yield() { -#if defined(BOOST_HAS_WINTHREADS) - Sleep(0); -#elif defined(BOOST_HAS_PTHREADS) # if defined(BOOST_HAS_SCHED_YIELD) int res = 0; res = sched_yield(); @@ -288,9 +150,6 @@ void thread::yield() xtime_get(&xt, TIME_UTC); sleep(xt); # endif -#elif defined(BOOST_HAS_MPTASKS) - MPYield(); -#endif } thread_group::thread_group() diff --git a/src/timeconv.inl b/src/pthread/timeconv.inl similarity index 100% rename from src/timeconv.inl rename to src/pthread/timeconv.inl diff --git a/src/tss.cpp b/src/pthread/tss.cpp similarity index 100% rename from src/tss.cpp rename to src/pthread/tss.cpp diff --git a/src/xtime.cpp b/src/pthread/xtime.cpp similarity index 100% rename from src/xtime.cpp rename to src/pthread/xtime.cpp diff --git a/src/win32/exceptions.cpp b/src/win32/exceptions.cpp new file mode 100644 index 00000000..88813036 --- /dev/null +++ b/src/win32/exceptions.cpp @@ -0,0 +1,124 @@ +// Copyright (C) 2001-2003 +// William E. Kempf +// +// 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) + +#include + +#include +#include +#include + +namespace boost { + +thread_exception::thread_exception() + : m_sys_err(0) +{ +} + +thread_exception::thread_exception(int sys_err_code) + : m_sys_err(sys_err_code) +{ +} + +thread_exception::~thread_exception() throw() +{ +} + +int thread_exception::native_error() const +{ + return m_sys_err; +} + +lock_error::lock_error() +{ +} + +lock_error::lock_error(int sys_err_code) + : thread_exception(sys_err_code) +{ +} + +lock_error::~lock_error() throw() +{ +} + +const char* lock_error::what() const throw() +{ + return "boost::lock_error"; +} + +thread_resource_error::thread_resource_error() +{ +} + +thread_resource_error::thread_resource_error(int sys_err_code) + : thread_exception(sys_err_code) +{ +} + +thread_resource_error::~thread_resource_error() throw() +{ +} + +const char* thread_resource_error::what() const throw() +{ + return "boost::thread_resource_error"; +} + +unsupported_thread_option::unsupported_thread_option() +{ +} + +unsupported_thread_option::unsupported_thread_option(int sys_err_code) + : thread_exception(sys_err_code) +{ +} + +unsupported_thread_option::~unsupported_thread_option() throw() +{ +} + +const char* unsupported_thread_option::what() const throw() +{ + return "boost::unsupported_thread_option"; +} + +invalid_thread_argument::invalid_thread_argument() +{ +} + +invalid_thread_argument::invalid_thread_argument(int sys_err_code) + : thread_exception(sys_err_code) +{ +} + +invalid_thread_argument::~invalid_thread_argument() throw() +{ +} + +const char* invalid_thread_argument::what() const throw() +{ + return "boost::invalid_thread_argument"; +} + +thread_permission_error::thread_permission_error() +{ +} + +thread_permission_error::thread_permission_error(int sys_err_code) + : thread_exception(sys_err_code) +{ +} + +thread_permission_error::~thread_permission_error() throw() +{ +} + +const char* thread_permission_error::what() const throw() +{ + return "boost::thread_permission_error"; +} + +} // namespace boost diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp new file mode 100644 index 00000000..1b3b7207 --- /dev/null +++ b/src/win32/thread.cpp @@ -0,0 +1,443 @@ +// 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) +// (C) Copyright 2007 Anthony Williams + +#include +#include +#include +#include +#include +#include +#include + +namespace boost +{ + namespace + { +#ifdef _MSC_VER + __declspec(thread) detail::thread_data_base* current_thread_data=0; + detail::thread_data_base* get_current_thread_data() + { + return current_thread_data; + } + void set_current_thread_data(detail::thread_data_base* new_data) + { + current_thread_data=new_data; + } +#elif defined(__BORLANDC__) + detail::thread_data_base* __thread current_thread_data=0; + detail::thread_data_base* get_current_thread_data() + { + return current_thread_data; + } + void set_current_thread_data(detail::thread_data_base* new_data) + { + current_thread_data=new_data; + } +#else + + boost::once_flag current_thread_tls_init_flag=BOOST_ONCE_INIT; + DWORD current_thread_tls_key=0; + + void create_current_thread_tls_key() + { + current_thread_tls_key=TlsAlloc(); + BOOST_ASSERT(current_thread_tls_key!=TLS_OUT_OF_INDEXES); + } + + detail::thread_data_base* get_current_thread_data() + { + boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); + return (detail::thread_data_base*)TlsGetValue(current_thread_tls_key); + } + + void set_current_thread_data(detail::thread_data_base* new_data) + { + boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); + BOOL const res=TlsSetValue(current_thread_tls_key,new_data); + BOOST_ASSERT(res); + } +#endif + } + + void thread::yield() + { + this_thread::yield(); + } + + void thread::sleep(const system_time& target) + { + system_time const now(get_system_time()); + + if(target<=now) + { + this_thread::yield(); + } + else + { + this_thread::sleep(target-now); + } + } + + unsigned __stdcall thread::thread_start_function(void* param) + { + boost::intrusive_ptr thread_info(reinterpret_cast(param),false); + set_current_thread_data(thread_info.get()); + try + { + thread_info->run(); + } + catch(thread_canceled const&) + { + } + catch(...) + { + std::terminate(); + } + return 0; + } + + thread::thread() + {} + + void thread::start_thread() + { + uintptr_t const new_thread=_beginthreadex(0,0,&thread_start_function,thread_info.get(),CREATE_SUSPENDED,&thread_info->id); + if(!new_thread) + { + throw thread_resource_error(); + } + intrusive_ptr_add_ref(thread_info.get()); + thread_info->thread_handle=(detail::win32::handle)(new_thread); + ResumeThread(thread_info->thread_handle); + } + + thread::thread(boost::intrusive_ptr data): + thread_info(data) + {} + + namespace + { + struct externally_launched_thread: + detail::thread_data_base + { + externally_launched_thread() + { + ++count; + thread_handle=detail::win32::duplicate_handle(detail::win32::GetCurrentThread()); + } + + void run() + {} + }; + + struct externally_launched_thread_deleter + { + externally_launched_thread* thread_data; + + externally_launched_thread_deleter(externally_launched_thread* thread_data_): + thread_data(thread_data_) + {} + + void operator()() const + { + intrusive_ptr_release(thread_data); + } + }; + + } + + + thread thread::self() + { + if(!get_current_thread_data()) + { + externally_launched_thread* me=detail::heap_new(); + set_current_thread_data(me); + this_thread::at_thread_exit(externally_launched_thread_deleter(me)); + } + return thread(boost::intrusive_ptr(get_current_thread_data())); + } + + thread::~thread() + { + cancel(); + detach(); + } + + thread::thread(boost::move_t x) + { + { + boost::mutex::scoped_lock l(x->thread_info_mutex); + thread_info=x->thread_info; + } + x->release_handle(); + } + + thread& thread::operator=(boost::move_t x) + { + thread new_thread(x); + swap(new_thread); + return *this; + } + + thread::operator boost::move_t() + { + return boost::move_t(*this); + } + + boost::move_t thread::move() + { + return boost::move_t(*this); + } + + void thread::swap(thread& x) + { + thread_info.swap(x.thread_info); + } + + thread::id thread::get_id() const + { + boost::intrusive_ptr local_thread_info=get_thread_info(); + return local_thread_info?thread::id(local_thread_info->id):thread::id(); + } + + thread::cancel_handle thread::get_cancel_handle() const + { + boost::intrusive_ptr local_thread_info=get_thread_info(); + return local_thread_info?thread::cancel_handle(local_thread_info->cancel_handle.duplicate()):thread::cancel_handle(); + } + + bool thread::joinable() const + { + return get_thread_info(); + } + + void thread::join() + { + boost::intrusive_ptr local_thread_info=get_thread_info(); + if(local_thread_info) + { + this_thread::cancellable_wait(local_thread_info->thread_handle,detail::win32::infinite); + release_handle(); + } + } + + void thread::detach() + { + release_handle(); + } + + void thread::release_handle() + { + boost::mutex::scoped_lock l1(thread_info_mutex); + thread_info=0; + } + + void thread::cancel() + { + boost::intrusive_ptr local_thread_info=get_thread_info(); + if(local_thread_info) + { + detail::win32::SetEvent(local_thread_info->cancel_handle); + } + } + + bool thread::cancellation_requested() const + { + boost::intrusive_ptr local_thread_info=get_thread_info(); + return local_thread_info.get() && (detail::win32::WaitForSingleObject(local_thread_info->cancel_handle,0)==0); + } + + unsigned thread::hardware_concurrency() + { + SYSTEM_INFO info={0}; + GetSystemInfo(&info); + return info.dwNumberOfProcessors; + } + + thread::native_handle_type thread::native_handle() + { + boost::intrusive_ptr local_thread_info=get_thread_info(); + return local_thread_info?(detail::win32::handle)local_thread_info->thread_handle:detail::win32::invalid_handle_value; + } + + boost::intrusive_ptr thread::get_thread_info() const + { + boost::mutex::scoped_lock l(thread_info_mutex); + return thread_info; + } + + namespace this_thread + { + thread::cancel_handle get_cancel_handle() + { + return get_current_thread_data()?thread::cancel_handle(get_current_thread_data()->cancel_handle.duplicate()):thread::cancel_handle(); + } + + bool cancellable_wait(detail::win32::handle handle_to_wait_for,unsigned long milliseconds) + { + detail::win32::handle handles[2]={0}; + unsigned handle_count=0; + unsigned cancel_index=~0U; + if(handle_to_wait_for!=detail::win32::invalid_handle_value) + { + handles[handle_count++]=handle_to_wait_for; + } + if(get_current_thread_data() && get_current_thread_data()->cancel_enabled) + { + cancel_index=handle_count; + handles[handle_count++]=get_current_thread_data()->cancel_handle; + } + + if(handle_count) + { + unsigned long const notified_index=detail::win32::WaitForMultipleObjects(handle_count,handles,false,milliseconds); + if((handle_to_wait_for!=detail::win32::invalid_handle_value) && !notified_index) + { + return true; + } + else if(notified_index==cancel_index) + { + detail::win32::ResetEvent(get_current_thread_data()->cancel_handle); + throw thread_canceled(); + } + } + else + { + detail::win32::Sleep(milliseconds); + } + return false; + } + + thread::id get_id() + { + return thread::id(detail::win32::GetCurrentThreadId()); + } + + void cancellation_point() + { + if(cancellation_enabled() && cancellation_requested()) + { + detail::win32::ResetEvent(get_current_thread_data()->cancel_handle); + throw thread_canceled(); + } + } + + bool cancellation_enabled() + { + return get_current_thread_data() && get_current_thread_data()->cancel_enabled; + } + + bool cancellation_requested() + { + return get_current_thread_data() && (detail::win32::WaitForSingleObject(get_current_thread_data()->cancel_handle,0)==0); + } + + void yield() + { + detail::win32::Sleep(0); + } + + disable_cancellation::disable_cancellation(): + cancel_was_enabled(cancellation_enabled()) + { + if(cancel_was_enabled) + { + get_current_thread_data()->cancel_enabled=false; + } + } + + disable_cancellation::~disable_cancellation() + { + if(get_current_thread_data()) + { + get_current_thread_data()->cancel_enabled=cancel_was_enabled; + } + } + + enable_cancellation::enable_cancellation(disable_cancellation& d) + { + if(d.cancel_was_enabled) + { + get_current_thread_data()->cancel_enabled=true; + } + } + + enable_cancellation::~enable_cancellation() + { + if(get_current_thread_data()) + { + get_current_thread_data()->cancel_enabled=false; + } + } + } + + namespace detail + { + struct thread_exit_callback_node + { + boost::detail::thread_exit_function_base* func; + thread_exit_callback_node* next; + }; + } + namespace + { + void NTAPI thread_exit_func_callback(HINSTANCE, DWORD, PVOID); + typedef void (NTAPI* tls_callback)(HINSTANCE, DWORD, PVOID); + +#ifdef _MSC_VER + extern "C" + { + extern DWORD _tls_used; //the tls directory (located in .rdata segment) + extern tls_callback __xl_a[], __xl_z[]; //tls initializers */ + } + +#if (_MSC_VER >= 1300) // 1300 == VC++ 7.0 +# pragma data_seg(push, old_seg) +#endif + +#pragma data_seg(".CRT$XLB") + tls_callback p_thread_callback = thread_exit_func_callback; +#pragma data_seg() + +#if (_MSC_VER >= 1300) // 1300 == VC++ 7.0 +# pragma data_seg(pop, old_seg) +#endif +#endif + + void NTAPI thread_exit_func_callback(HINSTANCE h, DWORD dwReason, PVOID pv) + { + if((dwReason==DLL_THREAD_DETACH) || (dwReason==DLL_PROCESS_DETACH)) + { + if(boost::detail::thread_data_base* const current_thread_data=get_current_thread_data()) + { + while(current_thread_data->thread_exit_callbacks) + { + detail::thread_exit_callback_node* const current_node=current_thread_data->thread_exit_callbacks; + current_thread_data->thread_exit_callbacks=current_node->next; + if(current_node->func) + { + (*current_node->func)(); + boost::detail::heap_delete(current_node->func); + } + boost::detail::heap_delete(current_node); + } + } + } + } + } + + namespace detail + { + void add_thread_exit_function(thread_exit_function_base* func) + { + thread_exit_callback_node* const new_node=heap_new(); + new_node->func=func; + new_node->next=get_current_thread_data()->thread_exit_callbacks; + get_current_thread_data()->thread_exit_callbacks=new_node; + } + } +} + diff --git a/src/win32/timeconv.inl b/src/win32/timeconv.inl new file mode 100644 index 00000000..5ec3b179 --- /dev/null +++ b/src/win32/timeconv.inl @@ -0,0 +1,130 @@ +// Copyright (C) 2001-2003 +// William E. Kempf +// +// 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) + +// boostinspect:nounnamed + +namespace { +const int MILLISECONDS_PER_SECOND = 1000; +const int NANOSECONDS_PER_SECOND = 1000000000; +const int NANOSECONDS_PER_MILLISECOND = 1000000; + +const int MICROSECONDS_PER_SECOND = 1000000; +const int NANOSECONDS_PER_MICROSECOND = 1000; + +inline void to_time(int milliseconds, boost::xtime& xt) +{ + int res = 0; + res = boost::xtime_get(&xt, boost::TIME_UTC); + assert(res == boost::TIME_UTC); + + xt.sec += (milliseconds / MILLISECONDS_PER_SECOND); + xt.nsec += ((milliseconds % MILLISECONDS_PER_SECOND) * + NANOSECONDS_PER_MILLISECOND); + + if (xt.nsec >= NANOSECONDS_PER_SECOND) + { + ++xt.sec; + xt.nsec -= NANOSECONDS_PER_SECOND; + } +} + +#if defined(BOOST_HAS_PTHREADS) +inline void to_timespec(const boost::xtime& xt, timespec& ts) +{ + ts.tv_sec = static_cast(xt.sec); + ts.tv_nsec = static_cast(xt.nsec); + if(ts.tv_nsec >= NANOSECONDS_PER_SECOND) + { + ts.tv_sec += ts.tv_nsec / NANOSECONDS_PER_SECOND; + ts.tv_nsec %= NANOSECONDS_PER_SECOND; + } +} + +inline void to_time(int milliseconds, timespec& ts) +{ + boost::xtime xt; + to_time(milliseconds, xt); + to_timespec(xt, ts); +} + +inline void to_timespec_duration(const boost::xtime& xt, timespec& ts) +{ + boost::xtime cur; + int res = 0; + res = boost::xtime_get(&cur, boost::TIME_UTC); + assert(res == boost::TIME_UTC); + + if (boost::xtime_cmp(xt, cur) <= 0) + { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + else + { + ts.tv_sec = xt.sec - cur.sec; + ts.tv_nsec = xt.nsec - cur.nsec; + + if( ts.tv_nsec < 0 ) + { + ts.tv_sec -= 1; + ts.tv_nsec += NANOSECONDS_PER_SECOND; + } + if(ts.tv_nsec >= NANOSECONDS_PER_SECOND) + { + ts.tv_sec += ts.tv_nsec / NANOSECONDS_PER_SECOND; + ts.tv_nsec %= NANOSECONDS_PER_SECOND; + } + } +} +#endif + +inline void to_duration(boost::xtime xt, int& milliseconds) +{ + boost::xtime cur; + int res = 0; + res = boost::xtime_get(&cur, boost::TIME_UTC); + assert(res == boost::TIME_UTC); + + if (boost::xtime_cmp(xt, cur) <= 0) + milliseconds = 0; + else + { + if (cur.nsec > xt.nsec) + { + xt.nsec += NANOSECONDS_PER_SECOND; + --xt.sec; + } + milliseconds = (int)((xt.sec - cur.sec) * MILLISECONDS_PER_SECOND) + + (((xt.nsec - cur.nsec) + (NANOSECONDS_PER_MILLISECOND/2)) / + NANOSECONDS_PER_MILLISECOND); + } +} + +inline void to_microduration(boost::xtime xt, int& microseconds) +{ + boost::xtime cur; + int res = 0; + res = boost::xtime_get(&cur, boost::TIME_UTC); + assert(res == boost::TIME_UTC); + + if (boost::xtime_cmp(xt, cur) <= 0) + microseconds = 0; + else + { + if (cur.nsec > xt.nsec) + { + xt.nsec += NANOSECONDS_PER_SECOND; + --xt.sec; + } + microseconds = (int)((xt.sec - cur.sec) * MICROSECONDS_PER_SECOND) + + (((xt.nsec - cur.nsec) + (NANOSECONDS_PER_MICROSECOND/2)) / + NANOSECONDS_PER_MICROSECOND); + } +} +} + +// Change Log: +// 1 Jun 01 Initial creation. diff --git a/src/win32/tss.cpp b/src/win32/tss.cpp new file mode 100644 index 00000000..33f635b9 --- /dev/null +++ b/src/win32/tss.cpp @@ -0,0 +1,251 @@ +// Copyright (C) 2001-2003 William E. Kempf +// Copyright (C) 2006 Roland Schwarz +// +// 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) + +#include + +#include +#ifndef BOOST_THREAD_NO_TSS_CLEANUP + +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_HAS_WINTHREADS) +# include +# include +#endif + +namespace { + +typedef std::vector tss_slots; +typedef std::vector*> tss_data_cleanup_handlers_type; + +boost::once_flag tss_data_once = BOOST_ONCE_INIT; +boost::mutex* tss_data_mutex = 0; +tss_data_cleanup_handlers_type* tss_data_cleanup_handlers = 0; +#if defined(BOOST_HAS_WINTHREADS) + DWORD tss_data_native_key=TLS_OUT_OF_INDEXES; +#elif defined(BOOST_HAS_PTHREADS) + pthread_key_t tss_data_native_key; +#elif defined(BOOST_HAS_MPTASKS) + TaskStorageIndex tss_data_native_key; +#endif +int tss_data_use = 0; + +void tss_data_inc_use(boost::mutex::scoped_lock& lk) +{ + ++tss_data_use; +} + +void tss_data_dec_use(boost::mutex::scoped_lock& lk) +{ + if (0 == --tss_data_use) + { + tss_data_cleanup_handlers_type::size_type i; + for (i = 0; i < tss_data_cleanup_handlers->size(); ++i) + { + delete (*tss_data_cleanup_handlers)[i]; + } + delete tss_data_cleanup_handlers; + tss_data_cleanup_handlers = 0; + lk.unlock(); + delete tss_data_mutex; + tss_data_mutex = 0; +#if defined(BOOST_HAS_WINTHREADS) + TlsFree(tss_data_native_key); + tss_data_native_key=TLS_OUT_OF_INDEXES; +#elif defined(BOOST_HAS_PTHREADS) + pthread_key_delete(tss_data_native_key); +#elif defined(BOOST_HAS_MPTASKS) + // Don't know what to put here. + // But MPTASKS isn't currently maintained anyways... +#endif + } +} + +extern "C" void cleanup_slots(void* p) +{ + tss_slots* slots = static_cast(p); + boost::mutex::scoped_lock lock(*tss_data_mutex); + for (tss_slots::size_type i = 0; i < slots->size(); ++i) + { + (*(*tss_data_cleanup_handlers)[i])((*slots)[i]); + (*slots)[i] = 0; + } +#if defined(BOOST_HAS_WINTHREADS) + TlsSetValue(tss_data_native_key,0); +#endif + tss_data_dec_use(lock); + delete slots; +} + +void init_tss_data() +{ + std::auto_ptr + temp(new tss_data_cleanup_handlers_type); + + std::auto_ptr temp_mutex(new boost::mutex); + if (temp_mutex.get() == 0) + throw boost::thread_resource_error(); + +#if defined(BOOST_HAS_WINTHREADS) + //Force the cleanup implementation library to be linked in + tss_cleanup_implemented(); + + //Allocate tls slot + tss_data_native_key = TlsAlloc(); + if (tss_data_native_key == TLS_OUT_OF_INDEXES) + return; +#elif defined(BOOST_HAS_PTHREADS) + int res = pthread_key_create(&tss_data_native_key, &cleanup_slots); + if (res != 0) + return; +#elif defined(BOOST_HAS_MPTASKS) + OSStatus status = MPAllocateTaskStorageIndex(&tss_data_native_key); + if (status != noErr) + return; +#endif + + // The life time of cleanup handlers and mutex is beeing + // managed by a reference counting technique. + // This avoids a memory leak by releasing the global data + // after last use only, since the execution order of cleanup + // handlers is unspecified on any platform with regards to + // C++ destructor ordering rules. + tss_data_cleanup_handlers = temp.release(); + tss_data_mutex = temp_mutex.release(); +} + +#if defined(BOOST_HAS_WINTHREADS) +tss_slots* get_slots(bool alloc); + +void __cdecl tss_thread_exit() +{ + tss_slots* slots = get_slots(false); + if (slots) + cleanup_slots(slots); +} +#endif + +tss_slots* get_slots(bool alloc) +{ + tss_slots* slots = 0; + +#if defined(BOOST_HAS_WINTHREADS) + slots = static_cast( + TlsGetValue(tss_data_native_key)); +#elif defined(BOOST_HAS_PTHREADS) + slots = static_cast( + pthread_getspecific(tss_data_native_key)); +#elif defined(BOOST_HAS_MPTASKS) + slots = static_cast( + MPGetTaskStorageValue(tss_data_native_key)); +#endif + + if (slots == 0 && alloc) + { + std::auto_ptr temp(new tss_slots); + +#if defined(BOOST_HAS_WINTHREADS) + if (at_thread_exit(&tss_thread_exit) == -1) + return 0; + if (!TlsSetValue(tss_data_native_key, temp.get())) + return 0; +#elif defined(BOOST_HAS_PTHREADS) + if (pthread_setspecific(tss_data_native_key, temp.get()) != 0) + return 0; +#elif defined(BOOST_HAS_MPTASKS) + if (MPSetTaskStorageValue(tss_data_native_key, temp.get()) != noErr) + return 0; +#endif + { + boost::mutex::scoped_lock lock(*tss_data_mutex); + tss_data_inc_use(lock); + } + slots = temp.release(); + } + + return slots; +} + +} // namespace + +namespace boost { + +namespace detail { +void tss::init(boost::function1* pcleanup) +{ + boost::call_once(tss_data_once, &init_tss_data); + if (tss_data_cleanup_handlers == 0) + throw thread_resource_error(); + boost::mutex::scoped_lock lock(*tss_data_mutex); + try + { + tss_data_cleanup_handlers->push_back(pcleanup); + m_slot = tss_data_cleanup_handlers->size() - 1; + tss_data_inc_use(lock); + } + catch (...) + { + throw thread_resource_error(); + } +} + +tss::~tss() +{ + boost::mutex::scoped_lock lock(*tss_data_mutex); + tss_data_dec_use(lock); +} + +void* tss::get() const +{ + tss_slots* slots = get_slots(false); + + if (!slots) + return 0; + + if (m_slot >= slots->size()) + return 0; + + return (*slots)[m_slot]; +} + +void tss::set(void* value) +{ + tss_slots* slots = get_slots(true); + + if (!slots) + throw boost::thread_resource_error(); + + if (m_slot >= slots->size()) + { + try + { + slots->resize(m_slot + 1); + } + catch (...) + { + throw boost::thread_resource_error(); + } + } + + (*slots)[m_slot] = value; +} + +void tss::cleanup(void* value) +{ + boost::mutex::scoped_lock lock(*tss_data_mutex); + (*(*tss_data_cleanup_handlers)[m_slot])(value); +} + +} // namespace detail +} // namespace boost + +#endif //BOOST_THREAD_NO_TSS_CLEANUP diff --git a/src/tss_dll.cpp b/src/win32/tss_dll.cpp similarity index 100% rename from src/tss_dll.cpp rename to src/win32/tss_dll.cpp diff --git a/src/tss_hooks.cpp b/src/win32/tss_hooks.cpp similarity index 100% rename from src/tss_hooks.cpp rename to src/win32/tss_hooks.cpp diff --git a/src/tss_pe.cpp b/src/win32/tss_pe.cpp similarity index 100% rename from src/tss_pe.cpp rename to src/win32/tss_pe.cpp diff --git a/src/win32/xtime.cpp b/src/win32/xtime.cpp new file mode 100644 index 00000000..0ae854ff --- /dev/null +++ b/src/win32/xtime.cpp @@ -0,0 +1,158 @@ +// Copyright (C) 2001-2003 +// William E. Kempf +// +// 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) + +#include + +#if defined(BOOST_HAS_FTIME) +# define __STDC_CONSTANT_MACROS +#endif + +#include + +#if defined(BOOST_HAS_FTIME) +# include +# include +#elif defined(BOOST_HAS_GETTIMEOFDAY) +# include +#elif defined(BOOST_HAS_MPTASKS) +# include +# include +#endif + +#include + +namespace boost { + +#ifdef BOOST_HAS_MPTASKS + +namespace detail +{ + +using thread::force_cast; + +struct startup_time_info +{ + startup_time_info() + { + // 1970 Jan 1 at 00:00:00 + static const DateTimeRec k_sUNIXBase = {1970, 1, 1, 0, 0, 0, 0}; + static unsigned long s_ulUNIXBaseSeconds = 0UL; + + if(s_ulUNIXBaseSeconds == 0UL) + { + // calculate the number of seconds between the Mac OS base and the + // UNIX base the first time we enter this constructor. + DateToSeconds(&k_sUNIXBase, &s_ulUNIXBaseSeconds); + } + + unsigned long ulSeconds; + + // get the time in UpTime units twice, with the time in seconds in the + // middle. + uint64_t ullFirstUpTime = force_cast(UpTime()); + GetDateTime(&ulSeconds); + uint64_t ullSecondUpTime = force_cast(UpTime()); + + // calculate the midpoint of the two UpTimes, and save that. + uint64_t ullAverageUpTime = (ullFirstUpTime + ullSecondUpTime) / 2ULL; + m_sStartupAbsoluteTime = force_cast(ullAverageUpTime); + + // save the number of seconds, recentered at the UNIX base. + m_ulStartupSeconds = ulSeconds - s_ulUNIXBaseSeconds; + } + + AbsoluteTime m_sStartupAbsoluteTime; + UInt32 m_ulStartupSeconds; +}; + +static startup_time_info g_sStartupTimeInfo; + +} // namespace detail + +#endif + + +int xtime_get(struct xtime* xtp, int clock_type) +{ + if (clock_type == TIME_UTC) + { +#if defined(BOOST_HAS_FTIME) + FILETIME ft; +# if defined(BOOST_NO_GETSYSTEMTIMEASFILETIME) + { + SYSTEMTIME st; + GetSystemTime(&st); + SystemTimeToFileTime(&st,&ft); + } +# else + GetSystemTimeAsFileTime(&ft); +# endif + static const boost::uint64_t TIMESPEC_TO_FILETIME_OFFSET = + UINT64_C(116444736000000000); + + const boost::uint64_t ft64 = + (static_cast(ft.dwHighDateTime) << 32) + + ft.dwLowDateTime; + + xtp->sec = static_cast( + (ft64 - TIMESPEC_TO_FILETIME_OFFSET) / 10000000 + ); + + xtp->nsec = static_cast( + ((ft64 - TIMESPEC_TO_FILETIME_OFFSET) % 10000000) * 100 + ); + + return clock_type; +#elif defined(BOOST_HAS_GETTIMEOFDAY) + struct timeval tv; +# ifndef NDEBUG + int res = +#endif + gettimeofday(&tv, 0); + assert(0 == res); + assert(tv.tv_sec >= 0); + assert(tv.tv_usec >= 0); + xtp->sec = tv.tv_sec; + xtp->nsec = tv.tv_usec * 1000; + return clock_type; +#elif defined(BOOST_HAS_CLOCK_GETTIME) + timespec ts; +# ifndef NDEBUG + int res = +# endif + clock_gettime(CLOCK_REALTIME, &ts); + assert(0 == res); + xtp->sec = ts.tv_sec; + xtp->nsec = ts.tv_nsec; + return clock_type; +#elif defined(BOOST_HAS_MPTASKS) + using detail::thread::force_cast; + // the Mac OS does not have an MP-safe way of getting the date/time, + // so we use a delta from the startup time. We _could_ defer this + // and use something that is interrupt-safe, but this would be _SLOW_, + // and we need speed here. + const uint64_t k_ullNanosecondsPerSecond(1000ULL * 1000ULL * 1000ULL); + AbsoluteTime sUpTime(UpTime()); + uint64_t ullNanoseconds( + force_cast( + AbsoluteDeltaToNanoseconds(sUpTime, + detail::g_sStartupTimeInfo.m_sStartupAbsoluteTime))); + uint64_t ullSeconds = (ullNanoseconds / k_ullNanosecondsPerSecond); + ullNanoseconds -= (ullSeconds * k_ullNanosecondsPerSecond); + xtp->sec = detail::g_sStartupTimeInfo.m_ulStartupSeconds + ullSeconds; + xtp->nsec = ullNanoseconds; + return clock_type; +#else +# error "xtime_get implementation undefined" +#endif + } + return 0; +} + +} // namespace boost + +// Change Log: +// 8 Feb 01 WEKEMPF Initial version. diff --git a/test/test_shared_mutex.cpp b/test/test_shared_mutex.cpp index 763758d3..14058180 100644 --- a/test/test_shared_mutex.cpp +++ b/test/test_shared_mutex.cpp @@ -9,6 +9,8 @@ #include #include #include "util.inl" +#include +#include #define CHECK_LOCKED_VALUE_EQUAL(mutex_name,value,expected_value) \ { \ @@ -37,10 +39,10 @@ namespace unsigned& max_simultaneous_running_): rw_mutex(rw_mutex_), unblocked_count(unblocked_count_), - unblocked_count_mutex(unblocked_count_mutex_), - finish_mutex(finish_mutex_), simultaneous_running_count(simultaneous_running_count_), - max_simultaneous_running(max_simultaneous_running_) + max_simultaneous_running(max_simultaneous_running_), + unblocked_count_mutex(unblocked_count_mutex_), + finish_mutex(finish_mutex_) {} void operator()() @@ -129,7 +131,7 @@ void test_only_one_writer_permitted() pool.join_all(); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,number_of_threads); - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1u); } void test_reader_blocks_writer() @@ -156,7 +158,7 @@ void test_reader_blocks_writer() pool.join_all(); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,2U); - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1u); } void test_unlocking_writer_unblocks_all_readers() @@ -218,19 +220,19 @@ void test_unlocking_last_reader_only_unblocks_one_writer() { pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_writing_mutex,simultaneous_running_writers,max_simultaneous_writers)); } - boost::thread::sleep(delay(1)); + boost::thread::sleep(delay(2)); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count); finish_reading_lock.unlock(); - boost::thread::sleep(delay(1)); + boost::thread::sleep(delay(2)); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); finish_writing_lock.unlock(); pool.join_all(); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+writer_count); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_readers,reader_count); - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_writers,1); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_writers,1u); } void test_only_one_upgrade_lock_permitted() @@ -261,7 +263,7 @@ void test_only_one_upgrade_lock_permitted() pool.join_all(); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,number_of_threads); - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1u); } void test_can_lock_upgrade_if_currently_locked_shared() @@ -334,7 +336,7 @@ void test_if_other_thread_has_write_lock_try_lock_shared_returns_false() boost::mutex::scoped_lock finish_lock(finish_mutex); boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); boost::thread::sleep(delay(1)); - CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); bool const try_succeeded=rw_mutex.try_lock_shared(); BOOST_CHECK(!try_succeeded); @@ -400,7 +402,7 @@ void test_if_other_thread_has_shared_lock_try_lock_shared_returns_true() boost::mutex::scoped_lock finish_lock(finish_mutex); boost::thread writer(simple_reading_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); boost::thread::sleep(delay(1)); - CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); bool const try_succeeded=rw_mutex.try_lock_shared(); BOOST_CHECK(try_succeeded); @@ -422,12 +424,18 @@ void test_timed_lock_shared_times_out_if_write_lock_held() boost::mutex::scoped_lock finish_lock(finish_mutex); boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); boost::thread::sleep(delay(1)); - CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); boost::system_time const start=boost::get_system_time(); boost::system_time const timeout=start+boost::posix_time::milliseconds(100); bool const timed_lock_succeeded=rw_mutex.timed_lock_shared(timeout); - BOOST_CHECK(timeout<=boost::get_system_time()); + boost::system_time const wakeup=boost::get_system_time(); + BOOST_CHECK(timeout<=(wakeup+boost::posix_time::milliseconds(1))); + if(timeout>wakeup) + { + std::cout<<"timeout="< #include #include #include +#include #include #include #include #include +#include +#include -namespace boost { +namespace boost +{ class thread; @@ -78,40 +81,198 @@ namespace boost { }; } - struct xtime; - class BOOST_THREAD_DECL thread : private noncopyable + class thread_cancelled + {}; + + namespace detail { + struct thread_exit_callback_node; + + struct thread_data_base + { + boost::shared_ptr self; + pthread_t thread_handle; + boost::mutex done_mutex; + boost::condition_variable done_condition; + bool done; + bool join_started; + bool joined; + boost::detail::thread_exit_callback_node* thread_exit_callbacks; + bool cancel_enabled; + + thread_data_base(): + done(false),join_started(false),joined(false), + thread_exit_callbacks(0), + cancel_enabled(true) + {} + virtual ~thread_data_base() + {} + + virtual void run()=0; + }; + } + + struct xtime; + class BOOST_THREAD_DECL thread + { + private: + thread(thread&); + thread& operator=(thread&); + + template + struct thread_data: + detail::thread_data_base + { + F f; + + thread_data(F f_): + f(f_) + {} + thread_data(boost::move_t f_): + f(f_) + {} + + void run() + { + f(); + } + }; + + mutable boost::mutex thread_info_mutex; + boost::shared_ptr thread_info; + + void start_thread(); + + explicit thread(boost::shared_ptr data); + + boost::shared_ptr get_thread_info() const; + public: thread(); - explicit thread(const function0& threadfunc); ~thread(); + + template + explicit thread(F f): + thread_info(new thread_data(f)) + { + start_thread(); + } + template + explicit thread(boost::move_t f): + thread_info(new thread_data(f)) + { + start_thread(); + } - bool operator==(const thread& other) const; - bool operator!=(const thread& other) const; + explicit thread(boost::move_t x); + thread& operator=(boost::move_t x); + operator boost::move_t(); + boost::move_t move(); - void join(); - - static void sleep(const xtime& xt); - static void yield(); + void swap(thread& x); typedef detail::thread_id id; - id get_id() const - { - return m_id; - } + id get_id() const; - private: - id m_id; - bool m_joinable; + bool joinable() const; + void join(); + void detach(); + + static unsigned hardware_concurrency(); + + // backwards compatibility + bool operator==(const thread& other) const; + bool operator!=(const thread& other) const; + + static void sleep(const system_time& xt); + static void yield(); + + // extensions + class cancel_handle; + cancel_handle get_cancel_handle() const; + void cancel(); + bool cancellation_requested() const; + + static thread self(); }; namespace this_thread { + class disable_cancellation + { + disable_cancellation(const disable_cancellation&); + disable_cancellation& operator=(const disable_cancellation&); + + bool cancel_was_enabled; + friend class enable_cancellation; + public: + disable_cancellation(); + ~disable_cancellation(); + }; + + class enable_cancellation + { + enable_cancellation(const enable_cancellation&); + enable_cancellation& operator=(const enable_cancellation&); + public: + explicit enable_cancellation(disable_cancellation& d); + ~enable_cancellation(); + }; + inline thread::id get_id() { return thread::id(pthread_self()); } + + void BOOST_THREAD_DECL cancellation_point(); + bool BOOST_THREAD_DECL cancellation_enabled(); + bool BOOST_THREAD_DECL cancellation_requested(); + + void BOOST_THREAD_DECL yield(); + template + void sleep(TimeDuration const& rel_time) + { + thread::sleep(get_system_time()+rel_time); + } + } + + namespace detail + { + struct thread_exit_function_base + { + virtual ~thread_exit_function_base() + {} + virtual void operator()() const=0; + }; + + template + struct thread_exit_function: + thread_exit_function_base + { + F f; + + thread_exit_function(F f_): + f(f_) + {} + + void operator()() const + { + f(); + } + }; + + void add_thread_exit_function(thread_exit_function_base*); + } + + namespace this_thread + { + template + void at_thread_exit(F f) + { + detail::thread_exit_function_base* const thread_exit_func=new detail::thread_exit_function(f); + detail::add_thread_exit_function(thread_exit_func); + } } class BOOST_THREAD_DECL thread_group : private noncopyable @@ -133,4 +294,4 @@ namespace boost { } // namespace boost -#endif // BOOST_THREAD_WEK070601_HPP +#endif diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index c5b99ced..8e143f8e 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -20,7 +20,7 @@ namespace boost { - class thread_canceled + class thread_cancelled {}; namespace detail @@ -105,19 +105,19 @@ namespace boost ~thread(); template - thread(F f): + explicit thread(F f): thread_info(detail::heap_new >(f)) { start_thread(); } template - thread(boost::move_t f): + explicit thread(boost::move_t f): thread_info(detail::heap_new >(f)) { start_thread(); } - thread(boost::move_t x); + explicit thread(boost::move_t x); thread& operator=(boost::move_t x); operator boost::move_t(); boost::move_t move(); diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index c4757d29..a1827324 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -11,213 +11,339 @@ #include #include #include -#include +#include #include "timeconv.inl" -namespace { - -class thread_param +namespace boost { -public: - thread_param(const boost::function0& threadfunc) - : m_threadfunc(threadfunc), m_started(false) + namespace detail { + struct thread_exit_callback_node + { + boost::detail::thread_exit_function_base* func; + thread_exit_callback_node* next; + }; } - void wait() + namespace { - boost::mutex::scoped_lock scoped_lock(m_mutex); - while (!m_started) - m_condition.wait(scoped_lock); - } - void started() - { - boost::mutex::scoped_lock scoped_lock(m_mutex); - m_started = true; - m_condition.notify_one(); + boost::once_flag current_thread_tls_init_flag=BOOST_ONCE_INIT; + pthread_key_t current_thread_tls_key; + + extern "C" + { + void tls_destructor(void* data) + { + boost::detail::thread_data_base* thread_info=static_cast(data); + if(thread_info) + { + while(thread_info->thread_exit_callbacks) + { + boost::detail::thread_exit_callback_node* const current_node=thread_info->thread_exit_callbacks; + thread_info->thread_exit_callbacks=current_node->next; + if(current_node->func) + { + (*current_node->func)(); + delete current_node->func; + } + delete current_node; + } + } + } + } + + + void create_current_thread_tls_key() + { + int const res=pthread_key_create(¤t_thread_tls_key,NULL); + BOOST_ASSERT(!res); + } + + boost::detail::thread_data_base* get_current_thread_data() + { + boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); + return (boost::detail::thread_data_base*)pthread_getspecific(current_thread_tls_key); + } + + void set_current_thread_data(detail::thread_data_base* new_data) + { + boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); + int const res=pthread_setspecific(current_thread_tls_key,new_data); + BOOST_ASSERT(!res); + } + + + extern "C" + { + void* thread_proxy(void* param) + { + boost::shared_ptr thread_info = static_cast(param)->self; + thread_info->self.reset(); + set_current_thread_data(thread_info.get()); + try + { + thread_info->run(); + } + catch(thread_cancelled const&) + { + } + catch(...) + { + std::terminate(); + } + + tls_destructor(thread_info.get()); + set_current_thread_data(0); + boost::lock_guard lock(thread_info->done_mutex); + thread_info->done=true; + thread_info->done_condition.notify_all(); + return 0; + } + + } } - boost::mutex m_mutex; - boost::condition m_condition; - const boost::function0& m_threadfunc; - bool m_started; -}; -} // unnamed namespace + thread::thread() + {} -extern "C" { - static void* thread_proxy(void* param) + void thread::start_thread() { - thread_param* p = static_cast(param); - boost::function0 threadfunc = p->m_threadfunc; - p->started(); - threadfunc(); - return 0; + thread_info->self=thread_info; + int const res = pthread_create(&thread_info->thread_handle, 0, &thread_proxy, thread_info.get()); + if (res != 0) + { + thread_info->self.reset(); + throw thread_resource_error(); + } } -} - -namespace boost { - -thread::thread(): - m_joinable(false) -{} - -thread::thread(const function0& threadfunc) - : m_joinable(true) -{ - thread_param param(threadfunc); - int res = 0; - pthread_t m_thread; - res = pthread_create(&m_thread, 0, &thread_proxy, ¶m); - if (res != 0) - throw thread_resource_error(); - m_id=m_thread; - param.wait(); -} - -thread::~thread() -{ - if (m_joinable) + thread::~thread() { - pthread_detach(*m_id.id); + detach(); } -} -bool thread::operator==(const thread& other) const -{ - return m_id==other.m_id; -} - -bool thread::operator!=(const thread& other) const -{ - return !operator==(other); -} - -void thread::join() -{ - assert(m_joinable); //See race condition comment below - int res = 0; - res = pthread_join(*m_id.id, 0); - assert(res == 0); - // This isn't a race condition since any race that could occur would - // have us in undefined behavior territory any way. - m_joinable = false; -} - -void thread::sleep(const xtime& xt) -{ - for (int foo=0; foo < 5; ++foo) + bool thread::operator==(const thread& other) const { + return get_id()==other.get_id(); + } + + bool thread::operator!=(const thread& other) const + { + return !operator==(other); + } + + boost::shared_ptr thread::get_thread_info() const + { + lock_guard l(thread_info_mutex); + return thread_info; + } + + void thread::join() + { + boost::shared_ptr const local_thread_info=get_thread_info(); + if(local_thread_info) + { + bool do_join=false; + + { + unique_lock lock(local_thread_info->done_mutex); + while(!local_thread_info->done) + { + local_thread_info->done_condition.wait(lock); + } + do_join=!local_thread_info->join_started; + + if(do_join) + { + local_thread_info->join_started=true; + } + else + { + while(!local_thread_info->joined) + { + local_thread_info->done_condition.wait(lock); + } + } + } + if(do_join) + { + void* result=0; + int const res=pthread_join(local_thread_info->thread_handle,&result); + BOOST_ASSERT(!res); + lock_guard lock(local_thread_info->done_mutex); + local_thread_info->joined=true; + local_thread_info->done_condition.notify_all(); + } + + lock_guard l1(thread_info_mutex); + if(thread_info==local_thread_info) + { + thread_info.reset(); + } + } + } + + bool thread::joinable() const + { + return get_thread_info(); + } + + + void thread::detach() + { + boost::shared_ptr local_thread_info; + { + lock_guard l1(thread_info_mutex); + thread_info.swap(local_thread_info); + } + + if(local_thread_info) + { + lock_guard lock(local_thread_info->done_mutex); + if(!local_thread_info->join_started) + { + int const res=pthread_detach(local_thread_info->thread_handle); + BOOST_ASSERT(!res); + local_thread_info->join_started=true; + local_thread_info->joined=true; + } + } + } + + void thread::sleep(const system_time& st) + { + xtime const xt=get_xtime(st); + + for (int foo=0; foo < 5; ++foo) + { # if defined(BOOST_HAS_PTHREAD_DELAY_NP) - timespec ts; - to_timespec_duration(xt, ts); - int res = 0; - res = pthread_delay_np(&ts); - assert(res == 0); + timespec ts; + to_timespec_duration(xt, ts); + int res = 0; + res = pthread_delay_np(&ts); + BOOST_ASSERT(res == 0); # elif defined(BOOST_HAS_NANOSLEEP) - timespec ts; - to_timespec_duration(xt, ts); + timespec ts; + to_timespec_duration(xt, ts); - // nanosleep takes a timespec that is an offset, not - // an absolute time. - nanosleep(&ts, 0); + // nanosleep takes a timespec that is an offset, not + // an absolute time. + nanosleep(&ts, 0); # else - mutex mx; - mutex::scoped_lock lock(mx); - condition cond; - cond.timed_wait(lock, xt); + mutex mx; + mutex::scoped_lock lock(mx); + condition cond; + cond.timed_wait(lock, xt); # endif - xtime cur; - xtime_get(&cur, TIME_UTC); - if (xtime_cmp(xt, cur) <= 0) - return; + xtime cur; + xtime_get(&cur, TIME_UTC); + if (xtime_cmp(xt, cur) <= 0) + return; + } } -} -void thread::yield() -{ + void thread::yield() + { # if defined(BOOST_HAS_SCHED_YIELD) - int res = 0; - res = sched_yield(); - assert(res == 0); + int res = 0; + res = sched_yield(); + BOOST_ASSERT(res == 0); # elif defined(BOOST_HAS_PTHREAD_YIELD) - int res = 0; - res = pthread_yield(); - assert(res == 0); + int res = 0; + res = pthread_yield(); + BOOST_ASSERT(res == 0); # else - xtime xt; - xtime_get(&xt, TIME_UTC); - sleep(xt); + xtime xt; + xtime_get(&xt, TIME_UTC); + sleep(xt); # endif -} - -thread_group::thread_group() -{ -} - -thread_group::~thread_group() -{ - // We shouldn't have to scoped_lock here, since referencing this object - // from another thread while we're deleting it in the current thread is - // going to lead to undefined behavior any way. - for (std::list::iterator it = m_threads.begin(); - it != m_threads.end(); ++it) - { - delete (*it); } -} -thread* thread_group::create_thread(const function0& threadfunc) -{ - // No scoped_lock required here since the only "shared data" that's - // modified here occurs inside add_thread which does scoped_lock. - std::auto_ptr thrd(new thread(threadfunc)); - add_thread(thrd.get()); - return thrd.release(); -} - -void thread_group::add_thread(thread* thrd) -{ - mutex::scoped_lock scoped_lock(m_mutex); - - // For now we'll simply ignore requests to add a thread object multiple - // times. Should we consider this an error and either throw or return an - // error value? - std::list::iterator it = std::find(m_threads.begin(), - m_threads.end(), thrd); - assert(it == m_threads.end()); - if (it == m_threads.end()) - m_threads.push_back(thrd); -} - -void thread_group::remove_thread(thread* thrd) -{ - mutex::scoped_lock scoped_lock(m_mutex); - - // For now we'll simply ignore requests to remove a thread object that's - // not in the group. Should we consider this an error and either throw or - // return an error value? - std::list::iterator it = std::find(m_threads.begin(), - m_threads.end(), thrd); - assert(it != m_threads.end()); - if (it != m_threads.end()) - m_threads.erase(it); -} - -void thread_group::join_all() -{ - mutex::scoped_lock scoped_lock(m_mutex); - for (std::list::iterator it = m_threads.begin(); - it != m_threads.end(); ++it) + unsigned thread::hardware_concurrency() { - (*it)->join(); + return 1; } -} -int thread_group::size() const -{ + thread::id thread::get_id() const + { + boost::shared_ptr local_thread_info=get_thread_info(); + if(local_thread_info) + { + return id(local_thread_info->thread_handle); + } + else + { + return id(); + } + } + + thread_group::thread_group() + { + } + + thread_group::~thread_group() + { + // We shouldn't have to scoped_lock here, since referencing this object + // from another thread while we're deleting it in the current thread is + // going to lead to undefined behavior any way. + for (std::list::iterator it = m_threads.begin(); + it != m_threads.end(); ++it) + { + delete (*it); + } + } + + thread* thread_group::create_thread(const function0& threadfunc) + { + // No scoped_lock required here since the only "shared data" that's + // modified here occurs inside add_thread which does scoped_lock. + std::auto_ptr thrd(new thread(threadfunc)); + add_thread(thrd.get()); + return thrd.release(); + } + + void thread_group::add_thread(thread* thrd) + { + mutex::scoped_lock scoped_lock(m_mutex); + + // For now we'll simply ignore requests to add a thread object multiple + // times. Should we consider this an error and either throw or return an + // error value? + std::list::iterator it = std::find(m_threads.begin(), + m_threads.end(), thrd); + BOOST_ASSERT(it == m_threads.end()); + if (it == m_threads.end()) + m_threads.push_back(thrd); + } + + void thread_group::remove_thread(thread* thrd) + { + mutex::scoped_lock scoped_lock(m_mutex); + + // For now we'll simply ignore requests to remove a thread object that's + // not in the group. Should we consider this an error and either throw or + // return an error value? + std::list::iterator it = std::find(m_threads.begin(), + m_threads.end(), thrd); + BOOST_ASSERT(it != m_threads.end()); + if (it != m_threads.end()) + m_threads.erase(it); + } + + void thread_group::join_all() + { + mutex::scoped_lock scoped_lock(m_mutex); + for (std::list::iterator it = m_threads.begin(); + it != m_threads.end(); ++it) + { + (*it)->join(); + } + } + + int thread_group::size() const + { return m_threads.size(); -} + } -} // namespace boost +} diff --git a/src/pthread/tss.cpp b/src/pthread/tss.cpp index 33f635b9..1163054d 100644 --- a/src/pthread/tss.cpp +++ b/src/pthread/tss.cpp @@ -30,13 +30,7 @@ typedef std::vector*> tss_data_cleanup_handlers_ty boost::once_flag tss_data_once = BOOST_ONCE_INIT; boost::mutex* tss_data_mutex = 0; tss_data_cleanup_handlers_type* tss_data_cleanup_handlers = 0; -#if defined(BOOST_HAS_WINTHREADS) - DWORD tss_data_native_key=TLS_OUT_OF_INDEXES; -#elif defined(BOOST_HAS_PTHREADS) pthread_key_t tss_data_native_key; -#elif defined(BOOST_HAS_MPTASKS) - TaskStorageIndex tss_data_native_key; -#endif int tss_data_use = 0; void tss_data_inc_use(boost::mutex::scoped_lock& lk) @@ -58,15 +52,7 @@ void tss_data_dec_use(boost::mutex::scoped_lock& lk) lk.unlock(); delete tss_data_mutex; tss_data_mutex = 0; -#if defined(BOOST_HAS_WINTHREADS) - TlsFree(tss_data_native_key); - tss_data_native_key=TLS_OUT_OF_INDEXES; -#elif defined(BOOST_HAS_PTHREADS) pthread_key_delete(tss_data_native_key); -#elif defined(BOOST_HAS_MPTASKS) - // Don't know what to put here. - // But MPTASKS isn't currently maintained anyways... -#endif } } @@ -79,9 +65,6 @@ extern "C" void cleanup_slots(void* p) (*(*tss_data_cleanup_handlers)[i])((*slots)[i]); (*slots)[i] = 0; } -#if defined(BOOST_HAS_WINTHREADS) - TlsSetValue(tss_data_native_key,0); -#endif tss_data_dec_use(lock); delete slots; } @@ -95,23 +78,9 @@ void init_tss_data() if (temp_mutex.get() == 0) throw boost::thread_resource_error(); -#if defined(BOOST_HAS_WINTHREADS) - //Force the cleanup implementation library to be linked in - tss_cleanup_implemented(); - - //Allocate tls slot - tss_data_native_key = TlsAlloc(); - if (tss_data_native_key == TLS_OUT_OF_INDEXES) - return; -#elif defined(BOOST_HAS_PTHREADS) int res = pthread_key_create(&tss_data_native_key, &cleanup_slots); if (res != 0) return; -#elif defined(BOOST_HAS_MPTASKS) - OSStatus status = MPAllocateTaskStorageIndex(&tss_data_native_key); - if (status != noErr) - return; -#endif // The life time of cleanup handlers and mutex is beeing // managed by a reference counting technique. @@ -123,48 +92,20 @@ void init_tss_data() tss_data_mutex = temp_mutex.release(); } -#if defined(BOOST_HAS_WINTHREADS) -tss_slots* get_slots(bool alloc); - -void __cdecl tss_thread_exit() -{ - tss_slots* slots = get_slots(false); - if (slots) - cleanup_slots(slots); -} -#endif tss_slots* get_slots(bool alloc) { tss_slots* slots = 0; -#if defined(BOOST_HAS_WINTHREADS) - slots = static_cast( - TlsGetValue(tss_data_native_key)); -#elif defined(BOOST_HAS_PTHREADS) slots = static_cast( pthread_getspecific(tss_data_native_key)); -#elif defined(BOOST_HAS_MPTASKS) - slots = static_cast( - MPGetTaskStorageValue(tss_data_native_key)); -#endif if (slots == 0 && alloc) { std::auto_ptr temp(new tss_slots); -#if defined(BOOST_HAS_WINTHREADS) - if (at_thread_exit(&tss_thread_exit) == -1) - return 0; - if (!TlsSetValue(tss_data_native_key, temp.get())) - return 0; -#elif defined(BOOST_HAS_PTHREADS) if (pthread_setspecific(tss_data_native_key, temp.get()) != 0) return 0; -#elif defined(BOOST_HAS_MPTASKS) - if (MPSetTaskStorageValue(tss_data_native_key, temp.get()) != noErr) - return 0; -#endif { boost::mutex::scoped_lock lock(*tss_data_mutex); tss_data_inc_use(lock); diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index 2110c038..94462b94 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -88,7 +88,7 @@ namespace boost { thread_info->run(); } - catch(thread_canceled const&) + catch(thread_cancelled const&) { } catch(...) @@ -302,7 +302,7 @@ namespace boost else if(notified_index==cancel_index) { detail::win32::ResetEvent(get_current_thread_data()->cancel_handle); - throw thread_canceled(); + throw thread_cancelled(); } } else @@ -322,7 +322,7 @@ namespace boost if(cancellation_enabled() && cancellation_requested()) { detail::win32::ResetEvent(get_current_thread_data()->cancel_handle); - throw thread_canceled(); + throw thread_cancelled(); } } From ee3d772235bf4300a8b66337f849ca7ace5bc3a1 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 25 Oct 2007 07:17:20 +0000 Subject: [PATCH 023/214] thread move constructor is not explicit, so self() compiles for MSVC8 and Intel; thread_exit_callback_node constructor added to remove warnings on MSVC8; thread destructor no longer calls cancel [SVN r40456] --- include/boost/thread/pthread/thread.hpp | 6 ++---- include/boost/thread/win32/thread.hpp | 2 +- src/win32/thread.cpp | 12 ++++++++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index 199f3b0c..76b503ee 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -150,7 +150,7 @@ namespace boost public: thread(); ~thread(); - + template explicit thread(F f): thread_info(new thread_data(f)) @@ -158,7 +158,7 @@ namespace boost start_thread(); } template - explicit thread(boost::move_t f): + thread(boost::move_t f): thread_info(new thread_data(f)) { start_thread(); @@ -193,8 +193,6 @@ namespace boost cancel_handle get_cancel_handle() const; void cancel(); bool cancellation_requested() const; - - static thread self(); }; namespace this_thread diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 8e143f8e..766b2068 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -117,7 +117,7 @@ namespace boost start_thread(); } - explicit thread(boost::move_t x); + thread(boost::move_t x); thread& operator=(boost::move_t x); operator boost::move_t(); boost::move_t move(); diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index 94462b94..88ec8b88 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -162,7 +162,6 @@ namespace boost thread::~thread() { - cancel(); detach(); } @@ -381,6 +380,11 @@ namespace boost { boost::detail::thread_exit_function_base* func; thread_exit_callback_node* next; + + thread_exit_callback_node(boost::detail::thread_exit_function_base* func_, + thread_exit_callback_node* next_): + func(func_),next(next_) + {} }; } namespace @@ -434,9 +438,9 @@ namespace boost { void add_thread_exit_function(thread_exit_function_base* func) { - thread_exit_callback_node* const new_node=heap_new(); - new_node->func=func; - new_node->next=get_current_thread_data()->thread_exit_callbacks; + thread_exit_callback_node* const new_node= + heap_new(func, + get_current_thread_data()->thread_exit_callbacks); get_current_thread_data()->thread_exit_callbacks=new_node; } } From afb6684bde84af1529dd5169bd4098aa58c0b3e8 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 26 Oct 2007 07:33:22 +0000 Subject: [PATCH 024/214] added tests for cancellation [SVN r40472] --- include/boost/thread/barrier.hpp | 4 +- include/boost/thread/pthread/thread.hpp | 26 +++++--- include/boost/thread/win32/thread.hpp | 12 ++-- src/pthread/thread.cpp | 89 +++++++++++++++++++++++-- src/win32/thread.cpp | 4 +- test/test_once.cpp | 6 +- test/test_thread.cpp | 54 ++++++++++++++- 7 files changed, 167 insertions(+), 28 deletions(-) diff --git a/include/boost/thread/barrier.hpp b/include/boost/thread/barrier.hpp index 79a22e66..665e3b03 100644 --- a/include/boost/thread/barrier.hpp +++ b/include/boost/thread/barrier.hpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include @@ -48,7 +48,7 @@ namespace boost private: mutex m_mutex; - condition m_cond; + condition_variable m_cond; unsigned int m_threshold; unsigned int m_count; unsigned int m_generation; diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index 76b503ee..ffc89b50 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -92,18 +92,20 @@ namespace boost { boost::shared_ptr self; pthread_t thread_handle; - boost::mutex done_mutex; + boost::mutex data_mutex; boost::condition_variable done_condition; bool done; bool join_started; bool joined; boost::detail::thread_exit_callback_node* thread_exit_callbacks; bool cancel_enabled; + bool cancel_requested; thread_data_base(): done(false),join_started(false),joined(false), thread_exit_callbacks(0), - cancel_enabled(true) + cancel_enabled(true), + cancel_requested(false) {} virtual ~thread_data_base() {} @@ -203,19 +205,19 @@ namespace boost disable_cancellation& operator=(const disable_cancellation&); bool cancel_was_enabled; - friend class enable_cancellation; + friend class restore_cancellation; public: disable_cancellation(); ~disable_cancellation(); }; - class enable_cancellation + class restore_cancellation { - enable_cancellation(const enable_cancellation&); - enable_cancellation& operator=(const enable_cancellation&); + restore_cancellation(const restore_cancellation&); + restore_cancellation& operator=(const restore_cancellation&); public: - explicit enable_cancellation(disable_cancellation& d); - ~enable_cancellation(); + explicit restore_cancellation(disable_cancellation& d); + ~restore_cancellation(); }; inline thread::id get_id() @@ -227,9 +229,13 @@ namespace boost bool BOOST_THREAD_DECL cancellation_enabled(); bool BOOST_THREAD_DECL cancellation_requested(); - void BOOST_THREAD_DECL yield(); + inline void yield() + { + thread::yield(); + } + template - void sleep(TimeDuration const& rel_time) + inline void sleep(TimeDuration const& rel_time) { thread::sleep(get_system_time()+rel_time); } diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 766b2068..3ebfe5eb 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -161,19 +161,19 @@ namespace boost disable_cancellation& operator=(const disable_cancellation&); bool cancel_was_enabled; - friend class enable_cancellation; + friend class restore_cancellation; public: disable_cancellation(); ~disable_cancellation(); }; - class enable_cancellation + class restore_cancellation { - enable_cancellation(const enable_cancellation&); - enable_cancellation& operator=(const enable_cancellation&); + restore_cancellation(const restore_cancellation&); + restore_cancellation& operator=(const restore_cancellation&); public: - explicit enable_cancellation(disable_cancellation& d); - ~enable_cancellation(); + explicit restore_cancellation(disable_cancellation& d); + ~restore_cancellation(); }; thread::id BOOST_THREAD_DECL get_id(); diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index a1827324..bc9b6828 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -94,7 +94,7 @@ namespace boost tls_destructor(thread_info.get()); set_current_thread_data(0); - boost::lock_guard lock(thread_info->done_mutex); + boost::lock_guard lock(thread_info->data_mutex); thread_info->done=true; thread_info->done_condition.notify_all(); return 0; @@ -147,7 +147,7 @@ namespace boost bool do_join=false; { - unique_lock lock(local_thread_info->done_mutex); + unique_lock lock(local_thread_info->data_mutex); while(!local_thread_info->done) { local_thread_info->done_condition.wait(lock); @@ -171,7 +171,7 @@ namespace boost void* result=0; int const res=pthread_join(local_thread_info->thread_handle,&result); BOOST_ASSERT(!res); - lock_guard lock(local_thread_info->done_mutex); + lock_guard lock(local_thread_info->data_mutex); local_thread_info->joined=true; local_thread_info->done_condition.notify_all(); } @@ -200,7 +200,7 @@ namespace boost if(local_thread_info) { - lock_guard lock(local_thread_info->done_mutex); + lock_guard lock(local_thread_info->data_mutex); if(!local_thread_info->join_started) { int const res=pthread_detach(local_thread_info->thread_handle); @@ -278,6 +278,87 @@ namespace boost } } + void thread::cancel() + { + boost::shared_ptr local_thread_info=get_thread_info(); + if(local_thread_info) + { + lock_guard lk(local_thread_info->data_mutex); + local_thread_info->cancel_requested=true; + } + } + + + namespace this_thread + { + void cancellation_point() + { + boost::detail::thread_data_base* const thread_info=get_current_thread_data(); + if(thread_info && thread_info->cancel_enabled) + { + lock_guard lg(thread_info->data_mutex); + if(thread_info->cancel_requested) + { + thread_info->cancel_requested=false; + throw thread_cancelled(); + } + } + } + + bool cancellation_enabled() + { + boost::detail::thread_data_base* const thread_info=get_current_thread_data(); + return thread_info && thread_info->cancel_enabled; + } + + bool cancellation_requested() + { + boost::detail::thread_data_base* const thread_info=get_current_thread_data(); + if(!thread_info) + { + return false; + } + else + { + lock_guard lg(thread_info->data_mutex); + return thread_info->cancel_requested; + } + } + + disable_cancellation::disable_cancellation(): + cancel_was_enabled(cancellation_enabled()) + { + if(cancel_was_enabled) + { + get_current_thread_data()->cancel_enabled=false; + } + } + + disable_cancellation::~disable_cancellation() + { + if(get_current_thread_data()) + { + get_current_thread_data()->cancel_enabled=cancel_was_enabled; + } + } + + restore_cancellation::restore_cancellation(disable_cancellation& d) + { + if(d.cancel_was_enabled) + { + get_current_thread_data()->cancel_enabled=true; + } + } + + restore_cancellation::~restore_cancellation() + { + if(get_current_thread_data()) + { + get_current_thread_data()->cancel_enabled=false; + } + } + } + thread_group::thread_group() { } diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index 88ec8b88..cb802261 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -357,7 +357,7 @@ namespace boost } } - enable_cancellation::enable_cancellation(disable_cancellation& d) + restore_cancellation::restore_cancellation(disable_cancellation& d) { if(d.cancel_was_enabled) { @@ -365,7 +365,7 @@ namespace boost } } - enable_cancellation::~enable_cancellation() + restore_cancellation::~restore_cancellation() { if(get_current_thread_data()) { diff --git a/test/test_once.cpp b/test/test_once.cpp index eb9510d8..24b1affd 100644 --- a/test/test_once.cpp +++ b/test/test_once.cpp @@ -38,7 +38,7 @@ void call_once_thread() void test_call_once() { - unsigned const num_threads=100; + unsigned const num_threads=20; boost::thread_group group; for(unsigned i=0;i #include +#include #include @@ -59,7 +60,7 @@ void test_creation() void do_test_id_comparison() { boost::thread::id const self=boost::this_thread::get_id(); - boost::thread thrd(bind(&comparison_thread, self)); + boost::thread thrd(boost::bind(&comparison_thread, self)); thrd.join(); } @@ -68,6 +69,55 @@ void test_id_comparison() timed_test(&do_test_id_comparison, 1); } +void cancellation_point_thread(boost::mutex* m,bool* failed) +{ + boost::mutex::scoped_lock lk(*m); + boost::this_thread::cancellation_point(); + *failed=true; +} + +void do_test_thread_cancels_at_cancellation_point() +{ + boost::mutex m; + bool failed=false; + boost::mutex::scoped_lock lk(m); + boost::thread thrd(boost::bind(&cancellation_point_thread,&m,&failed)); + thrd.cancel(); + lk.unlock(); + thrd.join(); + BOOST_CHECK(!failed); +} + +void test_thread_cancels_at_cancellation_point() +{ + timed_test(&do_test_thread_cancels_at_cancellation_point, 1); +} + +void disabled_cancellation_point_thread(boost::mutex* m,bool* failed) +{ + boost::mutex::scoped_lock lk(*m); + boost::this_thread::disable_cancellation dc; + boost::this_thread::cancellation_point(); + *failed=false; +} + +void do_test_thread_no_cancel_if_cancels_disabled_at_cancellation_point() +{ + boost::mutex m; + bool failed=true; + boost::mutex::scoped_lock lk(m); + boost::thread thrd(boost::bind(&disabled_cancellation_point_thread,&m,&failed)); + thrd.cancel(); + lk.unlock(); + thrd.join(); + BOOST_CHECK(!failed); +} + +void test_thread_no_cancel_if_cancels_disabled_at_cancellation_point() +{ + timed_test(&do_test_thread_no_cancel_if_cancels_disabled_at_cancellation_point, 1); +} + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = @@ -76,6 +126,8 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) test->add(BOOST_TEST_CASE(test_sleep)); test->add(BOOST_TEST_CASE(test_creation)); test->add(BOOST_TEST_CASE(test_id_comparison)); + test->add(BOOST_TEST_CASE(test_thread_cancels_at_cancellation_point)); + test->add(BOOST_TEST_CASE(test_thread_no_cancel_if_cancels_disabled_at_cancellation_point)); return test; } From c7f963f57ea7263cfd6734e538c50c3c91bfa202 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 26 Oct 2007 09:04:25 +0000 Subject: [PATCH 025/214] Make sure every library can be installed by using bjam stage|install in libs//build. [SVN r40475] --- build/Jamfile.v2 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 76d0f446..5069f840 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -80,4 +80,5 @@ lib boost_thread pthread $(BOOST_PTHREAD_OPTS) ; - + +boost-install boost_thread ; From bc403742b52c076cf8f0cfe9bb35440ab6b67795 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 26 Oct 2007 09:45:46 +0000 Subject: [PATCH 026/214] disable_cancellation and restore_cancellation need to be declared BOOST_THREAD_DECL to work with DLLs [SVN r40476] --- include/boost/thread/pthread/thread.hpp | 2 -- include/boost/thread/win32/thread.hpp | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index ffc89b50..6a5849d4 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -191,8 +191,6 @@ namespace boost static void yield(); // extensions - class cancel_handle; - cancel_handle get_cancel_handle() const; void cancel(); bool cancellation_requested() const; }; diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 3ebfe5eb..2c001d9d 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -155,7 +155,7 @@ namespace boost namespace this_thread { - class disable_cancellation + class BOOST_THREAD_DECL disable_cancellation { disable_cancellation(const disable_cancellation&); disable_cancellation& operator=(const disable_cancellation&); @@ -167,7 +167,7 @@ namespace boost ~disable_cancellation(); }; - class restore_cancellation + class BOOST_THREAD_DECL restore_cancellation { restore_cancellation(const restore_cancellation&); restore_cancellation& operator=(const restore_cancellation&); From 1d5bbd11a8c2d7082a6b50acce869c0e7fef43f3 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 26 Oct 2007 09:53:10 +0000 Subject: [PATCH 027/214] disable_cancellation and restore_cancellation need to be declared BOOST_THREAD_DECL to work with DLLs with pthread-win32 [SVN r40477] --- include/boost/thread/pthread/thread.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index 6a5849d4..93dec9df 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -197,7 +197,7 @@ namespace boost namespace this_thread { - class disable_cancellation + class BOOST_THREAD_DECL disable_cancellation { disable_cancellation(const disable_cancellation&); disable_cancellation& operator=(const disable_cancellation&); @@ -209,7 +209,7 @@ namespace boost ~disable_cancellation(); }; - class restore_cancellation + class BOOST_THREAD_DECL restore_cancellation { restore_cancellation(const restore_cancellation&); restore_cancellation& operator=(const restore_cancellation&); From 6485717c52bbeaf5fc1541881669d0bef56b6758 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 26 Oct 2007 10:46:01 +0000 Subject: [PATCH 028/214] improved lifetime management of thread data [SVN r40478] --- src/win32/thread.cpp | 94 ++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index cb802261..a2132260 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -80,10 +80,50 @@ namespace boost } } + namespace detail + { + struct thread_exit_callback_node + { + boost::detail::thread_exit_function_base* func; + thread_exit_callback_node* next; + + thread_exit_callback_node(boost::detail::thread_exit_function_base* func_, + thread_exit_callback_node* next_): + func(func_),next(next_) + {} + }; + + } + + namespace + { + void run_thread_exit_callbacks() + { + boost::intrusive_ptr current_thread_data(get_current_thread_data(),false); + if(current_thread_data) + { + while(current_thread_data->thread_exit_callbacks) + { + detail::thread_exit_callback_node* const current_node=current_thread_data->thread_exit_callbacks; + current_thread_data->thread_exit_callbacks=current_node->next; + if(current_node->func) + { + (*current_node->func)(); + boost::detail::heap_delete(current_node->func); + } + boost::detail::heap_delete(current_node); + } + } + set_current_thread_data(0); + } + + } + + unsigned __stdcall thread::thread_start_function(void* param) { - boost::intrusive_ptr thread_info(reinterpret_cast(param),false); - set_current_thread_data(thread_info.get()); + detail::thread_data_base* const thread_info(reinterpret_cast(param)); + set_current_thread_data(thread_info); try { thread_info->run(); @@ -95,6 +135,7 @@ namespace boost { std::terminate(); } + run_thread_exit_callbacks(); return 0; } @@ -127,27 +168,15 @@ namespace boost ++count; thread_handle=detail::win32::duplicate_handle(detail::win32::GetCurrentThread()); } + ~externally_launched_thread() + { + OutputDebugString("External thread finished\n"); + } void run() {} }; - - struct externally_launched_thread_deleter - { - externally_launched_thread* thread_data; - - externally_launched_thread_deleter(externally_launched_thread* thread_data_): - thread_data(thread_data_) - {} - - void operator()() const - { - intrusive_ptr_release(thread_data); - } - }; - } - thread thread::self() { @@ -155,7 +184,6 @@ namespace boost { externally_launched_thread* me=detail::heap_new(); set_current_thread_data(me); - this_thread::at_thread_exit(externally_launched_thread_deleter(me)); } return thread(boost::intrusive_ptr(get_current_thread_data())); } @@ -374,19 +402,6 @@ namespace boost } } - namespace detail - { - struct thread_exit_callback_node - { - boost::detail::thread_exit_function_base* func; - thread_exit_callback_node* next; - - thread_exit_callback_node(boost::detail::thread_exit_function_base* func_, - thread_exit_callback_node* next_): - func(func_),next(next_) - {} - }; - } namespace { void NTAPI thread_exit_func_callback(HINSTANCE, DWORD, PVOID); @@ -416,20 +431,7 @@ namespace boost { if((dwReason==DLL_THREAD_DETACH) || (dwReason==DLL_PROCESS_DETACH)) { - if(boost::detail::thread_data_base* const current_thread_data=get_current_thread_data()) - { - while(current_thread_data->thread_exit_callbacks) - { - detail::thread_exit_callback_node* const current_node=current_thread_data->thread_exit_callbacks; - current_thread_data->thread_exit_callbacks=current_node->next; - if(current_node->func) - { - (*current_node->func)(); - boost::detail::heap_delete(current_node->func); - } - boost::detail::heap_delete(current_node); - } - } + run_thread_exit_callbacks(); } } } From 8efc8458e134ad7bd7f77513b109048ca37420a6 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 30 Oct 2007 17:16:24 +0000 Subject: [PATCH 029/214] Added specialization for reference_wrapper to allow use of boost::ref with boost::thread (again) [SVN r40609] --- include/boost/thread/pthread/thread.hpp | 16 ++++++++++++ include/boost/thread/win32/thread.hpp | 18 ++++++++++++++ test/test_thread.cpp | 33 +++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index 93dec9df..4ca7db08 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -195,6 +195,22 @@ namespace boost bool cancellation_requested() const; }; + template + struct thread::thread_data >: + detail::thread_data_base + { + F& f; + + thread_data(boost::reference_wrapper f_): + f(f_) + {} + + void run() + { + f(); + } + }; + namespace this_thread { class BOOST_THREAD_DECL disable_cancellation diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 2c001d9d..1e0575d8 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -17,6 +17,7 @@ #include #include #include +#include namespace boost { @@ -153,6 +154,23 @@ namespace boost static thread self(); }; + template + struct thread::thread_data >: + detail::thread_data_base + { + F& f; + + thread_data(boost::reference_wrapper f_): + f(f_) + {} + + void run() + { + f(); + } + }; + + namespace this_thread { class BOOST_THREAD_DECL disable_cancellation diff --git a/test/test_thread.cpp b/test/test_thread.cpp index 730e112c..49656c9a 100644 --- a/test/test_thread.cpp +++ b/test/test_thread.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include @@ -118,6 +120,36 @@ void test_thread_no_cancel_if_cancels_disabled_at_cancellation_point() timed_test(&do_test_thread_no_cancel_if_cancels_disabled_at_cancellation_point, 1); } +struct non_copyable_functor: + boost::noncopyable +{ + unsigned value; + + non_copyable_functor(): + value(0) + {} + + void operator()() + { + value=999; + } +}; + +void do_test_creation_through_reference_wrapper() +{ + non_copyable_functor f; + + boost::thread thrd(boost::ref(f)); + thrd.join(); + BOOST_CHECK_EQUAL(f.value, 999); +} + +void test_creation_through_reference_wrapper() +{ + timed_test(&do_test_creation_through_reference_wrapper, 1); +} + + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = @@ -128,6 +160,7 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) test->add(BOOST_TEST_CASE(test_id_comparison)); test->add(BOOST_TEST_CASE(test_thread_cancels_at_cancellation_point)); test->add(BOOST_TEST_CASE(test_thread_no_cancel_if_cancels_disabled_at_cancellation_point)); + test->add(BOOST_TEST_CASE(test_creation_through_reference_wrapper)); return test; } From 370f5d461cec744601ac1fda6dd44180a0bd83e9 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 1 Nov 2007 17:07:47 +0000 Subject: [PATCH 030/214] condition wait and sleep are now cancellation points [SVN r40647] --- .../thread/pthread/condition_variable.hpp | 134 ++++++++---------- .../thread/pthread/condition_variable_fwd.hpp | 53 +++++++ include/boost/thread/pthread/thread.hpp | 35 +---- include/boost/thread/pthread/thread_data.hpp | 93 ++++++++++++ .../boost/thread/win32/condition_variable.hpp | 5 +- include/boost/thread/win32/thread.hpp | 6 +- src/pthread/thread.cpp | 134 ++++++++++-------- test/test_condition.cpp | 23 ++- 8 files changed, 309 insertions(+), 174 deletions(-) create mode 100644 include/boost/thread/pthread/condition_variable_fwd.hpp create mode 100644 include/boost/thread/pthread/thread_data.hpp diff --git a/include/boost/thread/pthread/condition_variable.hpp b/include/boost/thread/pthread/condition_variable.hpp index 99f58c57..84a1e8cb 100644 --- a/include/boost/thread/pthread/condition_variable.hpp +++ b/include/boost/thread/pthread/condition_variable.hpp @@ -13,78 +13,56 @@ #include #include "timespec.hpp" #include "pthread_mutex_scoped_lock.hpp" +#include "thread_data.hpp" +#include "condition_variable_fwd.hpp" namespace boost { - class condition_variable + inline condition_variable::condition_variable() { - private: - pthread_cond_t cond; + int const res=pthread_cond_init(&cond,NULL); + if(res) + { + throw thread_resource_error(); + } + } + inline condition_variable::~condition_variable() + { + int const res=pthread_cond_destroy(&cond); + BOOST_ASSERT(!res); + } + + inline void condition_variable::wait(unique_lock& m) + { + detail::cancel_wrapper allow_cancel(&cond); + int const cond_res=pthread_cond_wait(&cond,m.mutex()->native_handle()); + BOOST_ASSERT(!cond_res); + } + + inline bool condition_variable::timed_wait(unique_lock& m,boost::system_time const& wait_until) + { + detail::cancel_wrapper allow_cancel(&cond); + struct timespec const timeout=detail::get_timespec(wait_until); + int const cond_res=pthread_cond_timedwait(&cond,m.mutex()->native_handle(),&timeout); + if(cond_res==ETIMEDOUT) + { + return false; + } + BOOST_ASSERT(!cond_res); + return true; + } + + inline void condition_variable::notify_one() + { + int const res=pthread_cond_signal(&cond); + BOOST_ASSERT(!res); + } - condition_variable(condition_variable&); - condition_variable& operator=(condition_variable&); - public: - condition_variable() - { - int const res=pthread_cond_init(&cond,NULL); - if(res) - { - throw thread_resource_error(); - } - } - ~condition_variable() - { - int const res=pthread_cond_destroy(&cond); - BOOST_ASSERT(!res); - } - - void wait(unique_lock& m) - { - int const cond_res=pthread_cond_wait(&cond,m.mutex()->native_handle()); - BOOST_ASSERT(!cond_res); - } - - template - void wait(unique_lock& m,predicate_type pred) - { - while(!pred()) wait(m); - } - - bool timed_wait(unique_lock& m,boost::system_time const& wait_until) - { - struct timespec const timeout=detail::get_timespec(wait_until); - int const cond_res=pthread_cond_timedwait(&cond,m.mutex()->native_handle(),&timeout); - if(cond_res==ETIMEDOUT) - { - return false; - } - BOOST_ASSERT(!cond_res); - return true; - } - - template - bool timed_wait(unique_lock& m,boost::system_time const& wait_until,predicate_type pred) - { - while (!pred()) - { - if(!timed_wait(m, wait_until)) - return false; - } - return true; - } - - void notify_one() - { - int const res=pthread_cond_signal(&cond); - BOOST_ASSERT(!res); - } - - void notify_all() - { - int const res=pthread_cond_broadcast(&cond); - BOOST_ASSERT(!res); - } - }; + inline void condition_variable::notify_all() + { + int const res=pthread_cond_broadcast(&cond); + BOOST_ASSERT(!res); + } class condition_variable_any { @@ -123,11 +101,14 @@ namespace boost { int res=0; { - boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); - m.unlock(); - res=pthread_cond_wait(&cond,&internal_mutex); + detail::cancel_wrapper allow_cancel(&cond); + { + boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); + m.unlock(); + res=pthread_cond_wait(&cond,&internal_mutex); + } + m.lock(); } - m.lock(); if(res) { throw condition_error(); @@ -146,11 +127,14 @@ namespace boost struct timespec const timeout=detail::get_timespec(wait_until); int res=0; { - boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); - m.unlock(); - res=pthread_cond_timedwait(&cond,&internal_mutex,&timeout); + detail::cancel_wrapper allow_cancel(&cond); + { + boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); + m.unlock(); + res=pthread_cond_timedwait(&cond,&internal_mutex,&timeout); + } + m.lock(); } - m.lock(); if(res==ETIMEDOUT) { return false; diff --git a/include/boost/thread/pthread/condition_variable_fwd.hpp b/include/boost/thread/pthread/condition_variable_fwd.hpp new file mode 100644 index 00000000..fb38f584 --- /dev/null +++ b/include/boost/thread/pthread/condition_variable_fwd.hpp @@ -0,0 +1,53 @@ +#ifndef BOOST_THREAD_PTHREAD_CONDITION_VARIABLE_FWD_HPP +#define BOOST_THREAD_PTHREAD_CONDITION_VARIABLE_FWD_HPP +// 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) +// (C) Copyright 2007 Anthony Williams + +#include +#include +#include + +namespace boost +{ + class condition_variable + { + private: + pthread_cond_t cond; + + condition_variable(condition_variable&); + condition_variable& operator=(condition_variable&); + + struct cancel_wrapper; + public: + condition_variable(); + ~condition_variable(); + + void wait(unique_lock& m); + + template + void wait(unique_lock& m,predicate_type pred) + { + while(!pred()) wait(m); + } + + bool timed_wait(unique_lock& m,boost::system_time const& wait_until); + + template + bool timed_wait(unique_lock& m,boost::system_time const& wait_until,predicate_type pred) + { + while (!pred()) + { + if(!timed_wait(m, wait_until)) + return false; + } + return true; + } + + void notify_one(); + void notify_all(); + }; +} + +#endif diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index 4ca7db08..baf04d19 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -20,10 +20,10 @@ #include #include #include +#include "thread_data.hpp" namespace boost { - class thread; namespace detail @@ -81,39 +81,6 @@ namespace boost }; } - class thread_cancelled - {}; - - namespace detail - { - struct thread_exit_callback_node; - - struct thread_data_base - { - boost::shared_ptr self; - pthread_t thread_handle; - boost::mutex data_mutex; - boost::condition_variable done_condition; - bool done; - bool join_started; - bool joined; - boost::detail::thread_exit_callback_node* thread_exit_callbacks; - bool cancel_enabled; - bool cancel_requested; - - thread_data_base(): - done(false),join_started(false),joined(false), - thread_exit_callbacks(0), - cancel_enabled(true), - cancel_requested(false) - {} - virtual ~thread_data_base() - {} - - virtual void run()=0; - }; - } - struct xtime; class BOOST_THREAD_DECL thread { diff --git a/include/boost/thread/pthread/thread_data.hpp b/include/boost/thread/pthread/thread_data.hpp new file mode 100644 index 00000000..93d2bbca --- /dev/null +++ b/include/boost/thread/pthread/thread_data.hpp @@ -0,0 +1,93 @@ +#ifndef BOOST_THREAD_PTHREAD_THREAD_DATA_HPP +#define BOOST_THREAD_PTHREAD_THREAD_DATA_HPP +// 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) +// (C) Copyright 2007 Anthony Williams + +#include +#include +#include +#include +#include +#include "condition_variable_fwd.hpp" + +namespace boost +{ + class thread_cancelled + {}; + + namespace detail + { + struct thread_exit_callback_node; + + struct thread_data_base + { + boost::shared_ptr self; + pthread_t thread_handle; + boost::mutex data_mutex; + boost::condition_variable done_condition; + boost::mutex sleep_mutex; + boost::condition_variable sleep_condition; + bool done; + bool join_started; + bool joined; + boost::detail::thread_exit_callback_node* thread_exit_callbacks; + bool cancel_enabled; + bool cancel_requested; + pthread_cond_t* current_cond; + + thread_data_base(): + done(false),join_started(false),joined(false), + thread_exit_callbacks(0), + cancel_enabled(true), + cancel_requested(false), + current_cond(0) + {} + virtual ~thread_data_base() + {} + + virtual void run()=0; + }; + + BOOST_THREAD_DECL thread_data_base* get_current_thread_data(); + + class cancel_wrapper + { + thread_data_base* const thread_info; + + void check_cancel() + { + if(thread_info->cancel_requested) + { + thread_info->cancel_requested=false; + throw thread_cancelled(); + } + } + + public: + explicit cancel_wrapper(pthread_cond_t* cond): + thread_info(detail::get_current_thread_data()) + { + if(thread_info && thread_info->cancel_enabled) + { + lock_guard guard(thread_info->data_mutex); + check_cancel(); + thread_info->current_cond=cond; + } + } + ~cancel_wrapper() + { + if(thread_info && thread_info->cancel_enabled) + { + lock_guard guard(thread_info->data_mutex); + thread_info->current_cond=NULL; + check_cancel(); + } + } + }; + } +} + + +#endif diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index 50319339..5f173e45 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -156,13 +156,10 @@ namespace boost ++generations[0].count; sem=detail::win32::duplicate_handle(generations[0].semaphore); } - unsigned long const wait_result=detail::win32::WaitForSingleObject(sem,::boost::detail::get_milliseconds_until(wait_until)); - - if(wait_result==detail::win32::timeout) + if(!this_thread::cancellable_wait(sem,::boost::detail::get_milliseconds_until(wait_until))) { break; } - BOOST_ASSERT(!wait_result); unsigned long const woken_result=detail::win32::WaitForSingleObject(local_wake_sem,0); BOOST_ASSERT(woken_result==detail::win32::timeout || woken_result==0); diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 1e0575d8..9d2f00f1 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -197,6 +197,10 @@ namespace boost thread::id BOOST_THREAD_DECL get_id(); bool BOOST_THREAD_DECL cancellable_wait(detail::win32::handle handle_to_wait_for,unsigned long milliseconds); + inline bool cancellable_wait(unsigned long milliseconds) + { + return cancellable_wait(detail::win32::invalid_handle_value,milliseconds); + } void BOOST_THREAD_DECL cancellation_point(); bool BOOST_THREAD_DECL cancellation_enabled(); @@ -207,7 +211,7 @@ namespace boost template void sleep(TimeDuration const& rel_time) { - cancellable_wait(detail::win32::invalid_handle_value,static_cast(rel_time.total_milliseconds())); + cancellable_wait(static_cast(rel_time.total_milliseconds())); } } diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index bc9b6828..abe8f0f4 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -24,41 +24,42 @@ namespace boost boost::detail::thread_exit_function_base* func; thread_exit_callback_node* next; }; - } - namespace - { - boost::once_flag current_thread_tls_init_flag=BOOST_ONCE_INIT; - pthread_key_t current_thread_tls_key; - extern "C" + namespace { - void tls_destructor(void* data) + boost::once_flag current_thread_tls_init_flag=BOOST_ONCE_INIT; + pthread_key_t current_thread_tls_key; + + extern "C" { - boost::detail::thread_data_base* thread_info=static_cast(data); - if(thread_info) + void tls_destructor(void* data) { - while(thread_info->thread_exit_callbacks) + boost::detail::thread_data_base* thread_info=static_cast(data); + if(thread_info) { - boost::detail::thread_exit_callback_node* const current_node=thread_info->thread_exit_callbacks; - thread_info->thread_exit_callbacks=current_node->next; - if(current_node->func) + while(thread_info->thread_exit_callbacks) { - (*current_node->func)(); - delete current_node->func; + boost::detail::thread_exit_callback_node* const current_node=thread_info->thread_exit_callbacks; + thread_info->thread_exit_callbacks=current_node->next; + if(current_node->func) + { + (*current_node->func)(); + delete current_node->func; + } + delete current_node; } - delete current_node; } } } - } - void create_current_thread_tls_key() - { - int const res=pthread_key_create(¤t_thread_tls_key,NULL); - BOOST_ASSERT(!res); + void create_current_thread_tls_key() + { + int const res=pthread_key_create(¤t_thread_tls_key,NULL); + BOOST_ASSERT(!res); + } } - + boost::detail::thread_data_base* get_current_thread_data() { boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); @@ -71,15 +72,17 @@ namespace boost int const res=pthread_setspecific(current_thread_tls_key,new_data); BOOST_ASSERT(!res); } - - + } + + namespace + { extern "C" { void* thread_proxy(void* param) { boost::shared_ptr thread_info = static_cast(param)->self; thread_info->self.reset(); - set_current_thread_data(thread_info.get()); + detail::set_current_thread_data(thread_info.get()); try { thread_info->run(); @@ -92,8 +95,8 @@ namespace boost std::terminate(); } - tls_destructor(thread_info.get()); - set_current_thread_data(0); + detail::tls_destructor(thread_info.get()); + detail::set_current_thread_data(0); boost::lock_guard lock(thread_info->data_mutex); thread_info->done=true; thread_info->done_condition.notify_all(); @@ -213,33 +216,43 @@ namespace boost void thread::sleep(const system_time& st) { - xtime const xt=get_xtime(st); - - for (int foo=0; foo < 5; ++foo) + detail::thread_data_base* const thread_info=detail::get_current_thread_data(); + + if(thread_info) { + unique_lock lk(thread_info->sleep_mutex); + while(thread_info->sleep_condition.timed_wait(lk,st)); + } + else + { + xtime const xt=get_xtime(st); + + for (int foo=0; foo < 5; ++foo) + { # if defined(BOOST_HAS_PTHREAD_DELAY_NP) - timespec ts; - to_timespec_duration(xt, ts); - int res = 0; - res = pthread_delay_np(&ts); - BOOST_ASSERT(res == 0); + timespec ts; + to_timespec_duration(xt, ts); + int res = 0; + res = pthread_delay_np(&ts); + BOOST_ASSERT(res == 0); # elif defined(BOOST_HAS_NANOSLEEP) - timespec ts; - to_timespec_duration(xt, ts); - - // nanosleep takes a timespec that is an offset, not - // an absolute time. - nanosleep(&ts, 0); + timespec ts; + to_timespec_duration(xt, ts); + + // nanosleep takes a timespec that is an offset, not + // an absolute time. + nanosleep(&ts, 0); # else - mutex mx; - mutex::scoped_lock lock(mx); - condition cond; - cond.timed_wait(lock, xt); + mutex mx; + mutex::scoped_lock lock(mx); + condition cond; + cond.timed_wait(lock, xt); # endif - xtime cur; - xtime_get(&cur, TIME_UTC); - if (xtime_cmp(xt, cur) <= 0) - return; + xtime cur; + xtime_get(&cur, TIME_UTC); + if (xtime_cmp(xt, cur) <= 0) + return; + } } } @@ -285,6 +298,11 @@ namespace boost { lock_guard lk(local_thread_info->data_mutex); local_thread_info->cancel_requested=true; + if(local_thread_info->current_cond) + { + int const res=pthread_cond_broadcast(local_thread_info->current_cond); + BOOST_ASSERT(!res); + } } } @@ -293,7 +311,7 @@ namespace boost { void cancellation_point() { - boost::detail::thread_data_base* const thread_info=get_current_thread_data(); + boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data(); if(thread_info && thread_info->cancel_enabled) { lock_guard lg(thread_info->data_mutex); @@ -307,13 +325,13 @@ namespace boost bool cancellation_enabled() { - boost::detail::thread_data_base* const thread_info=get_current_thread_data(); + boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data(); return thread_info && thread_info->cancel_enabled; } bool cancellation_requested() { - boost::detail::thread_data_base* const thread_info=get_current_thread_data(); + boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data(); if(!thread_info) { return false; @@ -330,15 +348,15 @@ namespace boost { if(cancel_was_enabled) { - get_current_thread_data()->cancel_enabled=false; + detail::get_current_thread_data()->cancel_enabled=false; } } disable_cancellation::~disable_cancellation() { - if(get_current_thread_data()) + if(detail::get_current_thread_data()) { - get_current_thread_data()->cancel_enabled=cancel_was_enabled; + detail::get_current_thread_data()->cancel_enabled=cancel_was_enabled; } } @@ -346,15 +364,15 @@ namespace boost { if(d.cancel_was_enabled) { - get_current_thread_data()->cancel_enabled=true; + detail::get_current_thread_data()->cancel_enabled=true; } } restore_cancellation::~restore_cancellation() { - if(get_current_thread_data()) + if(detail::get_current_thread_data()) { - get_current_thread_data()->cancel_enabled=false; + detail::get_current_thread_data()->cancel_enabled=false; } } } diff --git a/test/test_condition.cpp b/test/test_condition.cpp index 4570f06e..dea55ead 100644 --- a/test/test_condition.cpp +++ b/test/test_condition.cpp @@ -103,7 +103,7 @@ void do_test_condition_notify_one() void test_condition_notify_one() { - timed_test(&do_test_condition_notify_one, 2, execution_monitor::use_mutex); + timed_test(&do_test_condition_notify_one, 100, execution_monitor::use_mutex); } void do_test_condition_notify_all() @@ -131,7 +131,7 @@ void test_condition_notify_all() // We should have already tested notify_one here, so // a timed test with the default execution_monitor::use_condition // should be OK, and gives the fastest performance - timed_test(&do_test_condition_notify_all, 3); + timed_test(&do_test_condition_notify_all, 100); } void do_test_condition_waits() @@ -189,6 +189,24 @@ void test_condition_waits() timed_test(&do_test_condition_waits, 12); } +void do_test_condition_wait_is_a_cancellation_point() +{ + condition_test_data data; + + boost::thread thread(bind(&condition_test_thread, &data)); + + thread.cancel(); + thread.join(); + BOOST_CHECK_EQUAL(data.awoken,0); +} + + +void test_condition_wait_is_a_cancellation_point() +{ + timed_test(&do_test_condition_wait_is_a_cancellation_point, 1); +} + + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = @@ -197,6 +215,7 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) test->add(BOOST_TEST_CASE(&test_condition_notify_one)); test->add(BOOST_TEST_CASE(&test_condition_notify_all)); test->add(BOOST_TEST_CASE(&test_condition_waits)); + test->add(BOOST_TEST_CASE(&test_condition_wait_is_a_cancellation_point)); return test; } From 08a840afe4f8010651afef0c9b939faf64fdf2b9 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 1 Nov 2007 17:18:54 +0000 Subject: [PATCH 031/214] shared_mutex lock functions are not cancellation points [SVN r40650] --- include/boost/thread/pthread/shared_mutex.hpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/include/boost/thread/pthread/shared_mutex.hpp b/include/boost/thread/pthread/shared_mutex.hpp index 300d008d..d2ebb7a2 100644 --- a/include/boost/thread/pthread/shared_mutex.hpp +++ b/include/boost/thread/pthread/shared_mutex.hpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include namespace boost { @@ -29,9 +29,9 @@ namespace boost state_data state; boost::mutex state_change; - boost::condition shared_cond; - boost::condition exclusive_cond; - boost::condition upgrade_cond; + boost::condition_variable shared_cond; + boost::condition_variable exclusive_cond; + boost::condition_variable upgrade_cond; void release_waiters() { @@ -53,6 +53,7 @@ namespace boost void lock_shared() { + boost::this_thread::disable_cancellation no_cancel; boost::mutex::scoped_lock lock(state_change); while(true) @@ -84,6 +85,7 @@ namespace boost bool timed_lock_shared(system_time const& timeout) { + boost::this_thread::disable_cancellation no_cancel; boost::mutex::scoped_lock lock(state_change); while(true) @@ -124,6 +126,7 @@ namespace boost void lock() { + boost::this_thread::disable_cancellation no_cancel; boost::mutex::scoped_lock lock(state_change); while(true) @@ -143,6 +146,7 @@ namespace boost bool timed_lock(system_time const& timeout) { + boost::this_thread::disable_cancellation no_cancel; boost::mutex::scoped_lock lock(state_change); while(true) @@ -189,6 +193,7 @@ namespace boost void lock_upgrade() { + boost::this_thread::disable_cancellation no_cancel; boost::mutex::scoped_lock lock(state_change); while(true) { @@ -205,6 +210,7 @@ namespace boost bool timed_lock_upgrade(system_time const& timeout) { + boost::this_thread::disable_cancellation no_cancel; boost::mutex::scoped_lock lock(state_change); while(true) { @@ -252,6 +258,7 @@ namespace boost void unlock_upgrade_and_lock() { + boost::this_thread::disable_cancellation no_cancel; boost::mutex::scoped_lock lock(state_change); --state.shared_count; while(true) From 3513eaf701484c413799b13c2b88598910c8e216 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 1 Nov 2007 18:04:55 +0000 Subject: [PATCH 032/214] added timed_join to thread [SVN r40653] --- include/boost/thread/pthread/thread.hpp | 7 ++++ include/boost/thread/win32/thread.hpp | 7 ++++ src/pthread/thread.cpp | 49 +++++++++++++++++++++++ src/win32/thread.cpp | 14 +++++++ test/test_once.cpp | 4 +- test/test_shared_mutex.cpp | 10 +---- test/test_thread.cpp | 52 ++++++++++++++++++++++++- 7 files changed, 132 insertions(+), 11 deletions(-) diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index baf04d19..9202262f 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -146,6 +146,13 @@ namespace boost bool joinable() const; void join(); + bool timed_join(const system_time& wait_until); + + template + inline bool timed_join(TimeDuration const& rel_time) + { + return timed_join(get_system_time()+rel_time); + } void detach(); static unsigned hardware_concurrency(); diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 9d2f00f1..ebca1d23 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -131,6 +131,13 @@ namespace boost bool joinable() const; void join(); + bool timed_join(const system_time& wait_until); + + template + inline bool timed_join(TimeDuration const& rel_time) + { + return timed_join(get_system_time()+rel_time); + } void detach(); static unsigned hardware_concurrency(); diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index abe8f0f4..8b4581a0 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -187,6 +187,55 @@ namespace boost } } + bool thread::timed_join(system_time const& wait_until) + { + boost::shared_ptr const local_thread_info=get_thread_info(); + if(local_thread_info) + { + bool do_join=false; + + { + unique_lock lock(local_thread_info->data_mutex); + while(!local_thread_info->done) + { + if(!local_thread_info->done_condition.timed_wait(lock,wait_until)) + { + return false; + } + } + do_join=!local_thread_info->join_started; + + if(do_join) + { + local_thread_info->join_started=true; + } + else + { + while(!local_thread_info->joined) + { + local_thread_info->done_condition.wait(lock); + } + } + } + if(do_join) + { + void* result=0; + int const res=pthread_join(local_thread_info->thread_handle,&result); + BOOST_ASSERT(!res); + lock_guard lock(local_thread_info->data_mutex); + local_thread_info->joined=true; + local_thread_info->done_condition.notify_all(); + } + + lock_guard l1(thread_info_mutex); + if(thread_info==local_thread_info) + { + thread_info.reset(); + } + } + return true; + } + bool thread::joinable() const { return get_thread_info(); diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index a2132260..9c379675 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -251,6 +251,20 @@ namespace boost release_handle(); } } + + bool thread::timed_join(boost::system_time const& wait_until) + { + boost::intrusive_ptr local_thread_info=get_thread_info(); + if(local_thread_info) + { + if(!this_thread::cancellable_wait(local_thread_info->thread_handle,get_milliseconds_until(wait_until))) + { + return false; + } + release_handle(); + } + return true; + } void thread::detach() { diff --git a/test/test_once.cpp b/test/test_once.cpp index 24b1affd..fe79cef9 100644 --- a/test/test_once.cpp +++ b/test/test_once.cpp @@ -142,8 +142,8 @@ void test_call_once_retried_on_exception() group.create_thread(&call_once_with_exception); } group.join_all(); - BOOST_CHECK_EQUAL(throw_before_third_pass::pass_counter,3); - BOOST_CHECK_EQUAL(exception_counter,2); + BOOST_CHECK_EQUAL(throw_before_third_pass::pass_counter,3u); + BOOST_CHECK_EQUAL(exception_counter,2u); } diff --git a/test/test_shared_mutex.cpp b/test/test_shared_mutex.cpp index 14058180..c0d863c8 100644 --- a/test/test_shared_mutex.cpp +++ b/test/test_shared_mutex.cpp @@ -427,15 +427,9 @@ void test_timed_lock_shared_times_out_if_write_lock_held() CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); boost::system_time const start=boost::get_system_time(); - boost::system_time const timeout=start+boost::posix_time::milliseconds(100); + boost::system_time const timeout=start+boost::posix_time::milliseconds(2000); bool const timed_lock_succeeded=rw_mutex.timed_lock_shared(timeout); - boost::system_time const wakeup=boost::get_system_time(); - BOOST_CHECK(timeout<=(wakeup+boost::posix_time::milliseconds(1))); - if(timeout>wakeup) - { - std::cout<<"timeout="<BOOST_HAS_PTHREADS - $(PTW32_INCLUDE) - $(PTW32_LIB) ; +feature.feature threadapi : pthread win32 : propagated ; +feature.set-default threadapi : [ default_threadapi ] ; + +rule tag ( name : type ? : property-set ) +{ + local result = $(name) ; + + if $(type) in STATIC_LIB SHARED_LIB IMPORT_LIB + { + local api = [ $(property-set).get ] ; - AVAILABLE_THREAD_APIS += pthread ; - } - else - { - echo "******************************************************" ; - echo "Building Boost.Thread without optional pthread support" ; - echo "If you need pthread you should specify the paths." ; - echo "For example:" ; - echo "PTW32_INCLUDE=C:\\Program Files\\ptw32\\Pre-built2\\include" ; - echo "PTW32_LIB=C:\\Program Files\\ptw32\\Pre-built2\\lib\\pthreadVC2.lib" ; - echo "******************************************************" ; - BOOST_PTHREAD_OPTS += no ; + # non native api gets additional tag + if $(api) != [ default_threadapi ] { + result = $(result)_$(api) ; } + } + + # forward to the boost tagging rule + return [ indirect.call $(BOOST_JAMROOT_MODULE)%$(BOOST_JAMROOT_MODULE).tag + $(result) : $(type) : $(property-set) ] ; } -else + + +rule requirements ( properties * ) { - AVAILABLE_THREAD_APIS = pthread ; - if [ os.name ] = CYGWIN + local result ; + if pthread in $(properties) + { + result += BOOST_THREAD_POSIX ; + if windows in $(properties) { - AVAILABLE_THREAD_APIS += win32 ; - } + local PTW32_INCLUDE ; + local PTW32_LIB ; + PTW32_INCLUDE = [ modules.peek : PTW32_INCLUDE ] ; + PTW32_LIB = [ modules.peek : PTW32_LIB ] ; + PTW32_INCLUDE ?= [ modules.peek user-config : PTW32_INCLUDE ] ; + PTW32_LIB ?= [ modules.peek user-config : PTW32_LIB ] ; + PTW32_INCLUDE ?= [ modules.peek site-config : PTW32_INCLUDE ] ; + PTW32_LIB ?= [ modules.peek site-config : PTW32_LIB ] ; + + if ! ( $(PTW32_INCLUDE) && $(PTW32_LIB) ) + { + if ! $(.notified) + { + echo "************************************************************" ; + echo "Trying to build Boost.Thread with pthread support." ; + echo "If you need pthread you should specify the paths." ; + echo "You can specify them in site-config.jam, user-config.jam" ; + echo "or in the environment." ; + echo "For example:" ; + echo "PTW32_INCLUDE=C:\\Program Files\\ptw32\\Pre-built2\\include" ; + echo "PTW32_LIB=C:\\Program Files\\ptw32\\Pre-built2\\lib" ; + echo "************************************************************" ; + .notified = true ; + } + result = no ; + } + else + { + local include_path = [ path.make $(PTW32_INCLUDE) ] ; + local lib_path = [ path.make $(PTW32_LIB) ] ; + result += $(include_path) ; + local libname = pthread ; + if msvc in $(properties) + { + libname = $(libname)VC2.lib ; + } + if gcc in $(properties) + { + libname = lib$(libname)GC2.a ; + } + lib_path = [ path.glob $(lib_path) : $(libname) ] ; + if ! $(lib_path) + { + if ! $(.notified) + { + echo "************************************************************" ; + echo "Trying to build Boost.Thread with pthread support." ; + echo "But the library" $(libname) "could not be found in path" ; + echo $(PTW32_LIB) ; + echo "************************************************************" ; + .notified = true ; + } + result = no ; + } + result += $(lib_path) ; + + } + } + } + return $(result) ; } -feature.feature thrd-api : $(AVAILABLE_THREAD_APIS) : propagated composite ; -feature.compose pthread : $(BOOST_PTHREAD_OPTS) ; +alias thread_sources + : ## win32 sources ## + win32/thread.cpp + win32/exceptions.cpp + win32/xtime.cpp + win32/tss.cpp + win32/tss_hooks.cpp + win32/tss_dll.cpp + win32/tss_pe.cpp + : ## requirements ## + win32 + ; -lib boost_thread - : ## sources ## - win32/thread.cpp - win32/exceptions.cpp - win32/xtime.cpp - win32/tss.cpp - win32/tss_hooks.cpp - win32/tss_dll.cpp - win32/tss_pe.cpp - : ## requirements ## - win32 - ; +alias thread_sources + : ## pthread sources ## + pthread/thread.cpp + pthread/exceptions.cpp + pthread/xtime.cpp + pthread/tss.cpp + : ## requirements ## + pthread + ; + +explicit thread_sources ; -# build the pthread based variant lib boost_thread - : ## sources ## - pthread/thread.cpp - pthread/exceptions.cpp - pthread/xtime.cpp - pthread/tss.cpp - : ## requirements ## - pthread - $(BOOST_PTHREAD_OPTS) - ; - -boost-install boost_thread ; + : thread_sources + : @requirements + ; From abee301f3dc2b9fabdd614836d77070c4c72a486 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 2 Nov 2007 09:17:02 +0000 Subject: [PATCH 034/214] Added changes from David Deakins to enable compilation on Windows CE [SVN r40679] --- .../boost/thread/win32/thread_heap_alloc.hpp | 4 ++ .../boost/thread/win32/thread_primitives.hpp | 66 +++++++++++++++++-- src/tss_null.cpp | 3 +- src/win32/thread.cpp | 45 +++++++++++-- src/win32/tss.cpp | 52 ++------------- src/win32/tss_hooks.cpp | 6 ++ src/win32/tss_pe.cpp | 4 +- 7 files changed, 121 insertions(+), 59 deletions(-) diff --git a/include/boost/thread/win32/thread_heap_alloc.hpp b/include/boost/thread/win32/thread_heap_alloc.hpp index abf6e9ca..eed7d4b3 100644 --- a/include/boost/thread/win32/thread_heap_alloc.hpp +++ b/include/boost/thread/win32/thread_heap_alloc.hpp @@ -25,6 +25,10 @@ namespace boost #else +# ifdef HeapAlloc +# undef HeapAlloc +# endif + namespace boost { namespace detail diff --git a/include/boost/thread/win32/thread_primitives.hpp b/include/boost/thread/win32/thread_primitives.hpp index f8d1854e..89fecec7 100644 --- a/include/boost/thread/win32/thread_primitives.hpp +++ b/include/boost/thread/win32/thread_primitives.hpp @@ -3,7 +3,8 @@ // win32_thread_primitives.hpp // -// (C) Copyright 2005-6 Anthony Williams +// (C) Copyright 2005-7 Anthony Williams +// (C) Copyright 2007 David Deakins // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -28,9 +29,15 @@ namespace boost unsigned const timeout=WAIT_TIMEOUT; handle const invalid_handle_value=INVALID_HANDLE_VALUE; +# ifdef BOOST_NO_ANSI_APIS + using ::CreateMutexW; + using ::CreateEventW; + using ::CreateSemaphoreW; +# else using ::CreateMutexA; using ::CreateEventA; using ::CreateSemaphoreA; +# endif using ::CloseHandle; using ::ReleaseMutex; using ::ReleaseSemaphore; @@ -50,6 +57,29 @@ namespace boost } } #elif defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) + +# ifdef UNDER_CE +# ifndef WINAPI +# ifndef _WIN32_WCE_EMULATION +# define WINAPI __cdecl // Note this doesn't match the desktop definition +# else +# define WINAPI __stdcall +# endif +# endif + +# ifdef __cplusplus +extern "C" { +# endif +typedef int BOOL; +typedef unsigned long DWORD; +typedef void* HANDLE; + +# include +# ifdef __cplusplus +} +# endif +# endif + namespace boost { namespace detail @@ -70,25 +100,41 @@ namespace boost extern "C" { struct _SECURITY_ATTRIBUTES; +# ifdef BOOST_NO_ANSI_APIS + __declspec(dllimport) void* __stdcall CreateMutexW(_SECURITY_ATTRIBUTES*,int,wchar_t const*); + __declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES*,long,long,wchar_t const*); + __declspec(dllimport) void* __stdcall CreateEventW(_SECURITY_ATTRIBUTES*,int,int,wchar_t const*); +# else __declspec(dllimport) void* __stdcall CreateMutexA(_SECURITY_ATTRIBUTES*,int,char const*); __declspec(dllimport) void* __stdcall CreateSemaphoreA(_SECURITY_ATTRIBUTES*,long,long,char const*); __declspec(dllimport) void* __stdcall CreateEventA(_SECURITY_ATTRIBUTES*,int,int,char const*); +# endif __declspec(dllimport) int __stdcall CloseHandle(void*); __declspec(dllimport) int __stdcall ReleaseMutex(void*); - __declspec(dllimport) unsigned long __stdcall GetCurrentProcessId(); - __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(); __declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void*,unsigned long); + __declspec(dllimport) unsigned long __stdcall WaitForMultipleObjects(unsigned long nCount,void* const * lpHandles,int bWaitAll,unsigned long dwMilliseconds); __declspec(dllimport) int __stdcall ReleaseSemaphore(void*,long,long*); - __declspec(dllimport) void* __stdcall GetCurrentThread(); - __declspec(dllimport) void* __stdcall GetCurrentProcess(); __declspec(dllimport) int __stdcall DuplicateHandle(void*,void*,void*,void**,unsigned long,int,unsigned long); __declspec(dllimport) unsigned long __stdcall SleepEx(unsigned long,int); __declspec(dllimport) void __stdcall Sleep(unsigned long); typedef void (__stdcall *queue_user_apc_callback_function)(ulong_ptr); __declspec(dllimport) unsigned long __stdcall QueueUserAPC(queue_user_apc_callback_function,void*,ulong_ptr); + +# ifndef UNDER_CE + __declspec(dllimport) unsigned long __stdcall GetCurrentProcessId(); + __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(); + __declspec(dllimport) void* __stdcall GetCurrentThread(); + __declspec(dllimport) void* __stdcall GetCurrentProcess(); __declspec(dllimport) int __stdcall SetEvent(void*); __declspec(dllimport) int __stdcall ResetEvent(void*); - __declspec(dllimport) unsigned long __stdcall WaitForMultipleObjects(unsigned long nCount,void* const * lpHandles,int bWaitAll,unsigned long dwMilliseconds); +# else + using ::GetCurrentProcessId; + using ::GetCurrentThreadId; + using ::GetCurrentThread; + using ::GetCurrentProcess; + using ::SetEvent; + using ::ResetEvent; +# endif } } } @@ -117,7 +163,11 @@ namespace boost inline handle create_anonymous_event(event_type type,initial_event_state state) { +#if !defined(BOOST_NO_ANSI_APIS) handle const res=win32::CreateEventA(0,type,state,0); +#else + handle const res=win32::CreateEventW(0,type,state,0); +#endif if(!res) { throw thread_resource_error(); @@ -127,7 +177,11 @@ namespace boost inline handle create_anonymous_semaphore(long initial_count,long max_count) { +#if !defined(BOOST_NO_ANSI_APIS) handle const res=CreateSemaphoreA(NULL,initial_count,max_count,NULL); +#else + handle const res=CreateSemaphoreW(NULL,initial_count,max_count,NULL); +#endif if(!res) { throw thread_resource_error(); diff --git a/src/tss_null.cpp b/src/tss_null.cpp index 07271782..3288c679 100644 --- a/src/tss_null.cpp +++ b/src/tss_null.cpp @@ -1,11 +1,12 @@ // (C) Copyright Michael Glassford 2004. +// (C) Copyright 2007 Anthony Williams // Use, modification and distribution are subject to 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) #include -#if defined(BOOST_HAS_WINTHREADS) && (defined(BOOST_THREAD_BUILD_LIB) || defined(BOOST_THREAD_TEST)) && !defined(_MSC_VER) +#if defined(BOOST_HAS_WINTHREADS) && (defined(BOOST_THREAD_BUILD_LIB) || defined(BOOST_THREAD_TEST)) && (!defined(_MSC_VER) || defined(UNDER_CE)) /* This file is a "null" implementation of tss cleanup; it's diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index 9c379675..466e5e51 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -2,11 +2,14 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007 David Deakins #include #include #include +#ifndef UNDER_CE #include +#endif #include #include #include @@ -15,7 +18,7 @@ namespace boost { namespace { -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(UNDER_CE) __declspec(thread) detail::thread_data_base* current_thread_data=0; detail::thread_data_base* get_current_thread_data() { @@ -59,6 +62,41 @@ namespace boost BOOST_ASSERT(res); } #endif + +#ifdef BOOST_NO_THREADEX +// Windows CE doesn't define _beginthreadex + + struct ThreadProxyData + { + typedef unsigned (__stdcall* func)(void*); + func start_address_; + void* arglist_; + ThreadProxyData(func start_address,void* arglist) : start_address_(start_address), arglist_(arglist) {} + }; + + DWORD WINAPI ThreadProxy(LPVOID args) + { + ThreadProxyData* data=reinterpret_cast(args); + DWORD ret=data->start_address_(data->arglist_); + delete data; + return ret; + } + + typedef void* uintptr_t; + + inline uintptr_t const _beginthreadex(void* security, unsigned stack_size, unsigned (__stdcall* start_address)(void*), + void* arglist, unsigned initflag, unsigned* thrdaddr) + { + DWORD threadID; + HANDLE hthread=CreateThread(static_cast(security),stack_size,ThreadProxy, + new ThreadProxyData(start_address,arglist),initflag,&threadID); + if (hthread!=0) + *thrdaddr=threadID; + return reinterpret_cast(hthread); + } + +#endif + } void thread::yield() @@ -166,12 +204,9 @@ namespace boost externally_launched_thread() { ++count; + cancel_enabled=false; thread_handle=detail::win32::duplicate_handle(detail::win32::GetCurrentThread()); } - ~externally_launched_thread() - { - OutputDebugString("External thread finished\n"); - } void run() {} diff --git a/src/win32/tss.cpp b/src/win32/tss.cpp index 33f635b9..eb5a4d81 100644 --- a/src/win32/tss.cpp +++ b/src/win32/tss.cpp @@ -1,5 +1,7 @@ // Copyright (C) 2001-2003 William E. Kempf // Copyright (C) 2006 Roland Schwarz +// Copyright (C) 2007 Anthony Williams +// Copyright (C) 2007 David Deakins // // 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) @@ -17,9 +19,10 @@ #include #include -#if defined(BOOST_HAS_WINTHREADS) -# include -# include +#include +#include +#if defined(UNDER_CE) && !defined(TLS_OUT_OF_INDEXES) +# define TLS_OUT_OF_INDEXES 0xFFFFFFFF #endif namespace { @@ -30,13 +33,7 @@ typedef std::vector*> tss_data_cleanup_handlers_ty boost::once_flag tss_data_once = BOOST_ONCE_INIT; boost::mutex* tss_data_mutex = 0; tss_data_cleanup_handlers_type* tss_data_cleanup_handlers = 0; -#if defined(BOOST_HAS_WINTHREADS) DWORD tss_data_native_key=TLS_OUT_OF_INDEXES; -#elif defined(BOOST_HAS_PTHREADS) - pthread_key_t tss_data_native_key; -#elif defined(BOOST_HAS_MPTASKS) - TaskStorageIndex tss_data_native_key; -#endif int tss_data_use = 0; void tss_data_inc_use(boost::mutex::scoped_lock& lk) @@ -58,15 +55,8 @@ void tss_data_dec_use(boost::mutex::scoped_lock& lk) lk.unlock(); delete tss_data_mutex; tss_data_mutex = 0; -#if defined(BOOST_HAS_WINTHREADS) TlsFree(tss_data_native_key); tss_data_native_key=TLS_OUT_OF_INDEXES; -#elif defined(BOOST_HAS_PTHREADS) - pthread_key_delete(tss_data_native_key); -#elif defined(BOOST_HAS_MPTASKS) - // Don't know what to put here. - // But MPTASKS isn't currently maintained anyways... -#endif } } @@ -79,9 +69,7 @@ extern "C" void cleanup_slots(void* p) (*(*tss_data_cleanup_handlers)[i])((*slots)[i]); (*slots)[i] = 0; } -#if defined(BOOST_HAS_WINTHREADS) TlsSetValue(tss_data_native_key,0); -#endif tss_data_dec_use(lock); delete slots; } @@ -95,7 +83,6 @@ void init_tss_data() if (temp_mutex.get() == 0) throw boost::thread_resource_error(); -#if defined(BOOST_HAS_WINTHREADS) //Force the cleanup implementation library to be linked in tss_cleanup_implemented(); @@ -103,15 +90,6 @@ void init_tss_data() tss_data_native_key = TlsAlloc(); if (tss_data_native_key == TLS_OUT_OF_INDEXES) return; -#elif defined(BOOST_HAS_PTHREADS) - int res = pthread_key_create(&tss_data_native_key, &cleanup_slots); - if (res != 0) - return; -#elif defined(BOOST_HAS_MPTASKS) - OSStatus status = MPAllocateTaskStorageIndex(&tss_data_native_key); - if (status != noErr) - return; -#endif // The life time of cleanup handlers and mutex is beeing // managed by a reference counting technique. @@ -123,7 +101,6 @@ void init_tss_data() tss_data_mutex = temp_mutex.release(); } -#if defined(BOOST_HAS_WINTHREADS) tss_slots* get_slots(bool alloc); void __cdecl tss_thread_exit() @@ -132,39 +109,22 @@ void __cdecl tss_thread_exit() if (slots) cleanup_slots(slots); } -#endif tss_slots* get_slots(bool alloc) { tss_slots* slots = 0; -#if defined(BOOST_HAS_WINTHREADS) slots = static_cast( TlsGetValue(tss_data_native_key)); -#elif defined(BOOST_HAS_PTHREADS) - slots = static_cast( - pthread_getspecific(tss_data_native_key)); -#elif defined(BOOST_HAS_MPTASKS) - slots = static_cast( - MPGetTaskStorageValue(tss_data_native_key)); -#endif if (slots == 0 && alloc) { std::auto_ptr temp(new tss_slots); -#if defined(BOOST_HAS_WINTHREADS) if (at_thread_exit(&tss_thread_exit) == -1) return 0; if (!TlsSetValue(tss_data_native_key, temp.get())) return 0; -#elif defined(BOOST_HAS_PTHREADS) - if (pthread_setspecific(tss_data_native_key, temp.get()) != 0) - return 0; -#elif defined(BOOST_HAS_MPTASKS) - if (MPSetTaskStorageValue(tss_data_native_key, temp.get()) != noErr) - return 0; -#endif { boost::mutex::scoped_lock lock(*tss_data_mutex); tss_data_inc_use(lock); diff --git a/src/win32/tss_hooks.cpp b/src/win32/tss_hooks.cpp index ba7c1972..a4720506 100644 --- a/src/win32/tss_hooks.cpp +++ b/src/win32/tss_hooks.cpp @@ -1,5 +1,7 @@ // Copyright (C) 2004 Michael Glassford // Copyright (C) 2006 Roland Schwarz +// Copyright (C) 2007 Anthony Williams +// Copyright (C) 2007 David Deakins // Use, modification and distribution are subject to 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) @@ -19,6 +21,10 @@ #define WIN32_LEAN_AND_MEAN #include +# if defined(UNDER_CE) && !defined(TLS_OUT_OF_INDEXES) +# define TLS_OUT_OF_INDEXES 0xFFFFFFFF +# endif + namespace { class CScopedCSLock diff --git a/src/win32/tss_pe.cpp b/src/win32/tss_pe.cpp index 0e250092..07fa9e12 100644 --- a/src/win32/tss_pe.cpp +++ b/src/win32/tss_pe.cpp @@ -1,11 +1,13 @@ // (C) Copyright Aaron W. LaFramboise, Roland Schwarz, Michael Glassford 2004. +// (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007 David Deakins // Use, modification and distribution are subject to 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) #include -#if defined(BOOST_HAS_WINTHREADS) && defined(BOOST_THREAD_BUILD_LIB) && defined(_MSC_VER) +#if defined(BOOST_THREAD_BUILD_LIB) && defined(_MSC_VER) && !defined(UNDER_CE) #include From 390863705648e83428e7048934410de77f5c3fb9 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 2 Nov 2007 11:47:56 +0000 Subject: [PATCH 035/214] rewrite xtime_get in terms of get_system_time to ensure clock consistency, and fix Borland test failures [SVN r40680] --- build/Jamfile.v2 | 2 - include/boost/thread/xtime.hpp | 29 +++--- src/pthread/xtime.cpp | 158 --------------------------------- src/win32/xtime.cpp | 158 --------------------------------- 4 files changed, 19 insertions(+), 328 deletions(-) delete mode 100644 src/pthread/xtime.cpp delete mode 100644 src/win32/xtime.cpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 49230dcd..36525b22 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -150,7 +150,6 @@ alias thread_sources : ## win32 sources ## win32/thread.cpp win32/exceptions.cpp - win32/xtime.cpp win32/tss.cpp win32/tss_hooks.cpp win32/tss_dll.cpp @@ -163,7 +162,6 @@ alias thread_sources : ## pthread sources ## pthread/thread.cpp pthread/exceptions.cpp - pthread/xtime.cpp pthread/tss.cpp : ## requirements ## pthread diff --git a/include/boost/thread/xtime.hpp b/include/boost/thread/xtime.hpp index 8f2f4230..e3970758 100644 --- a/include/boost/thread/xtime.hpp +++ b/include/boost/thread/xtime.hpp @@ -54,16 +54,6 @@ struct xtime }; -int BOOST_THREAD_DECL xtime_get(struct xtime* xtp, int clock_type); - -inline int xtime_cmp(const xtime& xt1, const xtime& xt2) -{ - if (xt1.sec == xt2.sec) - return (int)(xt1.nsec - xt2.nsec); - else - return (xt1.sec > xt2.sec) ? 1 : -1; -} - inline xtime get_xtime(boost::system_time const& abs_time) { xtime res={0}; @@ -74,6 +64,25 @@ inline xtime get_xtime(boost::system_time const& abs_time) return res; } +inline int xtime_get(struct xtime* xtp, int clock_type) +{ + if (clock_type == TIME_UTC) + { + *xtp=get_xtime(get_system_time()); + return clock_type; + } + return 0; +} + + +inline int xtime_cmp(const xtime& xt1, const xtime& xt2) +{ + if (xt1.sec == xt2.sec) + return (int)(xt1.nsec - xt2.nsec); + else + return (xt1.sec > xt2.sec) ? 1 : -1; +} + } // namespace boost #endif //BOOST_XTIME_WEK070601_HPP diff --git a/src/pthread/xtime.cpp b/src/pthread/xtime.cpp deleted file mode 100644 index 0ae854ff..00000000 --- a/src/pthread/xtime.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (C) 2001-2003 -// William E. Kempf -// -// 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) - -#include - -#if defined(BOOST_HAS_FTIME) -# define __STDC_CONSTANT_MACROS -#endif - -#include - -#if defined(BOOST_HAS_FTIME) -# include -# include -#elif defined(BOOST_HAS_GETTIMEOFDAY) -# include -#elif defined(BOOST_HAS_MPTASKS) -# include -# include -#endif - -#include - -namespace boost { - -#ifdef BOOST_HAS_MPTASKS - -namespace detail -{ - -using thread::force_cast; - -struct startup_time_info -{ - startup_time_info() - { - // 1970 Jan 1 at 00:00:00 - static const DateTimeRec k_sUNIXBase = {1970, 1, 1, 0, 0, 0, 0}; - static unsigned long s_ulUNIXBaseSeconds = 0UL; - - if(s_ulUNIXBaseSeconds == 0UL) - { - // calculate the number of seconds between the Mac OS base and the - // UNIX base the first time we enter this constructor. - DateToSeconds(&k_sUNIXBase, &s_ulUNIXBaseSeconds); - } - - unsigned long ulSeconds; - - // get the time in UpTime units twice, with the time in seconds in the - // middle. - uint64_t ullFirstUpTime = force_cast(UpTime()); - GetDateTime(&ulSeconds); - uint64_t ullSecondUpTime = force_cast(UpTime()); - - // calculate the midpoint of the two UpTimes, and save that. - uint64_t ullAverageUpTime = (ullFirstUpTime + ullSecondUpTime) / 2ULL; - m_sStartupAbsoluteTime = force_cast(ullAverageUpTime); - - // save the number of seconds, recentered at the UNIX base. - m_ulStartupSeconds = ulSeconds - s_ulUNIXBaseSeconds; - } - - AbsoluteTime m_sStartupAbsoluteTime; - UInt32 m_ulStartupSeconds; -}; - -static startup_time_info g_sStartupTimeInfo; - -} // namespace detail - -#endif - - -int xtime_get(struct xtime* xtp, int clock_type) -{ - if (clock_type == TIME_UTC) - { -#if defined(BOOST_HAS_FTIME) - FILETIME ft; -# if defined(BOOST_NO_GETSYSTEMTIMEASFILETIME) - { - SYSTEMTIME st; - GetSystemTime(&st); - SystemTimeToFileTime(&st,&ft); - } -# else - GetSystemTimeAsFileTime(&ft); -# endif - static const boost::uint64_t TIMESPEC_TO_FILETIME_OFFSET = - UINT64_C(116444736000000000); - - const boost::uint64_t ft64 = - (static_cast(ft.dwHighDateTime) << 32) - + ft.dwLowDateTime; - - xtp->sec = static_cast( - (ft64 - TIMESPEC_TO_FILETIME_OFFSET) / 10000000 - ); - - xtp->nsec = static_cast( - ((ft64 - TIMESPEC_TO_FILETIME_OFFSET) % 10000000) * 100 - ); - - return clock_type; -#elif defined(BOOST_HAS_GETTIMEOFDAY) - struct timeval tv; -# ifndef NDEBUG - int res = -#endif - gettimeofday(&tv, 0); - assert(0 == res); - assert(tv.tv_sec >= 0); - assert(tv.tv_usec >= 0); - xtp->sec = tv.tv_sec; - xtp->nsec = tv.tv_usec * 1000; - return clock_type; -#elif defined(BOOST_HAS_CLOCK_GETTIME) - timespec ts; -# ifndef NDEBUG - int res = -# endif - clock_gettime(CLOCK_REALTIME, &ts); - assert(0 == res); - xtp->sec = ts.tv_sec; - xtp->nsec = ts.tv_nsec; - return clock_type; -#elif defined(BOOST_HAS_MPTASKS) - using detail::thread::force_cast; - // the Mac OS does not have an MP-safe way of getting the date/time, - // so we use a delta from the startup time. We _could_ defer this - // and use something that is interrupt-safe, but this would be _SLOW_, - // and we need speed here. - const uint64_t k_ullNanosecondsPerSecond(1000ULL * 1000ULL * 1000ULL); - AbsoluteTime sUpTime(UpTime()); - uint64_t ullNanoseconds( - force_cast( - AbsoluteDeltaToNanoseconds(sUpTime, - detail::g_sStartupTimeInfo.m_sStartupAbsoluteTime))); - uint64_t ullSeconds = (ullNanoseconds / k_ullNanosecondsPerSecond); - ullNanoseconds -= (ullSeconds * k_ullNanosecondsPerSecond); - xtp->sec = detail::g_sStartupTimeInfo.m_ulStartupSeconds + ullSeconds; - xtp->nsec = ullNanoseconds; - return clock_type; -#else -# error "xtime_get implementation undefined" -#endif - } - return 0; -} - -} // namespace boost - -// Change Log: -// 8 Feb 01 WEKEMPF Initial version. diff --git a/src/win32/xtime.cpp b/src/win32/xtime.cpp deleted file mode 100644 index 0ae854ff..00000000 --- a/src/win32/xtime.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (C) 2001-2003 -// William E. Kempf -// -// 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) - -#include - -#if defined(BOOST_HAS_FTIME) -# define __STDC_CONSTANT_MACROS -#endif - -#include - -#if defined(BOOST_HAS_FTIME) -# include -# include -#elif defined(BOOST_HAS_GETTIMEOFDAY) -# include -#elif defined(BOOST_HAS_MPTASKS) -# include -# include -#endif - -#include - -namespace boost { - -#ifdef BOOST_HAS_MPTASKS - -namespace detail -{ - -using thread::force_cast; - -struct startup_time_info -{ - startup_time_info() - { - // 1970 Jan 1 at 00:00:00 - static const DateTimeRec k_sUNIXBase = {1970, 1, 1, 0, 0, 0, 0}; - static unsigned long s_ulUNIXBaseSeconds = 0UL; - - if(s_ulUNIXBaseSeconds == 0UL) - { - // calculate the number of seconds between the Mac OS base and the - // UNIX base the first time we enter this constructor. - DateToSeconds(&k_sUNIXBase, &s_ulUNIXBaseSeconds); - } - - unsigned long ulSeconds; - - // get the time in UpTime units twice, with the time in seconds in the - // middle. - uint64_t ullFirstUpTime = force_cast(UpTime()); - GetDateTime(&ulSeconds); - uint64_t ullSecondUpTime = force_cast(UpTime()); - - // calculate the midpoint of the two UpTimes, and save that. - uint64_t ullAverageUpTime = (ullFirstUpTime + ullSecondUpTime) / 2ULL; - m_sStartupAbsoluteTime = force_cast(ullAverageUpTime); - - // save the number of seconds, recentered at the UNIX base. - m_ulStartupSeconds = ulSeconds - s_ulUNIXBaseSeconds; - } - - AbsoluteTime m_sStartupAbsoluteTime; - UInt32 m_ulStartupSeconds; -}; - -static startup_time_info g_sStartupTimeInfo; - -} // namespace detail - -#endif - - -int xtime_get(struct xtime* xtp, int clock_type) -{ - if (clock_type == TIME_UTC) - { -#if defined(BOOST_HAS_FTIME) - FILETIME ft; -# if defined(BOOST_NO_GETSYSTEMTIMEASFILETIME) - { - SYSTEMTIME st; - GetSystemTime(&st); - SystemTimeToFileTime(&st,&ft); - } -# else - GetSystemTimeAsFileTime(&ft); -# endif - static const boost::uint64_t TIMESPEC_TO_FILETIME_OFFSET = - UINT64_C(116444736000000000); - - const boost::uint64_t ft64 = - (static_cast(ft.dwHighDateTime) << 32) - + ft.dwLowDateTime; - - xtp->sec = static_cast( - (ft64 - TIMESPEC_TO_FILETIME_OFFSET) / 10000000 - ); - - xtp->nsec = static_cast( - ((ft64 - TIMESPEC_TO_FILETIME_OFFSET) % 10000000) * 100 - ); - - return clock_type; -#elif defined(BOOST_HAS_GETTIMEOFDAY) - struct timeval tv; -# ifndef NDEBUG - int res = -#endif - gettimeofday(&tv, 0); - assert(0 == res); - assert(tv.tv_sec >= 0); - assert(tv.tv_usec >= 0); - xtp->sec = tv.tv_sec; - xtp->nsec = tv.tv_usec * 1000; - return clock_type; -#elif defined(BOOST_HAS_CLOCK_GETTIME) - timespec ts; -# ifndef NDEBUG - int res = -# endif - clock_gettime(CLOCK_REALTIME, &ts); - assert(0 == res); - xtp->sec = ts.tv_sec; - xtp->nsec = ts.tv_nsec; - return clock_type; -#elif defined(BOOST_HAS_MPTASKS) - using detail::thread::force_cast; - // the Mac OS does not have an MP-safe way of getting the date/time, - // so we use a delta from the startup time. We _could_ defer this - // and use something that is interrupt-safe, but this would be _SLOW_, - // and we need speed here. - const uint64_t k_ullNanosecondsPerSecond(1000ULL * 1000ULL * 1000ULL); - AbsoluteTime sUpTime(UpTime()); - uint64_t ullNanoseconds( - force_cast( - AbsoluteDeltaToNanoseconds(sUpTime, - detail::g_sStartupTimeInfo.m_sStartupAbsoluteTime))); - uint64_t ullSeconds = (ullNanoseconds / k_ullNanosecondsPerSecond); - ullNanoseconds -= (ullSeconds * k_ullNanosecondsPerSecond); - xtp->sec = detail::g_sStartupTimeInfo.m_ulStartupSeconds + ullSeconds; - xtp->nsec = ullNanoseconds; - return clock_type; -#else -# error "xtime_get implementation undefined" -#endif - } - return 0; -} - -} // namespace boost - -// Change Log: -// 8 Feb 01 WEKEMPF Initial version. From 31e1566e1dee6a4e62b76667eed2dcc69423b363 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 2 Nov 2007 14:58:48 +0000 Subject: [PATCH 036/214] renamed cancellation to interruption [SVN r40685] --- .../thread/pthread/condition_variable.hpp | 8 +- .../thread/pthread/condition_variable_fwd.hpp | 2 +- include/boost/thread/pthread/shared_mutex.hpp | 14 ++-- include/boost/thread/pthread/thread.hpp | 34 ++++---- include/boost/thread/pthread/thread_data.hpp | 32 ++++---- .../boost/thread/win32/condition_variable.hpp | 2 +- include/boost/thread/win32/thread.hpp | 80 +++++++++---------- src/pthread/thread.cpp | 60 ++++++++------ src/win32/thread.cpp | 78 +++++++++--------- test/test_condition.cpp | 10 +-- test/test_thread.cpp | 34 ++++---- 11 files changed, 184 insertions(+), 170 deletions(-) diff --git a/include/boost/thread/pthread/condition_variable.hpp b/include/boost/thread/pthread/condition_variable.hpp index 84a1e8cb..0bf053ad 100644 --- a/include/boost/thread/pthread/condition_variable.hpp +++ b/include/boost/thread/pthread/condition_variable.hpp @@ -34,14 +34,14 @@ namespace boost inline void condition_variable::wait(unique_lock& m) { - detail::cancel_wrapper allow_cancel(&cond); + detail::interruption_checker check_for_interruption(&cond); int const cond_res=pthread_cond_wait(&cond,m.mutex()->native_handle()); BOOST_ASSERT(!cond_res); } inline bool condition_variable::timed_wait(unique_lock& m,boost::system_time const& wait_until) { - detail::cancel_wrapper allow_cancel(&cond); + detail::interruption_checker check_for_interruption(&cond); struct timespec const timeout=detail::get_timespec(wait_until); int const cond_res=pthread_cond_timedwait(&cond,m.mutex()->native_handle(),&timeout); if(cond_res==ETIMEDOUT) @@ -101,7 +101,7 @@ namespace boost { int res=0; { - detail::cancel_wrapper allow_cancel(&cond); + detail::interruption_checker check_for_interruption(&cond); { boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); m.unlock(); @@ -127,7 +127,7 @@ namespace boost struct timespec const timeout=detail::get_timespec(wait_until); int res=0; { - detail::cancel_wrapper allow_cancel(&cond); + detail::interruption_checker check_for_interruption(&cond); { boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); m.unlock(); diff --git a/include/boost/thread/pthread/condition_variable_fwd.hpp b/include/boost/thread/pthread/condition_variable_fwd.hpp index fb38f584..57865acb 100644 --- a/include/boost/thread/pthread/condition_variable_fwd.hpp +++ b/include/boost/thread/pthread/condition_variable_fwd.hpp @@ -19,7 +19,7 @@ namespace boost condition_variable(condition_variable&); condition_variable& operator=(condition_variable&); - struct cancel_wrapper; + struct interruption_checker; public: condition_variable(); ~condition_variable(); diff --git a/include/boost/thread/pthread/shared_mutex.hpp b/include/boost/thread/pthread/shared_mutex.hpp index d2ebb7a2..ca9a6eeb 100644 --- a/include/boost/thread/pthread/shared_mutex.hpp +++ b/include/boost/thread/pthread/shared_mutex.hpp @@ -53,7 +53,7 @@ namespace boost void lock_shared() { - boost::this_thread::disable_cancellation no_cancel; + boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); while(true) @@ -85,7 +85,7 @@ namespace boost bool timed_lock_shared(system_time const& timeout) { - boost::this_thread::disable_cancellation no_cancel; + boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); while(true) @@ -126,7 +126,7 @@ namespace boost void lock() { - boost::this_thread::disable_cancellation no_cancel; + boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); while(true) @@ -146,7 +146,7 @@ namespace boost bool timed_lock(system_time const& timeout) { - boost::this_thread::disable_cancellation no_cancel; + boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); while(true) @@ -193,7 +193,7 @@ namespace boost void lock_upgrade() { - boost::this_thread::disable_cancellation no_cancel; + boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); while(true) { @@ -210,7 +210,7 @@ namespace boost bool timed_lock_upgrade(system_time const& timeout) { - boost::this_thread::disable_cancellation no_cancel; + boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); while(true) { @@ -258,7 +258,7 @@ namespace boost void unlock_upgrade_and_lock() { - boost::this_thread::disable_cancellation no_cancel; + boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); --state.shared_count; while(true) diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index 9202262f..b3e786e1 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -165,8 +165,8 @@ namespace boost static void yield(); // extensions - void cancel(); - bool cancellation_requested() const; + void interrupt(); + bool interruption_requested() const; }; template @@ -187,25 +187,25 @@ namespace boost namespace this_thread { - class BOOST_THREAD_DECL disable_cancellation + class BOOST_THREAD_DECL disable_interruption { - disable_cancellation(const disable_cancellation&); - disable_cancellation& operator=(const disable_cancellation&); + disable_interruption(const disable_interruption&); + disable_interruption& operator=(const disable_interruption&); - bool cancel_was_enabled; - friend class restore_cancellation; + bool interruption_was_enabled; + friend class restore_interruption; public: - disable_cancellation(); - ~disable_cancellation(); + disable_interruption(); + ~disable_interruption(); }; - class BOOST_THREAD_DECL restore_cancellation + class BOOST_THREAD_DECL restore_interruption { - restore_cancellation(const restore_cancellation&); - restore_cancellation& operator=(const restore_cancellation&); + restore_interruption(const restore_interruption&); + restore_interruption& operator=(const restore_interruption&); public: - explicit restore_cancellation(disable_cancellation& d); - ~restore_cancellation(); + explicit restore_interruption(disable_interruption& d); + ~restore_interruption(); }; inline thread::id get_id() @@ -213,9 +213,9 @@ namespace boost return thread::id(pthread_self()); } - void BOOST_THREAD_DECL cancellation_point(); - bool BOOST_THREAD_DECL cancellation_enabled(); - bool BOOST_THREAD_DECL cancellation_requested(); + void BOOST_THREAD_DECL interruption_point(); + bool BOOST_THREAD_DECL interruption_enabled(); + bool BOOST_THREAD_DECL interruption_requested(); inline void yield() { diff --git a/include/boost/thread/pthread/thread_data.hpp b/include/boost/thread/pthread/thread_data.hpp index 93d2bbca..76e8f919 100644 --- a/include/boost/thread/pthread/thread_data.hpp +++ b/include/boost/thread/pthread/thread_data.hpp @@ -14,7 +14,7 @@ namespace boost { - class thread_cancelled + class thread_interrupted {}; namespace detail @@ -33,15 +33,15 @@ namespace boost bool join_started; bool joined; boost::detail::thread_exit_callback_node* thread_exit_callbacks; - bool cancel_enabled; - bool cancel_requested; + bool interrupt_enabled; + bool interrupt_requested; pthread_cond_t* current_cond; thread_data_base(): done(false),join_started(false),joined(false), thread_exit_callbacks(0), - cancel_enabled(true), - cancel_requested(false), + interrupt_enabled(true), + interrupt_requested(false), current_cond(0) {} virtual ~thread_data_base() @@ -52,37 +52,37 @@ namespace boost BOOST_THREAD_DECL thread_data_base* get_current_thread_data(); - class cancel_wrapper + class interrupt_wrapper { thread_data_base* const thread_info; - void check_cancel() + void check_for_interruption() { - if(thread_info->cancel_requested) + if(thread_info->interrupt_requested) { - thread_info->cancel_requested=false; - throw thread_cancelled(); + thread_info->interrupt_requested=false; + throw thread_interrupted(); } } public: - explicit cancel_wrapper(pthread_cond_t* cond): + explicit interrupt_wrapper(pthread_cond_t* cond): thread_info(detail::get_current_thread_data()) { - if(thread_info && thread_info->cancel_enabled) + if(thread_info && thread_info->interrupt_enabled) { lock_guard guard(thread_info->data_mutex); - check_cancel(); + check_for_interruption(); thread_info->current_cond=cond; } } - ~cancel_wrapper() + ~interrupt_wrapper() { - if(thread_info && thread_info->cancel_enabled) + if(thread_info && thread_info->interrupt_enabled) { lock_guard guard(thread_info->data_mutex); thread_info->current_cond=NULL; - check_cancel(); + check_for_interruption(); } } }; diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index 5f173e45..3f2407dd 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -156,7 +156,7 @@ namespace boost ++generations[0].count; sem=detail::win32::duplicate_handle(generations[0].semaphore); } - if(!this_thread::cancellable_wait(sem,::boost::detail::get_milliseconds_until(wait_until))) + if(!this_thread::interruptible_wait(sem,::boost::detail::get_milliseconds_until(wait_until))) { break; } diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index ebca1d23..a81c6ee6 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -21,7 +21,7 @@ namespace boost { - class thread_cancelled + class thread_interrupted {}; namespace detail @@ -32,16 +32,16 @@ namespace boost { long count; detail::win32::handle_manager thread_handle; - detail::win32::handle_manager cancel_handle; + detail::win32::handle_manager interruption_handle; boost::detail::thread_exit_callback_node* thread_exit_callbacks; - bool cancel_enabled; + bool interruption_enabled; unsigned id; thread_data_base(): count(0),thread_handle(detail::win32::invalid_handle_value), - cancel_handle(create_anonymous_event(detail::win32::manual_reset_event,detail::win32::event_initially_reset)), + interruption_handle(create_anonymous_event(detail::win32::manual_reset_event,detail::win32::event_initially_reset)), thread_exit_callbacks(0), - cancel_enabled(true), + interruption_enabled(true), id(0) {} virtual ~thread_data_base() @@ -153,10 +153,10 @@ namespace boost static void sleep(const system_time& xt); // extensions - class cancel_handle; - cancel_handle get_cancel_handle() const; - void cancel(); - bool cancellation_requested() const; + class interruption_handle; + interruption_handle get_interruption_handle() const; + void interrupt(); + bool interruption_requested() const; static thread self(); }; @@ -180,45 +180,45 @@ namespace boost namespace this_thread { - class BOOST_THREAD_DECL disable_cancellation + class BOOST_THREAD_DECL disable_interruption { - disable_cancellation(const disable_cancellation&); - disable_cancellation& operator=(const disable_cancellation&); + disable_interruption(const disable_interruption&); + disable_interruption& operator=(const disable_interruption&); - bool cancel_was_enabled; - friend class restore_cancellation; + bool interruption_was_enabled; + friend class restore_interruption; public: - disable_cancellation(); - ~disable_cancellation(); + disable_interruption(); + ~disable_interruption(); }; - class BOOST_THREAD_DECL restore_cancellation + class BOOST_THREAD_DECL restore_interruption { - restore_cancellation(const restore_cancellation&); - restore_cancellation& operator=(const restore_cancellation&); + restore_interruption(const restore_interruption&); + restore_interruption& operator=(const restore_interruption&); public: - explicit restore_cancellation(disable_cancellation& d); - ~restore_cancellation(); + explicit restore_interruption(disable_interruption& d); + ~restore_interruption(); }; thread::id BOOST_THREAD_DECL get_id(); - bool BOOST_THREAD_DECL cancellable_wait(detail::win32::handle handle_to_wait_for,unsigned long milliseconds); - inline bool cancellable_wait(unsigned long milliseconds) + bool BOOST_THREAD_DECL interruptible_wait(detail::win32::handle handle_to_wait_for,unsigned long milliseconds); + inline bool interruptible_wait(unsigned long milliseconds) { - return cancellable_wait(detail::win32::invalid_handle_value,milliseconds); + return interruptible_wait(detail::win32::invalid_handle_value,milliseconds); } - void BOOST_THREAD_DECL cancellation_point(); - bool BOOST_THREAD_DECL cancellation_enabled(); - bool BOOST_THREAD_DECL cancellation_requested(); - thread::cancel_handle BOOST_THREAD_DECL get_cancel_handle(); + void BOOST_THREAD_DECL interruption_point(); + bool BOOST_THREAD_DECL interruption_enabled(); + bool BOOST_THREAD_DECL interruption_requested(); + thread::interruption_handle BOOST_THREAD_DECL get_interruption_handle(); void BOOST_THREAD_DECL yield(); template void sleep(TimeDuration const& rel_time) { - cancellable_wait(static_cast(rel_time.total_milliseconds())); + interruptible_wait(static_cast(rel_time.total_milliseconds())); } } @@ -285,32 +285,32 @@ namespace boost return get_id()!=other.get_id(); } - class thread::cancel_handle + class thread::interruption_handle { private: boost::detail::win32::handle_manager handle; friend class thread; - friend cancel_handle this_thread::get_cancel_handle(); + friend interruption_handle this_thread::get_interruption_handle(); - cancel_handle(detail::win32::handle h_): + interruption_handle(detail::win32::handle h_): handle(h_) {} public: - cancel_handle(cancel_handle const& other): + interruption_handle(interruption_handle const& other): handle(other.handle.duplicate()) {} - cancel_handle(): + interruption_handle(): handle(0) {} - void swap(cancel_handle& other) + void swap(interruption_handle& other) { handle.swap(other.handle); } - cancel_handle& operator=(cancel_handle const& other) + interruption_handle& operator=(interruption_handle const& other) { - cancel_handle temp(other); + interruption_handle temp(other); swap(temp); return *this; } @@ -320,7 +320,7 @@ namespace boost handle=0; } - void cancel() + void interrupt() { if(handle) { @@ -328,10 +328,10 @@ namespace boost } } - typedef void(cancel_handle::*bool_type)(); + typedef void(interruption_handle::*bool_type)(); operator bool_type() const { - return handle?&cancel_handle::cancel:0; + return handle?&interruption_handle::interrupt:0; } }; diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 8b4581a0..14a9c22a 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -87,7 +87,7 @@ namespace boost { thread_info->run(); } - catch(thread_cancelled const&) + catch(thread_interrupted const&) { } catch(...) @@ -340,13 +340,13 @@ namespace boost } } - void thread::cancel() + void thread::interrupt() { boost::shared_ptr local_thread_info=get_thread_info(); if(local_thread_info) { lock_guard lk(local_thread_info->data_mutex); - local_thread_info->cancel_requested=true; + local_thread_info->interrupt_requested=true; if(local_thread_info->current_cond) { int const res=pthread_cond_broadcast(local_thread_info->current_cond); @@ -354,31 +354,45 @@ namespace boost } } } + + bool thread::interruption_requested() const + { + boost::shared_ptr local_thread_info=get_thread_info(); + if(local_thread_info) + { + lock_guard lk(local_thread_info->data_mutex); + return local_thread_info->interrupt_requested; + } + else + { + return false; + } + } namespace this_thread { - void cancellation_point() + void interruption_point() { boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data(); - if(thread_info && thread_info->cancel_enabled) + if(thread_info && thread_info->interrupt_enabled) { lock_guard lg(thread_info->data_mutex); - if(thread_info->cancel_requested) + if(thread_info->interrupt_requested) { - thread_info->cancel_requested=false; - throw thread_cancelled(); + thread_info->interrupt_requested=false; + throw thread_interrupted(); } } } - bool cancellation_enabled() + bool interruption_enabled() { boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data(); - return thread_info && thread_info->cancel_enabled; + return thread_info && thread_info->interrupt_enabled; } - bool cancellation_requested() + bool interruption_requested() { boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data(); if(!thread_info) @@ -388,40 +402,40 @@ namespace boost else { lock_guard lg(thread_info->data_mutex); - return thread_info->cancel_requested; + return thread_info->interrupt_requested; } } - disable_cancellation::disable_cancellation(): - cancel_was_enabled(cancellation_enabled()) + disable_interruption::disable_interruption(): + interruption_was_enabled(interruption_enabled()) { - if(cancel_was_enabled) + if(interruption_was_enabled) { - detail::get_current_thread_data()->cancel_enabled=false; + detail::get_current_thread_data()->interrupt_enabled=false; } } - disable_cancellation::~disable_cancellation() + disable_interruption::~disable_interruption() { if(detail::get_current_thread_data()) { - detail::get_current_thread_data()->cancel_enabled=cancel_was_enabled; + detail::get_current_thread_data()->interrupt_enabled=interruption_was_enabled; } } - restore_cancellation::restore_cancellation(disable_cancellation& d) + restore_interruption::restore_interruption(disable_interruption& d) { - if(d.cancel_was_enabled) + if(d.interruption_was_enabled) { - detail::get_current_thread_data()->cancel_enabled=true; + detail::get_current_thread_data()->interrupt_enabled=true; } } - restore_cancellation::~restore_cancellation() + restore_interruption::~restore_interruption() { if(detail::get_current_thread_data()) { - detail::get_current_thread_data()->cancel_enabled=false; + detail::get_current_thread_data()->interrupt_enabled=false; } } } diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index 466e5e51..8e62b9ad 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -166,7 +166,7 @@ namespace boost { thread_info->run(); } - catch(thread_cancelled const&) + catch(thread_interrupted const&) { } catch(...) @@ -204,7 +204,7 @@ namespace boost externally_launched_thread() { ++count; - cancel_enabled=false; + interruption_enabled=false; thread_handle=detail::win32::duplicate_handle(detail::win32::GetCurrentThread()); } @@ -266,10 +266,10 @@ namespace boost return local_thread_info?thread::id(local_thread_info->id):thread::id(); } - thread::cancel_handle thread::get_cancel_handle() const + thread::interruption_handle thread::get_interruption_handle() const { boost::intrusive_ptr local_thread_info=get_thread_info(); - return local_thread_info?thread::cancel_handle(local_thread_info->cancel_handle.duplicate()):thread::cancel_handle(); + return local_thread_info?thread::interruption_handle(local_thread_info->interruption_handle.duplicate()):thread::interruption_handle(); } bool thread::joinable() const @@ -282,7 +282,7 @@ namespace boost boost::intrusive_ptr local_thread_info=get_thread_info(); if(local_thread_info) { - this_thread::cancellable_wait(local_thread_info->thread_handle,detail::win32::infinite); + this_thread::interruptible_wait(local_thread_info->thread_handle,detail::win32::infinite); release_handle(); } } @@ -292,7 +292,7 @@ namespace boost boost::intrusive_ptr local_thread_info=get_thread_info(); if(local_thread_info) { - if(!this_thread::cancellable_wait(local_thread_info->thread_handle,get_milliseconds_until(wait_until))) + if(!this_thread::interruptible_wait(local_thread_info->thread_handle,get_milliseconds_until(wait_until))) { return false; } @@ -312,19 +312,19 @@ namespace boost thread_info=0; } - void thread::cancel() + void thread::interrupt() { boost::intrusive_ptr local_thread_info=get_thread_info(); if(local_thread_info) { - detail::win32::SetEvent(local_thread_info->cancel_handle); + detail::win32::SetEvent(local_thread_info->interruption_handle); } } - bool thread::cancellation_requested() const + bool thread::interruption_requested() const { boost::intrusive_ptr local_thread_info=get_thread_info(); - return local_thread_info.get() && (detail::win32::WaitForSingleObject(local_thread_info->cancel_handle,0)==0); + return local_thread_info.get() && (detail::win32::WaitForSingleObject(local_thread_info->interruption_handle,0)==0); } unsigned thread::hardware_concurrency() @@ -348,24 +348,24 @@ namespace boost namespace this_thread { - thread::cancel_handle get_cancel_handle() + thread::interruption_handle get_interruption_handle() { - return get_current_thread_data()?thread::cancel_handle(get_current_thread_data()->cancel_handle.duplicate()):thread::cancel_handle(); + return get_current_thread_data()?thread::interruption_handle(get_current_thread_data()->interruption_handle.duplicate()):thread::interruption_handle(); } - bool cancellable_wait(detail::win32::handle handle_to_wait_for,unsigned long milliseconds) + bool interruptible_wait(detail::win32::handle handle_to_wait_for,unsigned long milliseconds) { detail::win32::handle handles[2]={0}; unsigned handle_count=0; - unsigned cancel_index=~0U; + unsigned interruption_index=~0U; if(handle_to_wait_for!=detail::win32::invalid_handle_value) { handles[handle_count++]=handle_to_wait_for; } - if(get_current_thread_data() && get_current_thread_data()->cancel_enabled) + if(get_current_thread_data() && get_current_thread_data()->interruption_enabled) { - cancel_index=handle_count; - handles[handle_count++]=get_current_thread_data()->cancel_handle; + interruption_index=handle_count; + handles[handle_count++]=get_current_thread_data()->interruption_handle; } if(handle_count) @@ -375,10 +375,10 @@ namespace boost { return true; } - else if(notified_index==cancel_index) + else if(notified_index==interruption_index) { - detail::win32::ResetEvent(get_current_thread_data()->cancel_handle); - throw thread_cancelled(); + detail::win32::ResetEvent(get_current_thread_data()->interruption_handle); + throw thread_interrupted(); } } else @@ -393,23 +393,23 @@ namespace boost return thread::id(detail::win32::GetCurrentThreadId()); } - void cancellation_point() + void interruption_point() { - if(cancellation_enabled() && cancellation_requested()) + if(interruption_enabled() && interruption_requested()) { - detail::win32::ResetEvent(get_current_thread_data()->cancel_handle); - throw thread_cancelled(); + detail::win32::ResetEvent(get_current_thread_data()->interruption_handle); + throw thread_interrupted(); } } - bool cancellation_enabled() + bool interruption_enabled() { - return get_current_thread_data() && get_current_thread_data()->cancel_enabled; + return get_current_thread_data() && get_current_thread_data()->interruption_enabled; } - bool cancellation_requested() + bool interruption_requested() { - return get_current_thread_data() && (detail::win32::WaitForSingleObject(get_current_thread_data()->cancel_handle,0)==0); + return get_current_thread_data() && (detail::win32::WaitForSingleObject(get_current_thread_data()->interruption_handle,0)==0); } void yield() @@ -417,36 +417,36 @@ namespace boost detail::win32::Sleep(0); } - disable_cancellation::disable_cancellation(): - cancel_was_enabled(cancellation_enabled()) + disable_interruption::disable_interruption(): + interruption_was_enabled(interruption_enabled()) { - if(cancel_was_enabled) + if(interruption_was_enabled) { - get_current_thread_data()->cancel_enabled=false; + get_current_thread_data()->interruption_enabled=false; } } - disable_cancellation::~disable_cancellation() + disable_interruption::~disable_interruption() { if(get_current_thread_data()) { - get_current_thread_data()->cancel_enabled=cancel_was_enabled; + get_current_thread_data()->interruption_enabled=interruption_was_enabled; } } - restore_cancellation::restore_cancellation(disable_cancellation& d) + restore_interruption::restore_interruption(disable_interruption& d) { - if(d.cancel_was_enabled) + if(d.interruption_was_enabled) { - get_current_thread_data()->cancel_enabled=true; + get_current_thread_data()->interruption_enabled=true; } } - restore_cancellation::~restore_cancellation() + restore_interruption::~restore_interruption() { if(get_current_thread_data()) { - get_current_thread_data()->cancel_enabled=false; + get_current_thread_data()->interruption_enabled=false; } } } diff --git a/test/test_condition.cpp b/test/test_condition.cpp index dea55ead..b827be2c 100644 --- a/test/test_condition.cpp +++ b/test/test_condition.cpp @@ -189,21 +189,21 @@ void test_condition_waits() timed_test(&do_test_condition_waits, 12); } -void do_test_condition_wait_is_a_cancellation_point() +void do_test_condition_wait_is_a_interruption_point() { condition_test_data data; boost::thread thread(bind(&condition_test_thread, &data)); - thread.cancel(); + thread.interrupt(); thread.join(); BOOST_CHECK_EQUAL(data.awoken,0); } -void test_condition_wait_is_a_cancellation_point() +void test_condition_wait_is_a_interruption_point() { - timed_test(&do_test_condition_wait_is_a_cancellation_point, 1); + timed_test(&do_test_condition_wait_is_a_interruption_point, 1); } @@ -215,7 +215,7 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) test->add(BOOST_TEST_CASE(&test_condition_notify_one)); test->add(BOOST_TEST_CASE(&test_condition_notify_all)); test->add(BOOST_TEST_CASE(&test_condition_waits)); - test->add(BOOST_TEST_CASE(&test_condition_wait_is_a_cancellation_point)); + test->add(BOOST_TEST_CASE(&test_condition_wait_is_a_interruption_point)); return test; } diff --git a/test/test_thread.cpp b/test/test_thread.cpp index 13317192..5535deb0 100644 --- a/test/test_thread.cpp +++ b/test/test_thread.cpp @@ -71,53 +71,53 @@ void test_id_comparison() timed_test(&do_test_id_comparison, 1); } -void cancellation_point_thread(boost::mutex* m,bool* failed) +void interruption_point_thread(boost::mutex* m,bool* failed) { boost::mutex::scoped_lock lk(*m); - boost::this_thread::cancellation_point(); + boost::this_thread::interruption_point(); *failed=true; } -void do_test_thread_cancels_at_cancellation_point() +void do_test_thread_interrupts_at_interruption_point() { boost::mutex m; bool failed=false; boost::mutex::scoped_lock lk(m); - boost::thread thrd(boost::bind(&cancellation_point_thread,&m,&failed)); - thrd.cancel(); + boost::thread thrd(boost::bind(&interruption_point_thread,&m,&failed)); + thrd.interrupt(); lk.unlock(); thrd.join(); BOOST_CHECK(!failed); } -void test_thread_cancels_at_cancellation_point() +void test_thread_interrupts_at_interruption_point() { - timed_test(&do_test_thread_cancels_at_cancellation_point, 1); + timed_test(&do_test_thread_interrupts_at_interruption_point, 1); } -void disabled_cancellation_point_thread(boost::mutex* m,bool* failed) +void disabled_interruption_point_thread(boost::mutex* m,bool* failed) { boost::mutex::scoped_lock lk(*m); - boost::this_thread::disable_cancellation dc; - boost::this_thread::cancellation_point(); + boost::this_thread::disable_interruption dc; + boost::this_thread::interruption_point(); *failed=false; } -void do_test_thread_no_cancel_if_cancels_disabled_at_cancellation_point() +void do_test_thread_no_interrupt_if_interrupts_disabled_at_interruption_point() { boost::mutex m; bool failed=true; boost::mutex::scoped_lock lk(m); - boost::thread thrd(boost::bind(&disabled_cancellation_point_thread,&m,&failed)); - thrd.cancel(); + boost::thread thrd(boost::bind(&disabled_interruption_point_thread,&m,&failed)); + thrd.interrupt(); lk.unlock(); thrd.join(); BOOST_CHECK(!failed); } -void test_thread_no_cancel_if_cancels_disabled_at_cancellation_point() +void test_thread_no_interrupt_if_interrupts_disabled_at_interruption_point() { - timed_test(&do_test_thread_no_cancel_if_cancels_disabled_at_cancellation_point, 1); + timed_test(&do_test_thread_no_interrupt_if_interrupts_disabled_at_interruption_point, 1); } struct non_copyable_functor: @@ -207,8 +207,8 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) test->add(BOOST_TEST_CASE(test_sleep)); test->add(BOOST_TEST_CASE(test_creation)); test->add(BOOST_TEST_CASE(test_id_comparison)); - test->add(BOOST_TEST_CASE(test_thread_cancels_at_cancellation_point)); - test->add(BOOST_TEST_CASE(test_thread_no_cancel_if_cancels_disabled_at_cancellation_point)); + test->add(BOOST_TEST_CASE(test_thread_interrupts_at_interruption_point)); + test->add(BOOST_TEST_CASE(test_thread_no_interrupt_if_interrupts_disabled_at_interruption_point)); test->add(BOOST_TEST_CASE(test_creation_through_reference_wrapper)); test->add(BOOST_TEST_CASE(test_timed_join)); From 304156c20eb97763eda0e4728498666b02c128a8 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 2 Nov 2007 18:19:49 +0000 Subject: [PATCH 037/214] Fixed typo with interruption change [SVN r40692] --- include/boost/thread/pthread/condition_variable_fwd.hpp | 1 - include/boost/thread/pthread/thread_data.hpp | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/boost/thread/pthread/condition_variable_fwd.hpp b/include/boost/thread/pthread/condition_variable_fwd.hpp index 57865acb..d9d589db 100644 --- a/include/boost/thread/pthread/condition_variable_fwd.hpp +++ b/include/boost/thread/pthread/condition_variable_fwd.hpp @@ -19,7 +19,6 @@ namespace boost condition_variable(condition_variable&); condition_variable& operator=(condition_variable&); - struct interruption_checker; public: condition_variable(); ~condition_variable(); diff --git a/include/boost/thread/pthread/thread_data.hpp b/include/boost/thread/pthread/thread_data.hpp index 76e8f919..e055f8de 100644 --- a/include/boost/thread/pthread/thread_data.hpp +++ b/include/boost/thread/pthread/thread_data.hpp @@ -52,7 +52,7 @@ namespace boost BOOST_THREAD_DECL thread_data_base* get_current_thread_data(); - class interrupt_wrapper + class interruption_checker { thread_data_base* const thread_info; @@ -66,7 +66,7 @@ namespace boost } public: - explicit interrupt_wrapper(pthread_cond_t* cond): + explicit interruption_checker(pthread_cond_t* cond): thread_info(detail::get_current_thread_data()) { if(thread_info && thread_info->interrupt_enabled) @@ -76,7 +76,7 @@ namespace boost thread_info->current_cond=cond; } } - ~interrupt_wrapper() + ~interruption_checker() { if(thread_info && thread_info->interrupt_enabled) { From e56708d4aa7a22f9ade5ca0f995e5e19a316a35b Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Sat, 3 Nov 2007 22:00:12 +0000 Subject: [PATCH 038/214] added missing include [SVN r40730] --- include/boost/thread/pthread/shared_mutex.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/boost/thread/pthread/shared_mutex.hpp b/include/boost/thread/pthread/shared_mutex.hpp index ca9a6eeb..4cc095a2 100644 --- a/include/boost/thread/pthread/shared_mutex.hpp +++ b/include/boost/thread/pthread/shared_mutex.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace boost From 9f6b5d169a7b9bf8bc46a1994e590ee1ed52701c Mon Sep 17 00:00:00 2001 From: Roland Schwarz Date: Sun, 4 Nov 2007 17:17:01 +0000 Subject: [PATCH 039/214] Get rid of "unsused variable" warnings by making use of BOOST_VERIFY. This changeset is for pthread only. [SVN r40742] --- .../thread/pthread/condition_variable.hpp | 27 +++++-------- include/boost/thread/pthread/mutex.hpp | 30 +++++--------- include/boost/thread/pthread/once.hpp | 6 +-- .../pthread/pthread_mutex_scoped_lock.hpp | 6 +-- .../boost/thread/pthread/recursive_mutex.hpp | 39 +++++++------------ src/pthread/thread.cpp | 30 +++++--------- 6 files changed, 45 insertions(+), 93 deletions(-) diff --git a/include/boost/thread/pthread/condition_variable.hpp b/include/boost/thread/pthread/condition_variable.hpp index 0bf053ad..a6f74aea 100644 --- a/include/boost/thread/pthread/condition_variable.hpp +++ b/include/boost/thread/pthread/condition_variable.hpp @@ -28,15 +28,13 @@ namespace boost } inline condition_variable::~condition_variable() { - int const res=pthread_cond_destroy(&cond); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_cond_destroy(&cond)); } inline void condition_variable::wait(unique_lock& m) { detail::interruption_checker check_for_interruption(&cond); - int const cond_res=pthread_cond_wait(&cond,m.mutex()->native_handle()); - BOOST_ASSERT(!cond_res); + BOOST_VERIFY(0==pthread_cond_wait(&cond,m.mutex()->native_handle())); } inline bool condition_variable::timed_wait(unique_lock& m,boost::system_time const& wait_until) @@ -54,14 +52,12 @@ namespace boost inline void condition_variable::notify_one() { - int const res=pthread_cond_signal(&cond); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_cond_signal(&cond)); } inline void condition_variable::notify_all() { - int const res=pthread_cond_broadcast(&cond); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_cond_broadcast(&cond)); } class condition_variable_any @@ -83,17 +79,14 @@ namespace boost int const res2=pthread_cond_init(&cond,NULL); if(res2) { - int const destroy_res=pthread_mutex_destroy(&internal_mutex); - BOOST_ASSERT(!destroy_res); + BOOST_VERIFY(0==pthread_mutex_destroy(&internal_mutex)); throw thread_resource_error(); } } ~condition_variable_any() { - int const res=pthread_mutex_destroy(&internal_mutex); - BOOST_ASSERT(!res); - int const res2=pthread_cond_destroy(&cond); - BOOST_ASSERT(!res2); + BOOST_VERIFY(0==pthread_mutex_destroy(&internal_mutex)); + BOOST_VERIFY(0==pthread_cond_destroy(&cond)); } template @@ -160,15 +153,13 @@ namespace boost void notify_one() { boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); - int const res=pthread_cond_signal(&cond); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_cond_signal(&cond)); } void notify_all() { boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); - int const res=pthread_cond_broadcast(&cond); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_cond_broadcast(&cond)); } }; diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index 7483686d..fafca729 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -42,20 +42,17 @@ namespace boost } ~mutex() { - int const res=pthread_mutex_destroy(&m); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_destroy(&m)); } void lock() { - int const res=pthread_mutex_lock(&m); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_lock(&m)); } void unlock() { - int const res=pthread_mutex_unlock(&m); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_unlock(&m)); } bool try_lock() @@ -98,8 +95,7 @@ namespace boost int const res2=pthread_cond_init(&cond,NULL); if(res2) { - int const destroy_res=pthread_mutex_destroy(&m); - BOOST_ASSERT(!destroy_res); + BOOST_VERIFY(0==pthread_mutex_destroy(&m)); throw thread_resource_error(); } is_locked=false; @@ -107,11 +103,9 @@ namespace boost } ~timed_mutex() { - int const res=pthread_mutex_destroy(&m); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_destroy(&m)); #ifndef BOOST_PTHREAD_HAS_TIMEDLOCK - int const res2=pthread_cond_destroy(&cond); - BOOST_ASSERT(!res2); + BOOST_VERIFY(0==pthread_cond_destroy(&cond)); #endif } @@ -124,14 +118,12 @@ namespace boost #ifdef BOOST_PTHREAD_HAS_TIMEDLOCK void lock() { - int const res=pthread_mutex_lock(&m); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_lock(&m)); } void unlock() { - int const res=pthread_mutex_unlock(&m); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_unlock(&m)); } bool try_lock() @@ -153,8 +145,7 @@ namespace boost boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); while(is_locked) { - int const cond_res=pthread_cond_wait(&cond,&m); - BOOST_ASSERT(!cond_res); + BOOST_VERIFY(0==pthread_cond_wait(&cond,&m)); } is_locked=true; } @@ -163,8 +154,7 @@ namespace boost { boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); is_locked=false; - int const res=pthread_cond_signal(&cond); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_cond_signal(&cond)); } bool try_lock() diff --git a/include/boost/thread/pthread/once.hpp b/include/boost/thread/pthread/once.hpp index 8453569f..5fa3a7dd 100644 --- a/include/boost/thread/pthread/once.hpp +++ b/include/boost/thread/pthread/once.hpp @@ -33,13 +33,11 @@ namespace boost { explicit pthread_mutex_scoped_lock(pthread_mutex_t* mutex_): mutex(mutex_) { - int const res=pthread_mutex_lock(mutex); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_lock(mutex)); } ~pthread_mutex_scoped_lock() { - int const res=pthread_mutex_unlock(mutex); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_unlock(mutex)); } }; } diff --git a/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp b/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp index a691284a..bde161d5 100644 --- a/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp +++ b/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp @@ -14,13 +14,11 @@ namespace boost explicit pthread_mutex_scoped_lock(pthread_mutex_t* m_): m(m_) { - int const res=pthread_mutex_lock(m); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_lock(m)); } ~pthread_mutex_scoped_lock() { - int const res=pthread_mutex_unlock(m); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_unlock(m)); } }; diff --git a/include/boost/thread/pthread/recursive_mutex.hpp b/include/boost/thread/pthread/recursive_mutex.hpp index f7ea9300..eb5b367b 100644 --- a/include/boost/thread/pthread/recursive_mutex.hpp +++ b/include/boost/thread/pthread/recursive_mutex.hpp @@ -53,25 +53,21 @@ namespace boost { throw thread_resource_error(); } - int const destroy_attr_res=pthread_mutexattr_destroy(&attr); - BOOST_ASSERT(!destroy_attr_res); + BOOST_VERIFY(0==pthread_mutexattr_destroy(&attr)); } ~recursive_mutex() { - int const res=pthread_mutex_destroy(&m); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_destroy(&m)); } void lock() { - int const res=pthread_mutex_lock(&m); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_lock(&m)); } void unlock() { - int const res=pthread_mutex_unlock(&m); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_unlock(&m)); } bool try_lock() @@ -117,12 +113,10 @@ namespace boost int const res=pthread_mutex_init(&m,&attr); if(res) { - int const destroy_attr_res=pthread_mutexattr_destroy(&attr); - BOOST_ASSERT(!destroy_attr_res); + BOOST_VERIFY(0==pthread_mutexattr_destroy(&attr)); throw thread_resource_error(); } - int const destroy_attr_res=pthread_mutexattr_destroy(&attr); - BOOST_ASSERT(!destroy_attr_res); + BOOST_VERIFY(0==pthread_mutexattr_destroy(&attr)); #else int const res=pthread_mutex_init(&m,NULL); if(res) @@ -132,8 +126,7 @@ namespace boost int const res2=pthread_cond_init(&cond,NULL); if(res2) { - int const destroy_res=pthread_mutex_destroy(&m); - BOOST_ASSERT(!destroy_res); + BOOST_VERIFY(0==pthread_mutex_destroy(&m)); throw thread_resource_error(); } is_locked=false; @@ -142,11 +135,9 @@ namespace boost } ~recursive_timed_mutex() { - int const res=pthread_mutex_destroy(&m); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_destroy(&m)); #ifndef BOOST_PTHREAD_HAS_TIMEDLOCK - int const res2=pthread_cond_destroy(&cond); - BOOST_ASSERT(!res2); + BOOST_VERIFY(0==pthread_cond_destroy(&cond)); #endif } @@ -159,14 +150,12 @@ namespace boost #ifdef BOOST_PTHREAD_HAS_TIMEDLOCK void lock() { - int const res=pthread_mutex_lock(&m); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_lock(&m)); } void unlock() { - int const res=pthread_mutex_unlock(&m); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_mutex_unlock(&m)); } bool try_lock() @@ -194,8 +183,7 @@ namespace boost while(is_locked) { - int const cond_res=pthread_cond_wait(&cond,&m); - BOOST_ASSERT(!cond_res); + BOOST_VERIFY(0==pthread_cond_wait(&cond,&m)); } is_locked=true; ++count; @@ -209,8 +197,7 @@ namespace boost { is_locked=false; } - int const res=pthread_cond_signal(&cond); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_cond_signal(&cond)); } bool try_lock() diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 14a9c22a..4e4eb747 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -55,8 +55,7 @@ namespace boost void create_current_thread_tls_key() { - int const res=pthread_key_create(¤t_thread_tls_key,NULL); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_key_create(¤t_thread_tls_key,NULL)); } } @@ -69,8 +68,7 @@ namespace boost void set_current_thread_data(detail::thread_data_base* new_data) { boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); - int const res=pthread_setspecific(current_thread_tls_key,new_data); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_setspecific(current_thread_tls_key,new_data)); } } @@ -172,8 +170,7 @@ namespace boost if(do_join) { void* result=0; - int const res=pthread_join(local_thread_info->thread_handle,&result); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_join(local_thread_info->thread_handle,&result)); lock_guard lock(local_thread_info->data_mutex); local_thread_info->joined=true; local_thread_info->done_condition.notify_all(); @@ -220,8 +217,7 @@ namespace boost if(do_join) { void* result=0; - int const res=pthread_join(local_thread_info->thread_handle,&result); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_join(local_thread_info->thread_handle,&result)); lock_guard lock(local_thread_info->data_mutex); local_thread_info->joined=true; local_thread_info->done_condition.notify_all(); @@ -255,8 +251,7 @@ namespace boost lock_guard lock(local_thread_info->data_mutex); if(!local_thread_info->join_started) { - int const res=pthread_detach(local_thread_info->thread_handle); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_detach(local_thread_info->thread_handle)); local_thread_info->join_started=true; local_thread_info->joined=true; } @@ -281,9 +276,7 @@ namespace boost # if defined(BOOST_HAS_PTHREAD_DELAY_NP) timespec ts; to_timespec_duration(xt, ts); - int res = 0; - res = pthread_delay_np(&ts); - BOOST_ASSERT(res == 0); + BOOST_VERIFY(0==pthread_delay_np(&ts)); # elif defined(BOOST_HAS_NANOSLEEP) timespec ts; to_timespec_duration(xt, ts); @@ -308,13 +301,9 @@ namespace boost void thread::yield() { # if defined(BOOST_HAS_SCHED_YIELD) - int res = 0; - res = sched_yield(); - BOOST_ASSERT(res == 0); + BOOST_VERIFY(0==sched_yield()); # elif defined(BOOST_HAS_PTHREAD_YIELD) - int res = 0; - res = pthread_yield(); - BOOST_ASSERT(res == 0); + BOOST_VERIFY(0==pthread_yield()); # else xtime xt; xtime_get(&xt, TIME_UTC); @@ -349,8 +338,7 @@ namespace boost local_thread_info->interrupt_requested=true; if(local_thread_info->current_cond) { - int const res=pthread_cond_broadcast(local_thread_info->current_cond); - BOOST_ASSERT(!res); + BOOST_VERIFY(0==pthread_cond_broadcast(local_thread_info->current_cond)); } } } From d2f82300932c5cf61a06ce6320449743c2726538 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 5 Nov 2007 10:15:24 +0000 Subject: [PATCH 040/214] threadapi is a composite feature again [SVN r40774] --- build/Jamfile.v2 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 36525b22..4facff76 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -53,8 +53,9 @@ local rule default_threadapi ( ) return $(api) ; } -feature.feature threadapi : pthread win32 : propagated ; +feature.feature threadapi : pthread win32 : propagated composite ; feature.set-default threadapi : [ default_threadapi ] ; +feature.compose pthread : BOOST_THREAD_POSIX ; rule tag ( name : type ? : property-set ) { From 89f2032c0db10619b4cfe901b6319e1862f71cab Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 5 Nov 2007 14:16:21 +0000 Subject: [PATCH 041/214] Use pthread_equal for comparing pthread_t IDs; use BOOST_VERIFY instead of BOOST_ASSERT in many places in order to avoid unused variable warnings [SVN r40787] --- .../thread/pthread/condition_variable.hpp | 18 +++++------ include/boost/thread/pthread/mutex.hpp | 20 ++++++------ include/boost/thread/pthread/once.hpp | 25 +++------------ .../pthread/pthread_mutex_scoped_lock.hpp | 4 +-- .../boost/thread/pthread/recursive_mutex.hpp | 32 +++++++++---------- .../boost/thread/win32/basic_timed_mutex.hpp | 3 +- .../boost/thread/win32/condition_variable.hpp | 3 +- include/boost/thread/win32/once.hpp | 6 ++-- include/boost/thread/win32/shared_mutex.hpp | 21 ++++-------- .../boost/thread/win32/thread_primitives.hpp | 6 ++-- src/pthread/thread.cpp | 18 +++++------ src/pthread/tss.cpp | 5 --- 12 files changed, 63 insertions(+), 98 deletions(-) diff --git a/include/boost/thread/pthread/condition_variable.hpp b/include/boost/thread/pthread/condition_variable.hpp index a6f74aea..7306ae3e 100644 --- a/include/boost/thread/pthread/condition_variable.hpp +++ b/include/boost/thread/pthread/condition_variable.hpp @@ -28,13 +28,13 @@ namespace boost } inline condition_variable::~condition_variable() { - BOOST_VERIFY(0==pthread_cond_destroy(&cond)); + BOOST_VERIFY(!pthread_cond_destroy(&cond)); } inline void condition_variable::wait(unique_lock& m) { detail::interruption_checker check_for_interruption(&cond); - BOOST_VERIFY(0==pthread_cond_wait(&cond,m.mutex()->native_handle())); + BOOST_VERIFY(!pthread_cond_wait(&cond,m.mutex()->native_handle())); } inline bool condition_variable::timed_wait(unique_lock& m,boost::system_time const& wait_until) @@ -52,12 +52,12 @@ namespace boost inline void condition_variable::notify_one() { - BOOST_VERIFY(0==pthread_cond_signal(&cond)); + BOOST_VERIFY(!pthread_cond_signal(&cond)); } inline void condition_variable::notify_all() { - BOOST_VERIFY(0==pthread_cond_broadcast(&cond)); + BOOST_VERIFY(!pthread_cond_broadcast(&cond)); } class condition_variable_any @@ -79,14 +79,14 @@ namespace boost int const res2=pthread_cond_init(&cond,NULL); if(res2) { - BOOST_VERIFY(0==pthread_mutex_destroy(&internal_mutex)); + BOOST_VERIFY(!pthread_mutex_destroy(&internal_mutex)); throw thread_resource_error(); } } ~condition_variable_any() { - BOOST_VERIFY(0==pthread_mutex_destroy(&internal_mutex)); - BOOST_VERIFY(0==pthread_cond_destroy(&cond)); + BOOST_VERIFY(!pthread_mutex_destroy(&internal_mutex)); + BOOST_VERIFY(!pthread_cond_destroy(&cond)); } template @@ -153,13 +153,13 @@ namespace boost void notify_one() { boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); - BOOST_VERIFY(0==pthread_cond_signal(&cond)); + BOOST_VERIFY(!pthread_cond_signal(&cond)); } void notify_all() { boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); - BOOST_VERIFY(0==pthread_cond_broadcast(&cond)); + BOOST_VERIFY(!pthread_cond_broadcast(&cond)); } }; diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index fafca729..89a2bb94 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -42,17 +42,17 @@ namespace boost } ~mutex() { - BOOST_VERIFY(0==pthread_mutex_destroy(&m)); + BOOST_VERIFY(!pthread_mutex_destroy(&m)); } void lock() { - BOOST_VERIFY(0==pthread_mutex_lock(&m)); + BOOST_VERIFY(!pthread_mutex_lock(&m)); } void unlock() { - BOOST_VERIFY(0==pthread_mutex_unlock(&m)); + BOOST_VERIFY(!pthread_mutex_unlock(&m)); } bool try_lock() @@ -95,7 +95,7 @@ namespace boost int const res2=pthread_cond_init(&cond,NULL); if(res2) { - BOOST_VERIFY(0==pthread_mutex_destroy(&m)); + BOOST_VERIFY(!pthread_mutex_destroy(&m)); throw thread_resource_error(); } is_locked=false; @@ -103,9 +103,9 @@ namespace boost } ~timed_mutex() { - BOOST_VERIFY(0==pthread_mutex_destroy(&m)); + BOOST_VERIFY(!pthread_mutex_destroy(&m)); #ifndef BOOST_PTHREAD_HAS_TIMEDLOCK - BOOST_VERIFY(0==pthread_cond_destroy(&cond)); + BOOST_VERIFY(!pthread_cond_destroy(&cond)); #endif } @@ -118,12 +118,12 @@ namespace boost #ifdef BOOST_PTHREAD_HAS_TIMEDLOCK void lock() { - BOOST_VERIFY(0==pthread_mutex_lock(&m)); + BOOST_VERIFY(!pthread_mutex_lock(&m)); } void unlock() { - BOOST_VERIFY(0==pthread_mutex_unlock(&m)); + BOOST_VERIFY(!pthread_mutex_unlock(&m)); } bool try_lock() @@ -145,7 +145,7 @@ namespace boost boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); while(is_locked) { - BOOST_VERIFY(0==pthread_cond_wait(&cond,&m)); + BOOST_VERIFY(!pthread_cond_wait(&cond,&m)); } is_locked=true; } @@ -154,7 +154,7 @@ namespace boost { boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); is_locked=false; - BOOST_VERIFY(0==pthread_cond_signal(&cond)); + BOOST_VERIFY(!pthread_cond_signal(&cond)); } bool try_lock() diff --git a/include/boost/thread/pthread/once.hpp b/include/boost/thread/pthread/once.hpp index 5fa3a7dd..7d8e8eb3 100644 --- a/include/boost/thread/pthread/once.hpp +++ b/include/boost/thread/pthread/once.hpp @@ -13,45 +13,28 @@ #include #include +#include "pthread_mutex_scoped_lock.hpp" namespace boost { struct once_flag { pthread_mutex_t mutex; - unsigned flag; + unsigned long flag; }; #define BOOST_ONCE_INIT {PTHREAD_MUTEX_INITIALIZER,0} - namespace detail - { - struct pthread_mutex_scoped_lock - { - pthread_mutex_t * mutex; - - explicit pthread_mutex_scoped_lock(pthread_mutex_t* mutex_): - mutex(mutex_) - { - BOOST_VERIFY(0==pthread_mutex_lock(mutex)); - } - ~pthread_mutex_scoped_lock() - { - BOOST_VERIFY(0==pthread_mutex_unlock(mutex)); - } - }; - } - template void call_once(once_flag& flag,Function f) { - long const function_complete_flag_value=0xc15730e2; + unsigned long const function_complete_flag_value=0xc15730e2ul; #ifdef BOOST_PTHREAD_HAS_ATOMICS if(::boost::detail::interlocked_read_acquire(&flag.flag)!=function_complete_flag_value) { #endif - detail::pthread_mutex_scoped_lock const lock(&flag.mutex); + pthread::pthread_mutex_scoped_lock const lock(&flag.mutex); if(flag.flag!=function_complete_flag_value) { f(); diff --git a/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp b/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp index bde161d5..e00e9477 100644 --- a/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp +++ b/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp @@ -14,11 +14,11 @@ namespace boost explicit pthread_mutex_scoped_lock(pthread_mutex_t* m_): m(m_) { - BOOST_VERIFY(0==pthread_mutex_lock(m)); + BOOST_VERIFY(!pthread_mutex_lock(m)); } ~pthread_mutex_scoped_lock() { - BOOST_VERIFY(0==pthread_mutex_unlock(m)); + BOOST_VERIFY(!pthread_mutex_unlock(m)); } }; diff --git a/include/boost/thread/pthread/recursive_mutex.hpp b/include/boost/thread/pthread/recursive_mutex.hpp index eb5b367b..f496822d 100644 --- a/include/boost/thread/pthread/recursive_mutex.hpp +++ b/include/boost/thread/pthread/recursive_mutex.hpp @@ -53,21 +53,21 @@ namespace boost { throw thread_resource_error(); } - BOOST_VERIFY(0==pthread_mutexattr_destroy(&attr)); + BOOST_VERIFY(!pthread_mutexattr_destroy(&attr)); } ~recursive_mutex() { - BOOST_VERIFY(0==pthread_mutex_destroy(&m)); + BOOST_VERIFY(!pthread_mutex_destroy(&m)); } void lock() { - BOOST_VERIFY(0==pthread_mutex_lock(&m)); + BOOST_VERIFY(!pthread_mutex_lock(&m)); } void unlock() { - BOOST_VERIFY(0==pthread_mutex_unlock(&m)); + BOOST_VERIFY(!pthread_mutex_unlock(&m)); } bool try_lock() @@ -113,10 +113,10 @@ namespace boost int const res=pthread_mutex_init(&m,&attr); if(res) { - BOOST_VERIFY(0==pthread_mutexattr_destroy(&attr)); + BOOST_VERIFY(!pthread_mutexattr_destroy(&attr)); throw thread_resource_error(); } - BOOST_VERIFY(0==pthread_mutexattr_destroy(&attr)); + BOOST_VERIFY(!pthread_mutexattr_destroy(&attr)); #else int const res=pthread_mutex_init(&m,NULL); if(res) @@ -126,7 +126,7 @@ namespace boost int const res2=pthread_cond_init(&cond,NULL); if(res2) { - BOOST_VERIFY(0==pthread_mutex_destroy(&m)); + BOOST_VERIFY(!pthread_mutex_destroy(&m)); throw thread_resource_error(); } is_locked=false; @@ -135,9 +135,9 @@ namespace boost } ~recursive_timed_mutex() { - BOOST_VERIFY(0==pthread_mutex_destroy(&m)); + BOOST_VERIFY(!pthread_mutex_destroy(&m)); #ifndef BOOST_PTHREAD_HAS_TIMEDLOCK - BOOST_VERIFY(0==pthread_cond_destroy(&cond)); + BOOST_VERIFY(!pthread_cond_destroy(&cond)); #endif } @@ -150,12 +150,12 @@ namespace boost #ifdef BOOST_PTHREAD_HAS_TIMEDLOCK void lock() { - BOOST_VERIFY(0==pthread_mutex_lock(&m)); + BOOST_VERIFY(!pthread_mutex_lock(&m)); } void unlock() { - BOOST_VERIFY(0==pthread_mutex_unlock(&m)); + BOOST_VERIFY(!pthread_mutex_unlock(&m)); } bool try_lock() @@ -175,7 +175,7 @@ namespace boost void lock() { boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); - if(is_locked && owner==pthread_self()) + if(is_locked && pthread_equal(owner,pthread_self())) { ++count; return; @@ -183,7 +183,7 @@ namespace boost while(is_locked) { - BOOST_VERIFY(0==pthread_cond_wait(&cond,&m)); + BOOST_VERIFY(!pthread_cond_wait(&cond,&m)); } is_locked=true; ++count; @@ -197,13 +197,13 @@ namespace boost { is_locked=false; } - BOOST_VERIFY(0==pthread_cond_signal(&cond)); + BOOST_VERIFY(!pthread_cond_signal(&cond)); } bool try_lock() { boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); - if(is_locked && owner!=pthread_self()) + if(is_locked && !pthread_equal(owner,pthread_self())) { return false; } @@ -217,7 +217,7 @@ namespace boost { struct timespec const timeout=detail::get_timespec(abs_time); boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); - if(is_locked && owner==pthread_self()) + if(is_locked && pthread_equal(owner,pthread_self())) { ++count; return true; diff --git a/include/boost/thread/win32/basic_timed_mutex.hpp b/include/boost/thread/win32/basic_timed_mutex.hpp index 6fdd40ab..fef19d7b 100644 --- a/include/boost/thread/win32/basic_timed_mutex.hpp +++ b/include/boost/thread/win32/basic_timed_mutex.hpp @@ -59,8 +59,7 @@ namespace boost void lock() { - bool const success=timed_lock(::boost::detail::get_system_time_sentinel()); - BOOST_ASSERT(success); + BOOST_VERIFY(timed_lock(::boost::detail::get_system_time_sentinel())); } bool timed_lock(::boost::system_time const& wait_until) { diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index 3f2407dd..c3861dd9 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -79,8 +79,7 @@ namespace boost { if(entry.semaphore) { - unsigned long const close_result=detail::win32::CloseHandle(entry.semaphore); - BOOST_ASSERT(close_result); + BOOST_VERIFY(detail::win32::CloseHandle(entry.semaphore)); entry.semaphore=0; } entry.notified=false; diff --git a/include/boost/thread/win32/once.hpp b/include/boost/thread/win32/once.hpp index 9a79d331..d1a525de 100644 --- a/include/boost/thread/win32/once.hpp +++ b/include/boost/thread/win32/once.hpp @@ -40,13 +40,11 @@ namespace boost explicit win32_mutex_scoped_lock(void* mutex_handle_): mutex_handle(mutex_handle_) { - unsigned long const res=win32::WaitForSingleObject(mutex_handle,win32::infinite); - BOOST_ASSERT(!res); + BOOST_VERIFY(!win32::WaitForSingleObject(mutex_handle,win32::infinite)); } ~win32_mutex_scoped_lock() { - bool const success=win32::ReleaseMutex(mutex_handle)!=0; - BOOST_ASSERT(success); + BOOST_VERIFY(win32::ReleaseMutex(mutex_handle)!=0); } }; diff --git a/include/boost/thread/win32/shared_mutex.hpp b/include/boost/thread/win32/shared_mutex.hpp index 2b0cf8d8..992491d5 100644 --- a/include/boost/thread/win32/shared_mutex.hpp +++ b/include/boost/thread/win32/shared_mutex.hpp @@ -57,14 +57,12 @@ namespace boost { if(old_state.exclusive_waiting) { - bool const success=detail::win32::ReleaseSemaphore(exclusive_sem,1,NULL)!=0; - BOOST_ASSERT(success); + BOOST_VERIFY(detail::win32::ReleaseSemaphore(exclusive_sem,1,NULL)!=0); } if(old_state.shared_waiting || old_state.exclusive_waiting) { - bool const success=detail::win32::ReleaseSemaphore(unlock_sem,old_state.shared_waiting + (old_state.exclusive_waiting?1:0),NULL)!=0; - BOOST_ASSERT(success); + BOOST_VERIFY(detail::win32::ReleaseSemaphore(unlock_sem,old_state.shared_waiting + (old_state.exclusive_waiting?1:0),NULL)!=0); } } @@ -112,8 +110,7 @@ namespace boost void lock_shared() { - bool const success=timed_lock_shared(::boost::detail::get_system_time_sentinel()); - BOOST_ASSERT(success); + BOOST_VERIFY(timed_lock_shared(::boost::detail::get_system_time_sentinel())); } bool timed_lock_shared(boost::system_time const& wait_until) @@ -218,8 +215,7 @@ namespace boost { if(old_state.upgrade) { - bool const success=detail::win32::ReleaseSemaphore(upgrade_sem,1,NULL)!=0; - BOOST_ASSERT(success); + BOOST_VERIFY(detail::win32::ReleaseSemaphore(upgrade_sem,1,NULL)!=0); } else { @@ -235,8 +231,7 @@ namespace boost void lock() { - bool const success=timed_lock(::boost::detail::get_system_time_sentinel()); - BOOST_ASSERT(success); + BOOST_VERIFY(timed_lock(::boost::detail::get_system_time_sentinel())); } bool timed_lock(boost::system_time const& wait_until) @@ -364,8 +359,7 @@ namespace boost return; } - unsigned long const res=detail::win32::WaitForSingleObject(unlock_sem,detail::win32::infinite); - BOOST_ASSERT(res==0); + BOOST_VERIFY(!detail::win32::WaitForSingleObject(unlock_sem,detail::win32::infinite)); } } @@ -421,8 +415,7 @@ namespace boost { if(!last_reader) { - unsigned long const res=detail::win32::WaitForSingleObject(upgrade_sem,detail::win32::infinite); - BOOST_ASSERT(res==0); + BOOST_VERIFY(!detail::win32::WaitForSingleObject(upgrade_sem,detail::win32::infinite)); } break; } diff --git a/include/boost/thread/win32/thread_primitives.hpp b/include/boost/thread/win32/thread_primitives.hpp index 89fecec7..8f6bc0ed 100644 --- a/include/boost/thread/win32/thread_primitives.hpp +++ b/include/boost/thread/win32/thread_primitives.hpp @@ -204,8 +204,7 @@ namespace boost inline void release_semaphore(handle semaphore,long count) { - bool const success=ReleaseSemaphore(semaphore,count,0)!=0; - BOOST_ASSERT(success); + BOOST_VERIFY(ReleaseSemaphore(semaphore,count,0)!=0); } class handle_manager @@ -219,8 +218,7 @@ namespace boost { if(handle_to_manage && handle_to_manage!=invalid_handle_value) { - unsigned long const result=CloseHandle(handle_to_manage); - BOOST_ASSERT(result); + BOOST_VERIFY(CloseHandle(handle_to_manage)); } } diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 4e4eb747..87a565ae 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -55,7 +55,7 @@ namespace boost void create_current_thread_tls_key() { - BOOST_VERIFY(0==pthread_key_create(¤t_thread_tls_key,NULL)); + BOOST_VERIFY(!pthread_key_create(¤t_thread_tls_key,NULL)); } } @@ -68,7 +68,7 @@ namespace boost void set_current_thread_data(detail::thread_data_base* new_data) { boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); - BOOST_VERIFY(0==pthread_setspecific(current_thread_tls_key,new_data)); + BOOST_VERIFY(!pthread_setspecific(current_thread_tls_key,new_data)); } } @@ -170,7 +170,7 @@ namespace boost if(do_join) { void* result=0; - BOOST_VERIFY(0==pthread_join(local_thread_info->thread_handle,&result)); + BOOST_VERIFY(!pthread_join(local_thread_info->thread_handle,&result)); lock_guard lock(local_thread_info->data_mutex); local_thread_info->joined=true; local_thread_info->done_condition.notify_all(); @@ -217,7 +217,7 @@ namespace boost if(do_join) { void* result=0; - BOOST_VERIFY(0==pthread_join(local_thread_info->thread_handle,&result)); + BOOST_VERIFY(!pthread_join(local_thread_info->thread_handle,&result)); lock_guard lock(local_thread_info->data_mutex); local_thread_info->joined=true; local_thread_info->done_condition.notify_all(); @@ -251,7 +251,7 @@ namespace boost lock_guard lock(local_thread_info->data_mutex); if(!local_thread_info->join_started) { - BOOST_VERIFY(0==pthread_detach(local_thread_info->thread_handle)); + BOOST_VERIFY(!pthread_detach(local_thread_info->thread_handle)); local_thread_info->join_started=true; local_thread_info->joined=true; } @@ -276,7 +276,7 @@ namespace boost # if defined(BOOST_HAS_PTHREAD_DELAY_NP) timespec ts; to_timespec_duration(xt, ts); - BOOST_VERIFY(0==pthread_delay_np(&ts)); + BOOST_VERIFY(!pthread_delay_np(&ts)); # elif defined(BOOST_HAS_NANOSLEEP) timespec ts; to_timespec_duration(xt, ts); @@ -301,9 +301,9 @@ namespace boost void thread::yield() { # if defined(BOOST_HAS_SCHED_YIELD) - BOOST_VERIFY(0==sched_yield()); + BOOST_VERIFY(!sched_yield()); # elif defined(BOOST_HAS_PTHREAD_YIELD) - BOOST_VERIFY(0==pthread_yield()); + BOOST_VERIFY(!pthread_yield()); # else xtime xt; xtime_get(&xt, TIME_UTC); @@ -338,7 +338,7 @@ namespace boost local_thread_info->interrupt_requested=true; if(local_thread_info->current_cond) { - BOOST_VERIFY(0==pthread_cond_broadcast(local_thread_info->current_cond)); + BOOST_VERIFY(!pthread_cond_broadcast(local_thread_info->current_cond)); } } } diff --git a/src/pthread/tss.cpp b/src/pthread/tss.cpp index 1163054d..2f3a4903 100644 --- a/src/pthread/tss.cpp +++ b/src/pthread/tss.cpp @@ -17,11 +17,6 @@ #include #include -#if defined(BOOST_HAS_WINTHREADS) -# include -# include -#endif - namespace { typedef std::vector tss_slots; From b7edb2873c4ed3cb1f45ae7585228e064600a7de Mon Sep 17 00:00:00 2001 From: Roland Schwarz Date: Mon, 5 Nov 2007 16:12:49 +0000 Subject: [PATCH 042/214] Usage requirements added. [SVN r40790] --- build/Jamfile.v2 | 139 +++++++++++++++++++++++++++++------------------ 1 file changed, 85 insertions(+), 54 deletions(-) diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 4facff76..10bceabe 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -53,9 +53,8 @@ local rule default_threadapi ( ) return $(api) ; } -feature.feature threadapi : pthread win32 : propagated composite ; +feature.feature threadapi : pthread win32 : propagated ; feature.set-default threadapi : [ default_threadapi ] ; -feature.compose pthread : BOOST_THREAD_POSIX ; rule tag ( name : type ? : property-set ) { @@ -76,6 +75,84 @@ rule tag ( name : type ? : property-set ) $(result) : $(type) : $(property-set) ] ; } +rule win32_pthread_paths ( properties * ) +{ + local result ; + local PTW32_INCLUDE ; + local PTW32_LIB ; + PTW32_INCLUDE = [ modules.peek : PTW32_INCLUDE ] ; + PTW32_LIB = [ modules.peek : PTW32_LIB ] ; + PTW32_INCLUDE ?= [ modules.peek user-config : PTW32_INCLUDE ] ; + PTW32_LIB ?= [ modules.peek user-config : PTW32_LIB ] ; + PTW32_INCLUDE ?= [ modules.peek site-config : PTW32_INCLUDE ] ; + PTW32_LIB ?= [ modules.peek site-config : PTW32_LIB ] ; + + if ! ( $(PTW32_INCLUDE) && $(PTW32_LIB) ) + { + if ! $(.notified) + { + echo "************************************************************" ; + echo "Trying to build Boost.Thread with pthread support." ; + echo "If you need pthread you should specify the paths." ; + echo "You can specify them in site-config.jam, user-config.jam" ; + echo "or in the environment." ; + echo "For example:" ; + echo "PTW32_INCLUDE=C:\\Program Files\\ptw32\\Pre-built2\\include" ; + echo "PTW32_LIB=C:\\Program Files\\ptw32\\Pre-built2\\lib" ; + echo "************************************************************" ; + .notified = true ; + } + } + else + { + local include_path = [ path.make $(PTW32_INCLUDE) ] ; + local lib_path = [ path.make $(PTW32_LIB) ] ; + local libname = pthread ; + if msvc in $(properties) + { + libname = $(libname)VC2.lib ; + } + if gcc in $(properties) + { + libname = lib$(libname)GC2.a ; + } + lib_path = [ path.glob $(lib_path) : $(libname) ] ; + if ! $(lib_path) + { + if ! $(.notified) + { + echo "************************************************************" ; + echo "Trying to build Boost.Thread with pthread support." ; + echo "But the library" $(libname) "could not be found in path" ; + echo $(PTW32_LIB) ; + echo "************************************************************" ; + .notified = true ; + } + } + else + { + result += $(include_path) ; + result += $(lib_path) ; + } + } + return $(result) ; +} + +rule usage-requirements ( properties * ) +{ + local result ; + if pthread in $(properties) + { + result += BOOST_THREAD_POSIX ; + if windows in $(properties) + { + result += [ win32_pthread_paths $(properties) ] ; + # TODO: What is for static linking? Is the also needed + # in that case? + } + } + return $(result) ; +} rule requirements ( properties * ) { @@ -85,62 +162,14 @@ rule requirements ( properties * ) result += BOOST_THREAD_POSIX ; if windows in $(properties) { - local PTW32_INCLUDE ; - local PTW32_LIB ; - PTW32_INCLUDE = [ modules.peek : PTW32_INCLUDE ] ; - PTW32_LIB = [ modules.peek : PTW32_LIB ] ; - PTW32_INCLUDE ?= [ modules.peek user-config : PTW32_INCLUDE ] ; - PTW32_LIB ?= [ modules.peek user-config : PTW32_LIB ] ; - PTW32_INCLUDE ?= [ modules.peek site-config : PTW32_INCLUDE ] ; - PTW32_LIB ?= [ modules.peek site-config : PTW32_LIB ] ; - - if ! ( $(PTW32_INCLUDE) && $(PTW32_LIB) ) + local paths = [ win32_pthread_paths $(properties) ] ; + if $(paths) { - if ! $(.notified) - { - echo "************************************************************" ; - echo "Trying to build Boost.Thread with pthread support." ; - echo "If you need pthread you should specify the paths." ; - echo "You can specify them in site-config.jam, user-config.jam" ; - echo "or in the environment." ; - echo "For example:" ; - echo "PTW32_INCLUDE=C:\\Program Files\\ptw32\\Pre-built2\\include" ; - echo "PTW32_LIB=C:\\Program Files\\ptw32\\Pre-built2\\lib" ; - echo "************************************************************" ; - .notified = true ; - } - result = no ; + result += $(paths) ; } else { - local include_path = [ path.make $(PTW32_INCLUDE) ] ; - local lib_path = [ path.make $(PTW32_LIB) ] ; - result += $(include_path) ; - local libname = pthread ; - if msvc in $(properties) - { - libname = $(libname)VC2.lib ; - } - if gcc in $(properties) - { - libname = lib$(libname)GC2.a ; - } - lib_path = [ path.glob $(lib_path) : $(libname) ] ; - if ! $(lib_path) - { - if ! $(.notified) - { - echo "************************************************************" ; - echo "Trying to build Boost.Thread with pthread support." ; - echo "But the library" $(libname) "could not be found in path" ; - echo $(PTW32_LIB) ; - echo "************************************************************" ; - .notified = true ; - } - result = no ; - } - result += $(lib_path) ; - + result = no ; } } } @@ -173,4 +202,6 @@ explicit thread_sources ; lib boost_thread : thread_sources : @requirements + : + : @usage-requirements ; From c246222ded857c144939791653f1754833d07f5a Mon Sep 17 00:00:00 2001 From: Roland Schwarz Date: Mon, 5 Nov 2007 16:22:17 +0000 Subject: [PATCH 043/214] Cosmetic change to please gcc. [SVN r40791] --- src/win32/tss_hooks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/win32/tss_hooks.cpp b/src/win32/tss_hooks.cpp index a4720506..8e9f7df8 100644 --- a/src/win32/tss_hooks.cpp +++ b/src/win32/tss_hooks.cpp @@ -30,7 +30,7 @@ class CScopedCSLock { public: - CScopedCSLock(LPCRITICAL_SECTION cs) : cs(cs), lk(true) { + CScopedCSLock(LPCRITICAL_SECTION cs) : lk(true), cs(cs) { ::EnterCriticalSection(cs); } ~CScopedCSLock() { From 320cb63df4e9272222a086ff50467f54b73eb367 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 5 Nov 2007 16:47:25 +0000 Subject: [PATCH 044/214] Use BOOST_VERIFY instead of BOOST_ASSERT in many places in order to avoid unused variable warnings [SVN r40792] --- src/win32/thread.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index 8e62b9ad..54636dcc 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -58,8 +58,7 @@ namespace boost void set_current_thread_data(detail::thread_data_base* new_data) { boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); - BOOL const res=TlsSetValue(current_thread_tls_key,new_data); - BOOST_ASSERT(res); + BOOST_VERIFY(TlsSetValue(current_thread_tls_key,new_data)); } #endif From 39c864e31f8ab41f6f64079eeda32c5ad4adccb6 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 6 Nov 2007 17:15:50 +0000 Subject: [PATCH 045/214] use condition so we know when threads have unblocked, to avoid hard-coding a delay [SVN r40846] --- test/test_shared_mutex.cpp | 94 +++++++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 16 deletions(-) diff --git a/test/test_shared_mutex.cpp b/test/test_shared_mutex.cpp index c0d863c8..62c1de09 100644 --- a/test/test_shared_mutex.cpp +++ b/test/test_shared_mutex.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include "util.inl" @@ -29,16 +30,19 @@ namespace unsigned& simultaneous_running_count; unsigned& max_simultaneous_running; boost::mutex& unblocked_count_mutex; + boost::condition_variable& unblocked_condition; boost::mutex& finish_mutex; public: locking_thread(boost::shared_mutex& rw_mutex_, unsigned& unblocked_count_, boost::mutex& unblocked_count_mutex_, + boost::condition_variable& unblocked_condition_, boost::mutex& finish_mutex_, unsigned& simultaneous_running_count_, unsigned& max_simultaneous_running_): rw_mutex(rw_mutex_), unblocked_count(unblocked_count_), + unblocked_condition(unblocked_condition_), simultaneous_running_count(simultaneous_running_count_), max_simultaneous_running(max_simultaneous_running_), unblocked_count_mutex(unblocked_count_mutex_), @@ -54,6 +58,7 @@ namespace { boost::mutex::scoped_lock ublock(unblocked_count_mutex); ++unblocked_count; + unblocked_condition.notify_one(); ++simultaneous_running_count; if(simultaneous_running_count>max_simultaneous_running) { @@ -84,15 +89,23 @@ void test_multiple_readers() unsigned simultaneous_running_count=0; unsigned max_simultaneous_running=0; boost::mutex unblocked_count_mutex; + boost::condition_variable unblocked_condition; boost::mutex finish_mutex; boost::mutex::scoped_lock finish_lock(finish_mutex); for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); } - boost::thread::sleep(delay(1)); + { + boost::mutex::scoped_lock lk(unblocked_count_mutex); + while(unblocked_count >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); } - boost::thread::sleep(delay(1)); + boost::thread::sleep(delay(2)); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); @@ -143,13 +158,22 @@ void test_reader_blocks_writer() unsigned simultaneous_running_count=0; unsigned max_simultaneous_running=0; boost::mutex unblocked_count_mutex; + boost::condition_variable unblocked_condition; boost::mutex finish_mutex; boost::mutex::scoped_lock finish_lock(finish_mutex); - pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); - boost::thread::sleep(delay(1)); + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); + { + boost::mutex::scoped_lock lk(unblocked_count_mutex); + while(unblocked_count<1) + { + unblocked_condition.wait(lk); + } + } CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); - pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); boost::thread::sleep(delay(1)); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); @@ -171,6 +195,7 @@ void test_unlocking_writer_unblocks_all_readers() unsigned simultaneous_running_count=0; unsigned max_simultaneous_running=0; boost::mutex unblocked_count_mutex; + boost::condition_variable unblocked_condition; boost::mutex finish_mutex; boost::mutex::scoped_lock finish_lock(finish_mutex); @@ -178,14 +203,22 @@ void test_unlocking_writer_unblocks_all_readers() for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); } boost::thread::sleep(delay(1)); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,0U); write_lock.unlock(); - boost::thread::sleep(delay(1)); + { + boost::mutex::scoped_lock lk(unblocked_count_mutex); + while(unblocked_count >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_reading_mutex,simultaneous_running_readers,max_simultaneous_readers)); + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_reading_mutex,simultaneous_running_readers,max_simultaneous_readers)); } + boost::thread::sleep(delay(1)); for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_writing_mutex,simultaneous_running_writers,max_simultaneous_writers)); + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_writing_mutex,simultaneous_running_writers,max_simultaneous_writers)); } - boost::thread::sleep(delay(2)); + { + boost::mutex::scoped_lock lk(unblocked_count_mutex); + while(unblocked_count >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); } boost::thread::sleep(delay(1)); @@ -275,6 +327,7 @@ void test_can_lock_upgrade_if_currently_locked_shared() unsigned simultaneous_running_count=0; unsigned max_simultaneous_running=0; boost::mutex unblocked_count_mutex; + boost::condition_variable unblocked_condition; boost::mutex finish_mutex; boost::mutex::scoped_lock finish_lock(finish_mutex); @@ -282,10 +335,19 @@ void test_can_lock_upgrade_if_currently_locked_shared() for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); } - pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,finish_mutex,simultaneous_running_count,max_simultaneous_running)); boost::thread::sleep(delay(1)); + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); + { + boost::mutex::scoped_lock lk(unblocked_count_mutex); + while(unblocked_count<(reader_count+1)) + { + unblocked_condition.wait(lk); + } + } CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); finish_lock.unlock(); From 5de1582a0a2163b6a8fd4ad432628082758bf21c Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 7 Nov 2007 12:10:17 +0000 Subject: [PATCH 046/214] Added missing licence and copyright [SVN r40884] --- include/boost/thread/condition.hpp | 6 ++++++ .../pthread/pthread_mutex_scoped_lock.hpp | 6 ++++++ include/boost/thread/pthread/thread.hpp | 18 +++++++++--------- include/boost/thread/pthread/timespec.hpp | 5 +++++ include/boost/thread/thread_time.hpp | 6 ++++++ 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/include/boost/thread/condition.hpp b/include/boost/thread/condition.hpp index 3f2be26d..35b879fe 100644 --- a/include/boost/thread/condition.hpp +++ b/include/boost/thread/condition.hpp @@ -1,5 +1,11 @@ #ifndef BOOST_THREAD_CONDITION_HPP #define BOOST_THREAD_CONDITION_HPP +// (C) Copyright 2007 Anthony Williams +// +// 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) + #include namespace boost diff --git a/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp b/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp index e00e9477..304718bc 100644 --- a/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp +++ b/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp @@ -1,5 +1,11 @@ #ifndef BOOST_PTHREAD_MUTEX_SCOPED_LOCK_HPP #define BOOST_PTHREAD_MUTEX_SCOPED_LOCK_HPP +// (C) Copyright 2007 Anthony Williams +// +// 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) + #include #include diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index b3e786e1..db4eb974 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -33,7 +33,7 @@ namespace boost namespace this_thread { - detail::thread_id get_id(); + BOOST_THREAD_DECL detail::thread_id get_id(); } namespace detail @@ -208,22 +208,22 @@ namespace boost ~restore_interruption(); }; - inline thread::id get_id() + BOOST_THREAD_DECL inline thread::id get_id() { return thread::id(pthread_self()); } - void BOOST_THREAD_DECL interruption_point(); - bool BOOST_THREAD_DECL interruption_enabled(); - bool BOOST_THREAD_DECL interruption_requested(); + BOOST_THREAD_DECL void interruption_point(); + BOOST_THREAD_DECL bool interruption_enabled(); + BOOST_THREAD_DECL bool interruption_requested(); - inline void yield() + BOOST_THREAD_DECL inline void yield() { thread::yield(); } template - inline void sleep(TimeDuration const& rel_time) + BOOST_THREAD_DECL inline void sleep(TimeDuration const& rel_time) { thread::sleep(get_system_time()+rel_time); } @@ -254,13 +254,13 @@ namespace boost } }; - void add_thread_exit_function(thread_exit_function_base*); + BOOST_THREAD_DECL void add_thread_exit_function(thread_exit_function_base*); } namespace this_thread { template - void at_thread_exit(F f) + inline void at_thread_exit(F f) { detail::thread_exit_function_base* const thread_exit_func=new detail::thread_exit_function(f); detail::add_thread_exit_function(thread_exit_func); diff --git a/include/boost/thread/pthread/timespec.hpp b/include/boost/thread/pthread/timespec.hpp index a7039f7a..f77bd3b7 100644 --- a/include/boost/thread/pthread/timespec.hpp +++ b/include/boost/thread/pthread/timespec.hpp @@ -1,5 +1,10 @@ #ifndef BOOST_THREAD_PTHREAD_TIMESPEC_HPP #define BOOST_THREAD_PTHREAD_TIMESPEC_HPP +// (C) Copyright 2007 Anthony Williams +// +// 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) #include #include diff --git a/include/boost/thread/thread_time.hpp b/include/boost/thread/thread_time.hpp index dd0c3a86..5468961f 100644 --- a/include/boost/thread/thread_time.hpp +++ b/include/boost/thread/thread_time.hpp @@ -1,5 +1,11 @@ #ifndef BOOST_THREAD_TIME_HPP #define BOOST_THREAD_TIME_HPP +// (C) Copyright 2007 Anthony Williams +// +// 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) + #include #include From ef9083089eac22dc81757fc47d075b4abdfc2401 Mon Sep 17 00:00:00 2001 From: Roland Schwarz Date: Fri, 9 Nov 2007 08:31:45 +0000 Subject: [PATCH 047/214] Force static linking for toolsets mingw and boorland. [SVN r40958] --- build/Jamfile.v2 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 10bceabe..364fd355 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -39,6 +39,8 @@ import path ; project boost/thread : source-location ../src : requirements multi + mingw:shared + borland:shared static:BOOST_THREAD_BUILD_LIB=1 shared:BOOST_THREAD_BUILD_DLL=1 -@$(BOOST_JAMROOT_MODULE)%$(BOOST_JAMROOT_MODULE).tag From 58c741e9ca0970c8fbaabec7dd3fb7fef29e8ea4 Mon Sep 17 00:00:00 2001 From: Roland Schwarz Date: Fri, 9 Nov 2007 09:28:29 +0000 Subject: [PATCH 048/214] Reverted the previous commit, until I find a better solution... [SVN r40959] --- build/Jamfile.v2 | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 364fd355..10bceabe 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -39,8 +39,6 @@ import path ; project boost/thread : source-location ../src : requirements multi - mingw:shared - borland:shared static:BOOST_THREAD_BUILD_LIB=1 shared:BOOST_THREAD_BUILD_DLL=1 -@$(BOOST_JAMROOT_MODULE)%$(BOOST_JAMROOT_MODULE).tag From 22647135fa21020bcf7b6beb74d3f9e1129c4bcc Mon Sep 17 00:00:00 2001 From: Roland Schwarz Date: Sat, 10 Nov 2007 19:25:45 +0000 Subject: [PATCH 049/214] Added static linking support for mingw compiler on windows. [SVN r40999] --- src/win32/tss_pe.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/src/win32/tss_pe.cpp b/src/win32/tss_pe.cpp index 07fa9e12..010920f6 100644 --- a/src/win32/tss_pe.cpp +++ b/src/win32/tss_pe.cpp @@ -1,4 +1,6 @@ +// $Id$ // (C) Copyright Aaron W. LaFramboise, Roland Schwarz, Michael Glassford 2004. +// (C) Copyright 2007 Roland Schwarz // (C) Copyright 2007 Anthony Williams // (C) Copyright 2007 David Deakins // Use, modification and distribution are subject to the @@ -7,7 +9,75 @@ #include -#if defined(BOOST_THREAD_BUILD_LIB) && defined(_MSC_VER) && !defined(UNDER_CE) +#if defined(BOOST_HAS_WINTHREADS) && defined(BOOST_THREAD_BUILD_LIB) + +#if defined(__MINGW32__) && !defined(_WIN64) + +#include + +#include + +#include + +extern "C" void tss_cleanup_implemented(void) {} + +namespace { + void NTAPI on_tls_callback(void* h, DWORD dwReason, PVOID pv) + { + switch (dwReason) + { + case DLL_THREAD_DETACH: + { + on_thread_exit(); + break; + } + } + } + + void on_after_ctors(void) + { + on_process_enter(); + } + + void on_before_dtors(void) + { + on_thread_exit(); + } + + void on_after_dtors(void) + { + on_process_exit(); + } +} + +extern "C" { + + void (* after_ctors )(void) __attribute__((section(".ctors"))) = on_after_ctors; + void (* before_dtors)(void) __attribute__((section(".dtors"))) = on_before_dtors; + void (* after_dtors )(void) __attribute__((section(".dtors.zzz"))) = on_after_dtors; + + ULONG __tls_index__ = 0; + char __tls_end__ __attribute__((section(".tls$zzz"))) = 0; + char __tls_start__ __attribute__((section(".tls"))) = 0; + + + PIMAGE_TLS_CALLBACK __crt_xl_start__ __attribute__ ((section(".CRT$XLA"))) = 0; + PIMAGE_TLS_CALLBACK __crt_xl_tls_callback__ __attribute__ ((section(".CRT$XLB"))) = on_tls_callback; + PIMAGE_TLS_CALLBACK __crt_xl_end__ __attribute__ ((section(".CRT$XLZ"))) = 0; +} + +extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata$T"))) = +{ + (DWORD) &__tls_start__, + (DWORD) &__tls_end__, + (DWORD) &__tls_index__, + (DWORD) (&__crt_xl_start__+1), + (DWORD) 0, + (DWORD) 0 +}; + + +#elif defined(_MSC_VER) && !defined(UNDER_CE) #include @@ -177,5 +247,6 @@ longer needed and can be removed. */ } +#endif //defined(_MSC_VER) && !defined(UNDER_CE) #endif //defined(BOOST_HAS_WINTHREADS) && defined(BOOST_THREAD_BUILD_LIB) From 519ed3834e9c43ea2529da65612a47760a10c974 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 13 Nov 2007 09:27:11 +0000 Subject: [PATCH 050/214] Integrated TSS with storage of thread data; cleaned up the heap allocation functions to throw bad_alloc if they run out of memory [SVN r41056] --- build/Jamfile.v2 | 2 - include/boost/thread/detail/tss_hooks.hpp | 2 +- include/boost/thread/tss.hpp | 14 +- include/boost/thread/win32/thread.hpp | 4 +- .../boost/thread/win32/thread_heap_alloc.hpp | 93 +++++++- include/boost/thread/win32/tss.hpp | 82 +++++++ src/win32/thread.cpp | 159 +++++++++---- src/win32/tss.cpp | 211 ----------------- src/win32/tss_hooks.cpp | 218 ------------------ src/win32/tss_pe.cpp | 3 + 10 files changed, 294 insertions(+), 494 deletions(-) create mode 100644 include/boost/thread/win32/tss.hpp delete mode 100644 src/win32/tss.cpp delete mode 100644 src/win32/tss_hooks.cpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 10bceabe..860542be 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -180,8 +180,6 @@ alias thread_sources : ## win32 sources ## win32/thread.cpp win32/exceptions.cpp - win32/tss.cpp - win32/tss_hooks.cpp win32/tss_dll.cpp win32/tss_pe.cpp : ## requirements ## diff --git a/include/boost/thread/detail/tss_hooks.hpp b/include/boost/thread/detail/tss_hooks.hpp index a1c1aa3b..4b18fe97 100644 --- a/include/boost/thread/detail/tss_hooks.hpp +++ b/include/boost/thread/detail/tss_hooks.hpp @@ -67,7 +67,7 @@ //Called automatically by Boost.Threads when //a method for doing so has been discovered. //Must not be omitted; may be called multiple times. - + extern "C" void tss_cleanup_implemented(void); //Dummy function used both to detect whether tss cleanup //cleanup has been implemented and to force diff --git a/include/boost/thread/tss.hpp b/include/boost/thread/tss.hpp index 0b33bda4..1870324c 100644 --- a/include/boost/thread/tss.hpp +++ b/include/boost/thread/tss.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2001-2003 William E. Kempf // Copyright (C) 2006 Roland Schwarz +// Copyright (C) 2007 Anthony Williams // // 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) @@ -9,14 +10,17 @@ #include +#ifdef BOOST_HAS_WINTHREADS +#include +#include BOOST_THREAD_PLATFORM(tss.hpp) +#else + #include #include #include #if defined(BOOST_HAS_PTHREADS) # include -#elif defined(BOOST_HAS_MPTASKS) -# include #endif namespace boost { @@ -57,10 +61,6 @@ private: void init(boost::function1* pcleanup); }; -#if defined(BOOST_HAS_MPTASKS) -void thread_cleanup(); -#endif - template struct tss_adapter { @@ -112,6 +112,8 @@ private: } // namespace boost +#endif + #endif //BOOST_TSS_WEK070601_HPP // Change Log: diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index a81c6ee6..1e237791 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -27,6 +27,7 @@ namespace boost namespace detail { struct thread_exit_callback_node; + struct tss_data_node; struct thread_data_base { @@ -34,13 +35,14 @@ namespace boost detail::win32::handle_manager thread_handle; detail::win32::handle_manager interruption_handle; boost::detail::thread_exit_callback_node* thread_exit_callbacks; + boost::detail::tss_data_node* tss_data; bool interruption_enabled; unsigned id; thread_data_base(): count(0),thread_handle(detail::win32::invalid_handle_value), interruption_handle(create_anonymous_event(detail::win32::manual_reset_event,detail::win32::event_initially_reset)), - thread_exit_callbacks(0), + thread_exit_callbacks(0),tss_data(0), interruption_enabled(true), id(0) {} diff --git a/include/boost/thread/win32/thread_heap_alloc.hpp b/include/boost/thread/win32/thread_heap_alloc.hpp index eed7d4b3..1835e35e 100644 --- a/include/boost/thread/win32/thread_heap_alloc.hpp +++ b/include/boost/thread/win32/thread_heap_alloc.hpp @@ -6,6 +6,8 @@ #define THREAD_HEAP_ALLOC_HPP #include #include "thread_primitives.hpp" +#include +#include #if defined( BOOST_USE_WINDOWS_H ) # include @@ -51,35 +53,106 @@ namespace boost { namespace detail { + inline BOOST_THREAD_DECL void* allocate_raw_heap_memory(unsigned size) + { + void* const heap_memory=detail::win32::HeapAlloc(detail::win32::GetProcessHeap(),0,size); + if(!heap_memory) + { + throw std::bad_alloc(); + } + return heap_memory; + } + + inline BOOST_THREAD_DECL void free_raw_heap_memory(void* heap_memory) + { + BOOST_VERIFY(detail::win32::HeapFree(detail::win32::GetProcessHeap(),0,heap_memory)!=0); + } + template T* heap_new() { - void* const heap_memory=detail::win32::HeapAlloc(detail::win32::GetProcessHeap(),0,sizeof(T)); - T* const data=new (heap_memory) T(); - return data; + void* const heap_memory=allocate_raw_heap_memory(sizeof(T)); + try + { + T* const data=new (heap_memory) T(); + return data; + } + catch(...) + { + free_raw_heap_memory(heap_memory); + throw; + } } template T* heap_new(A1 a1) { - void* const heap_memory=detail::win32::HeapAlloc(detail::win32::GetProcessHeap(),0,sizeof(T)); - T* const data=new (heap_memory) T(a1); - return data; + void* const heap_memory=allocate_raw_heap_memory(sizeof(T)); + try + { + T* const data=new (heap_memory) T(a1); + return data; + } + catch(...) + { + free_raw_heap_memory(heap_memory); + throw; + } } template T* heap_new(A1 a1,A2 a2) { - void* const heap_memory=detail::win32::HeapAlloc(detail::win32::GetProcessHeap(),0,sizeof(T)); - T* const data=new (heap_memory) T(a1,a2); - return data; + void* const heap_memory=allocate_raw_heap_memory(sizeof(T)); + try + { + T* const data=new (heap_memory) T(a1,a2); + return data; + } + catch(...) + { + free_raw_heap_memory(heap_memory); + throw; + } + } + + template + T* heap_new(A1 a1,A2 a2,A3 a3) + { + void* const heap_memory=allocate_raw_heap_memory(sizeof(T)); + try + { + T* const data=new (heap_memory) T(a1,a2,a3); + return data; + } + catch(...) + { + free_raw_heap_memory(heap_memory); + throw; + } + } + + template + T* heap_new(A1 a1,A2 a2,A3 a3,A4 a4) + { + void* const heap_memory=allocate_raw_heap_memory(sizeof(T)); + try + { + T* const data=new (heap_memory) T(a1,a2,a3,a4); + return data; + } + catch(...) + { + free_raw_heap_memory(heap_memory); + throw; + } } template void heap_delete(T* data) { data->~T(); - detail::win32::HeapFree(detail::win32::GetProcessHeap(),0,data); + free_raw_heap_memory(data); } template diff --git a/include/boost/thread/win32/tss.hpp b/include/boost/thread/win32/tss.hpp new file mode 100644 index 00000000..a99dfac4 --- /dev/null +++ b/include/boost/thread/win32/tss.hpp @@ -0,0 +1,82 @@ +#ifndef BOOST_THREAD_WIN32_TSS_HPP +#define BOOST_THREAD_WIN32_TSS_HPP + +namespace boost +{ + namespace detail + { + typedef void(*tss_cleanup_function)(void const* key,void* value); + + BOOST_THREAD_DECL void set_tss_data(void const* key,tss_cleanup_function func,void* tss_data,bool cleanup_existing); + BOOST_THREAD_DECL void* get_tss_data(void const* key); + } + + template + class thread_specific_ptr + { + private: + thread_specific_ptr(thread_specific_ptr&); + thread_specific_ptr& operator=(thread_specific_ptr&); + + static void delete_data(void const* self,void* value) + { + static_cast(self)->cleanup((T*)value); + } + + void cleanup(T* data) const + { + if(func) + { + func(data); + } + else + { + delete data; + } + } + + void (*func)(T*); + + public: + thread_specific_ptr(): + func(0) + {} + explicit thread_specific_ptr(void (*func_)(T*)): + func(func_) + {} + ~thread_specific_ptr() + { + reset(); + } + + T* get() const + { + return static_cast(detail::get_tss_data(this)); + } + T* operator->() const + { + return get(); + } + T& operator*() const + { + return *get(); + } + T* release() + { + T* const temp=get(); + detail::set_tss_data(this,0,0,false); + return temp; + } + void reset(T* new_value=0) + { + T* const current_value=get(); + if(current_value!=new_value) + { + detail::set_tss_data(this,delete_data,new_value,true); + } + } + }; +} + + +#endif diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index 54636dcc..fee2b4fd 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -12,7 +12,9 @@ #endif #include #include +#include #include +#include namespace boost { @@ -130,6 +132,19 @@ namespace boost {} }; + struct tss_data_node + { + void const* key; + boost::detail::tss_cleanup_function func; + void* value; + tss_data_node* next; + + tss_data_node(void const* key_,boost::detail::tss_cleanup_function func_,void* value_, + tss_data_node* next_): + key(key_),func(func_),value(value_),next(next_) + {} + }; + } namespace @@ -139,17 +154,31 @@ namespace boost boost::intrusive_ptr current_thread_data(get_current_thread_data(),false); if(current_thread_data) { - while(current_thread_data->thread_exit_callbacks) + while(current_thread_data->tss_data || current_thread_data->thread_exit_callbacks) { - detail::thread_exit_callback_node* const current_node=current_thread_data->thread_exit_callbacks; - current_thread_data->thread_exit_callbacks=current_node->next; - if(current_node->func) + while(current_thread_data->thread_exit_callbacks) { - (*current_node->func)(); - boost::detail::heap_delete(current_node->func); + detail::thread_exit_callback_node* const current_node=current_thread_data->thread_exit_callbacks; + current_thread_data->thread_exit_callbacks=current_node->next; + if(current_node->func) + { + (*current_node->func)(); + boost::detail::heap_delete(current_node->func); + } + boost::detail::heap_delete(current_node); + } + while(current_thread_data->tss_data) + { + detail::tss_data_node* const current_node=current_thread_data->tss_data; + current_thread_data->tss_data=current_node->next; + if(current_node->func) + { + (*current_node->func)(current_node->key,current_node->value); + } + boost::detail::heap_delete(current_node); } - boost::detail::heap_delete(current_node); } + } set_current_thread_data(0); } @@ -210,14 +239,20 @@ namespace boost void run() {} }; + + void make_external_thread_data() + { + externally_launched_thread* me=detail::heap_new(); + set_current_thread_data(me); + } + } thread thread::self() { if(!get_current_thread_data()) { - externally_launched_thread* me=detail::heap_new(); - set_current_thread_data(me); + make_external_thread_data(); } return thread(boost::intrusive_ptr(get_current_thread_data())); } @@ -450,49 +485,83 @@ namespace boost } } - namespace - { - void NTAPI thread_exit_func_callback(HINSTANCE, DWORD, PVOID); - typedef void (NTAPI* tls_callback)(HINSTANCE, DWORD, PVOID); - -#ifdef _MSC_VER - extern "C" - { - extern DWORD _tls_used; //the tls directory (located in .rdata segment) - extern tls_callback __xl_a[], __xl_z[]; //tls initializers */ - } - -#if (_MSC_VER >= 1300) // 1300 == VC++ 7.0 -# pragma data_seg(push, old_seg) -#endif - -#pragma data_seg(".CRT$XLB") - tls_callback p_thread_callback = thread_exit_func_callback; -#pragma data_seg() - -#if (_MSC_VER >= 1300) // 1300 == VC++ 7.0 -# pragma data_seg(pop, old_seg) -#endif -#endif - - void NTAPI thread_exit_func_callback(HINSTANCE h, DWORD dwReason, PVOID pv) - { - if((dwReason==DLL_THREAD_DETACH) || (dwReason==DLL_PROCESS_DETACH)) - { - run_thread_exit_callbacks(); - } - } - } - namespace detail { void add_thread_exit_function(thread_exit_function_base* func) { + detail::thread_data_base* const current_thread_data(get_current_thread_data()); thread_exit_callback_node* const new_node= heap_new(func, - get_current_thread_data()->thread_exit_callbacks); - get_current_thread_data()->thread_exit_callbacks=new_node; + current_thread_data->thread_exit_callbacks); + current_thread_data->thread_exit_callbacks=new_node; + } + + tss_data_node* find_tss_data(void const* key) + { + detail::thread_data_base* const current_thread_data(get_current_thread_data()); + if(current_thread_data) + { + detail::tss_data_node* current_node=current_thread_data->tss_data; + while(current_node) + { + if(current_node->key==key) + { + return current_node; + } + current_node=current_node->next; + } + } + return NULL; + } + + void* get_tss_data(void const* key) + { + if(tss_data_node* const current_node=find_tss_data(key)) + { + return current_node->value; + } + return NULL; + } + + void set_tss_data(void const* key,tss_cleanup_function func,void* tss_data,bool cleanup_existing) + { + tss_cleanup_implemented(); // if anyone uses TSS, we need the cleanup linked in + if(tss_data_node* const current_node=find_tss_data(key)) + { + if(cleanup_existing && current_node->func) + { + (current_node->func)(current_node->key,current_node->value); + } + current_node->func=func; + current_node->value=tss_data; + } + else + { + detail::thread_data_base* current_thread_data(get_current_thread_data()); + if(!current_thread_data) + { + make_external_thread_data(); + current_thread_data=get_current_thread_data(); + } + tss_data_node* const new_node=heap_new(key,func,tss_data,current_thread_data->tss_data); + current_thread_data->tss_data=new_node; + } } } } + +extern "C" BOOST_THREAD_DECL void on_process_enter() +{} + +extern "C" BOOST_THREAD_DECL void on_thread_enter() +{} + +extern "C" BOOST_THREAD_DECL void on_process_exit() +{} + +extern "C" BOOST_THREAD_DECL void on_thread_exit() +{ + boost::run_thread_exit_callbacks(); +} + diff --git a/src/win32/tss.cpp b/src/win32/tss.cpp deleted file mode 100644 index eb5a4d81..00000000 --- a/src/win32/tss.cpp +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright (C) 2001-2003 William E. Kempf -// Copyright (C) 2006 Roland Schwarz -// Copyright (C) 2007 Anthony Williams -// Copyright (C) 2007 David Deakins -// -// 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) - -#include - -#include -#ifndef BOOST_THREAD_NO_TSS_CLEANUP - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#if defined(UNDER_CE) && !defined(TLS_OUT_OF_INDEXES) -# define TLS_OUT_OF_INDEXES 0xFFFFFFFF -#endif - -namespace { - -typedef std::vector tss_slots; -typedef std::vector*> tss_data_cleanup_handlers_type; - -boost::once_flag tss_data_once = BOOST_ONCE_INIT; -boost::mutex* tss_data_mutex = 0; -tss_data_cleanup_handlers_type* tss_data_cleanup_handlers = 0; - DWORD tss_data_native_key=TLS_OUT_OF_INDEXES; -int tss_data_use = 0; - -void tss_data_inc_use(boost::mutex::scoped_lock& lk) -{ - ++tss_data_use; -} - -void tss_data_dec_use(boost::mutex::scoped_lock& lk) -{ - if (0 == --tss_data_use) - { - tss_data_cleanup_handlers_type::size_type i; - for (i = 0; i < tss_data_cleanup_handlers->size(); ++i) - { - delete (*tss_data_cleanup_handlers)[i]; - } - delete tss_data_cleanup_handlers; - tss_data_cleanup_handlers = 0; - lk.unlock(); - delete tss_data_mutex; - tss_data_mutex = 0; - TlsFree(tss_data_native_key); - tss_data_native_key=TLS_OUT_OF_INDEXES; - } -} - -extern "C" void cleanup_slots(void* p) -{ - tss_slots* slots = static_cast(p); - boost::mutex::scoped_lock lock(*tss_data_mutex); - for (tss_slots::size_type i = 0; i < slots->size(); ++i) - { - (*(*tss_data_cleanup_handlers)[i])((*slots)[i]); - (*slots)[i] = 0; - } - TlsSetValue(tss_data_native_key,0); - tss_data_dec_use(lock); - delete slots; -} - -void init_tss_data() -{ - std::auto_ptr - temp(new tss_data_cleanup_handlers_type); - - std::auto_ptr temp_mutex(new boost::mutex); - if (temp_mutex.get() == 0) - throw boost::thread_resource_error(); - - //Force the cleanup implementation library to be linked in - tss_cleanup_implemented(); - - //Allocate tls slot - tss_data_native_key = TlsAlloc(); - if (tss_data_native_key == TLS_OUT_OF_INDEXES) - return; - - // The life time of cleanup handlers and mutex is beeing - // managed by a reference counting technique. - // This avoids a memory leak by releasing the global data - // after last use only, since the execution order of cleanup - // handlers is unspecified on any platform with regards to - // C++ destructor ordering rules. - tss_data_cleanup_handlers = temp.release(); - tss_data_mutex = temp_mutex.release(); -} - -tss_slots* get_slots(bool alloc); - -void __cdecl tss_thread_exit() -{ - tss_slots* slots = get_slots(false); - if (slots) - cleanup_slots(slots); -} - -tss_slots* get_slots(bool alloc) -{ - tss_slots* slots = 0; - - slots = static_cast( - TlsGetValue(tss_data_native_key)); - - if (slots == 0 && alloc) - { - std::auto_ptr temp(new tss_slots); - - if (at_thread_exit(&tss_thread_exit) == -1) - return 0; - if (!TlsSetValue(tss_data_native_key, temp.get())) - return 0; - { - boost::mutex::scoped_lock lock(*tss_data_mutex); - tss_data_inc_use(lock); - } - slots = temp.release(); - } - - return slots; -} - -} // namespace - -namespace boost { - -namespace detail { -void tss::init(boost::function1* pcleanup) -{ - boost::call_once(tss_data_once, &init_tss_data); - if (tss_data_cleanup_handlers == 0) - throw thread_resource_error(); - boost::mutex::scoped_lock lock(*tss_data_mutex); - try - { - tss_data_cleanup_handlers->push_back(pcleanup); - m_slot = tss_data_cleanup_handlers->size() - 1; - tss_data_inc_use(lock); - } - catch (...) - { - throw thread_resource_error(); - } -} - -tss::~tss() -{ - boost::mutex::scoped_lock lock(*tss_data_mutex); - tss_data_dec_use(lock); -} - -void* tss::get() const -{ - tss_slots* slots = get_slots(false); - - if (!slots) - return 0; - - if (m_slot >= slots->size()) - return 0; - - return (*slots)[m_slot]; -} - -void tss::set(void* value) -{ - tss_slots* slots = get_slots(true); - - if (!slots) - throw boost::thread_resource_error(); - - if (m_slot >= slots->size()) - { - try - { - slots->resize(m_slot + 1); - } - catch (...) - { - throw boost::thread_resource_error(); - } - } - - (*slots)[m_slot] = value; -} - -void tss::cleanup(void* value) -{ - boost::mutex::scoped_lock lock(*tss_data_mutex); - (*(*tss_data_cleanup_handlers)[m_slot])(value); -} - -} // namespace detail -} // namespace boost - -#endif //BOOST_THREAD_NO_TSS_CLEANUP diff --git a/src/win32/tss_hooks.cpp b/src/win32/tss_hooks.cpp deleted file mode 100644 index 8e9f7df8..00000000 --- a/src/win32/tss_hooks.cpp +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright (C) 2004 Michael Glassford -// Copyright (C) 2006 Roland Schwarz -// Copyright (C) 2007 Anthony Williams -// Copyright (C) 2007 David Deakins -// Use, modification and distribution are subject to 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) - -#include - -#if defined(BOOST_HAS_WINTHREADS) - - #include - - #include -// #include - #include - - #include - - #define WIN32_LEAN_AND_MEAN - #include - -# if defined(UNDER_CE) && !defined(TLS_OUT_OF_INDEXES) -# define TLS_OUT_OF_INDEXES 0xFFFFFFFF -# endif - - namespace - { - class CScopedCSLock - { - public: - CScopedCSLock(LPCRITICAL_SECTION cs) : lk(true), cs(cs) { - ::EnterCriticalSection(cs); - } - ~CScopedCSLock() { - if (lk) ::LeaveCriticalSection(cs); - } - void Unlock() { - lk = false; - ::LeaveCriticalSection(cs); - } - private: - bool lk; - LPCRITICAL_SECTION cs; - }; - - typedef std::list thread_exit_handlers; - - boost::once_flag once_init_threadmon_mutex = BOOST_ONCE_INIT; - //boost::mutex* threadmon_mutex; - // We don't use boost::mutex here, to avoid a memory leak report, - // because we cannot delete it again easily. - CRITICAL_SECTION threadmon_mutex; - void init_threadmon_mutex(void) - { - //threadmon_mutex = new boost::mutex; - //if (!threadmon_mutex) - // throw boost::thread_resource_error(); - ::InitializeCriticalSection(&threadmon_mutex); - } - - const DWORD invalid_tls_key = TLS_OUT_OF_INDEXES; - DWORD tls_key = invalid_tls_key; - - unsigned long attached_thread_count = 0; - } - - /* - Calls to DllMain() and tls_callback() are serialized by the OS; - however, calls to at_thread_exit are not, so it must be protected - by a mutex. Since we already need a mutex for at_thread_exit(), - and since there is no guarantee that on_process_enter(), - on_process_exit(), on_thread_enter(), and on_thread_exit() - will be called only from DllMain() or tls_callback(), it makes - sense to protect those, too. - */ - - extern "C" BOOST_THREAD_DECL int at_thread_exit( - thread_exit_handler exit_handler - ) - { - boost::call_once(once_init_threadmon_mutex, init_threadmon_mutex); - //boost::mutex::scoped_lock lock(*threadmon_mutex); - CScopedCSLock lock(&threadmon_mutex); - - //Allocate a tls slot if necessary. - - if (tls_key == invalid_tls_key) - tls_key = TlsAlloc(); - - if (tls_key == invalid_tls_key) - return -1; - - //Get the exit handlers list for the current thread from tls. - - thread_exit_handlers* exit_handlers = - static_cast(TlsGetValue(tls_key)); - - if (!exit_handlers) - { - //No exit handlers list was created yet. - - try - { - //Attempt to create a new exit handlers list. - - exit_handlers = new thread_exit_handlers; - if (!exit_handlers) - return -1; - - //Attempt to store the list pointer in tls. - - if (TlsSetValue(tls_key, exit_handlers)) - ++attached_thread_count; - else - { - delete exit_handlers; - return -1; - } - } - catch (...) - { - return -1; - } - } - - //Like the C runtime library atexit() function, - //functions should be called in the reverse of - //the order they are added, so push them on the - //front of the list. - - try - { - exit_handlers->push_front(exit_handler); - } - catch (...) - { - return -1; - } - - //Like the atexit() function, a result of zero - //indicates success. - - return 0; - } - - extern "C" BOOST_THREAD_DECL void on_process_enter(void) - { - boost::call_once(once_init_threadmon_mutex, init_threadmon_mutex); -// boost::mutex::scoped_lock lock(*threadmon_mutex); - CScopedCSLock lock(&threadmon_mutex); - - BOOST_ASSERT(attached_thread_count == 0); - } - - extern "C" BOOST_THREAD_DECL void on_process_exit(void) - { - boost::call_once(once_init_threadmon_mutex, init_threadmon_mutex); -// boost::mutex::scoped_lock lock(*threadmon_mutex); - CScopedCSLock lock(&threadmon_mutex); - - BOOST_ASSERT(attached_thread_count == 0); - - //Free the tls slot if one was allocated. - - if (tls_key != invalid_tls_key) - { - TlsFree(tls_key); - tls_key = invalid_tls_key; - } - } - - extern "C" BOOST_THREAD_DECL void on_thread_enter(void) - { - //boost::call_once(init_threadmon_mutex, once_init_threadmon_mutex); - //boost::mutex::scoped_lock lock(*threadmon_mutex); - } - - extern "C" BOOST_THREAD_DECL void on_thread_exit(void) - { - boost::call_once(once_init_threadmon_mutex, init_threadmon_mutex); -// boost::mutex::scoped_lock lock(*threadmon_mutex); - CScopedCSLock lock(&threadmon_mutex); - - //Get the exit handlers list for the current thread from tls. - - if (tls_key == invalid_tls_key) - return; - - thread_exit_handlers* exit_handlers = - static_cast(TlsGetValue(tls_key)); - - //If a handlers list was found, use it. - - if (exit_handlers && TlsSetValue(tls_key, 0)) - { - BOOST_ASSERT(attached_thread_count > 0); - --attached_thread_count; - - //lock.unlock(); - lock.Unlock(); - - //Call each handler and remove it from the list - - while (!exit_handlers->empty()) - { - if (thread_exit_handler exit_handler = *exit_handlers->begin()) - (*exit_handler)(); - exit_handlers->pop_front(); - } - - delete exit_handlers; - exit_handlers = 0; - } - } - -#endif //defined(BOOST_HAS_WINTHREADS) diff --git a/src/win32/tss_pe.cpp b/src/win32/tss_pe.cpp index 010920f6..3aef63a1 100644 --- a/src/win32/tss_pe.cpp +++ b/src/win32/tss_pe.cpp @@ -220,10 +220,13 @@ extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata void NTAPI on_tls_callback(HINSTANCE h, DWORD dwReason, PVOID pv) { + OutputDebugString("on_tls_callback\n"); + switch (dwReason) { case DLL_THREAD_DETACH: { + OutputDebugString("on_tls_callback: thread_exit\n"); on_thread_exit(); break; } From a78e2b793e80a1bc2ac198790974e8b9d34db4d3 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 14 Nov 2007 11:08:09 +0000 Subject: [PATCH 051/214] ignore and join all threads in group on exception [SVN r41083] --- test/test_barrier.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/test/test_barrier.cpp b/test/test_barrier.cpp index b6982966..42f200ee 100644 --- a/test/test_barrier.cpp +++ b/test/test_barrier.cpp @@ -10,6 +10,7 @@ #include #include +#include namespace { @@ -38,9 +39,24 @@ void test_barrier() boost::thread_group g; global_parameter = 0; - for (int i = 0; i < N_THREADS; ++i) - g.create_thread(&barrier_thread); - + std::vector threads; + threads.reserve(N_THREADS); + + try + { + for (int i = 0; i < N_THREADS; ++i) + threads.push_back(g.create_thread(&barrier_thread)); + } + catch(...) + { + for(unsigned i=0;iinterrupt(); + } + g.join_all(); + throw; + } + g.join_all(); BOOST_CHECK(global_parameter == 5); From 114215088a2838f9a8ebaf4494581ecb53c31f3d Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 14 Nov 2007 11:56:53 +0000 Subject: [PATCH 052/214] interrupt and join all threads in a group if an exception is thrown during a test [SVN r41084] --- include/boost/thread/pthread/thread.hpp | 1 + include/boost/thread/win32/thread.hpp | 12 + test/test_barrier.cpp | 13 +- test/test_condition.cpp | 26 ++- test/test_once.cpp | 50 +++- test/test_shared_mutex.cpp | 291 +++++++++++++++--------- test/test_tss.cpp | 16 +- 7 files changed, 267 insertions(+), 142 deletions(-) diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index db4eb974..d86e3465 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -277,6 +277,7 @@ namespace boost void add_thread(thread* thrd); void remove_thread(thread* thrd); void join_all(); + void interrupt_all(); int size() const; private: diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 1e237791..eab05119 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -429,6 +429,18 @@ namespace boost } } + void interrupt_all() + { + boost::lock_guard guard(m); + + for(std::list::iterator it=threads.begin(),end=threads.end(); + it!=end; + ++it) + { + (*it)->interrupt(); + } + } + int size() const { boost::lock_guard guard(m); diff --git a/test/test_barrier.cpp b/test/test_barrier.cpp index 42f200ee..bccd346f 100644 --- a/test/test_barrier.cpp +++ b/test/test_barrier.cpp @@ -39,26 +39,19 @@ void test_barrier() boost::thread_group g; global_parameter = 0; - std::vector threads; - threads.reserve(N_THREADS); - try { for (int i = 0; i < N_THREADS; ++i) - threads.push_back(g.create_thread(&barrier_thread)); + g.create_thread(&barrier_thread); + g.join_all(); } catch(...) { - for(unsigned i=0;iinterrupt(); - } + g.interrupt_all(); g.join_all(); throw; } - g.join_all(); - BOOST_CHECK(global_parameter == 5); } diff --git a/test/test_condition.cpp b/test/test_condition.cpp index b827be2c..2dbe32d0 100644 --- a/test/test_condition.cpp +++ b/test/test_condition.cpp @@ -112,17 +112,27 @@ void do_test_condition_notify_all() boost::thread_group threads; condition_test_data data; - for (int i = 0; i < NUMTHREADS; ++i) - threads.create_thread(bind(&condition_test_thread, &data)); - + try { - boost::mutex::scoped_lock lock(data.mutex); - BOOST_CHECK(lock ? true : false); - data.notified++; - data.condition.notify_all(); + for (int i = 0; i < NUMTHREADS; ++i) + threads.create_thread(bind(&condition_test_thread, &data)); + + { + boost::mutex::scoped_lock lock(data.mutex); + BOOST_CHECK(lock ? true : false); + data.notified++; + data.condition.notify_all(); + } + + threads.join_all(); + } + catch(...) + { + threads.interrupt_all(); + threads.join_all(); + throw; } - threads.join_all(); BOOST_CHECK_EQUAL(data.awoken, NUMTHREADS); } diff --git a/test/test_once.cpp b/test/test_once.cpp index fe79cef9..634b4fd7 100644 --- a/test/test_once.cpp +++ b/test/test_once.cpp @@ -40,12 +40,22 @@ void test_call_once() { unsigned const num_threads=20; boost::thread_group group; - - for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_mutex,simultaneous_running_count,max_simultaneous_running)); - } + try { - boost::mutex::scoped_lock lk(unblocked_count_mutex); - while(unblocked_count >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); } + + { + boost::mutex::scoped_lock lk(unblocked_count_mutex); + while(unblocked_count >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_mutex,simultaneous_running_count,max_simultaneous_running)); + for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); + } + + boost::thread::sleep(delay(2)); + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); + + finish_lock.unlock(); + + pool.join_all(); + } + catch(...) + { + pool.interrupt_all(); + pool.join_all(); + throw; } - - boost::thread::sleep(delay(2)); - - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); - - finish_lock.unlock(); - - pool.join_all(); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,number_of_threads); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1u); @@ -162,24 +180,34 @@ void test_reader_blocks_writer() boost::mutex finish_mutex; boost::mutex::scoped_lock finish_lock(finish_mutex); - pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_mutex,simultaneous_running_count,max_simultaneous_running)); + try { - boost::mutex::scoped_lock lk(unblocked_count_mutex); - while(unblocked_count<1) + + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); { - unblocked_condition.wait(lk); + boost::mutex::scoped_lock lk(unblocked_count_mutex); + while(unblocked_count<1) + { + unblocked_condition.wait(lk); + } } + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); + + finish_lock.unlock(); + + pool.join_all(); + } + catch(...) + { + pool.interrupt_all(); + pool.join_all(); + throw; } - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); - pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_mutex,simultaneous_running_count,max_simultaneous_running)); - boost::thread::sleep(delay(1)); - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); - - finish_lock.unlock(); - - pool.join_all(); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,2U); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1u); @@ -201,28 +229,38 @@ void test_unlocking_writer_unblocks_all_readers() unsigned const reader_count=100; - for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_mutex,simultaneous_running_count,max_simultaneous_running)); - } - boost::thread::sleep(delay(1)); - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,0U); - - write_lock.unlock(); - - { - boost::mutex::scoped_lock lk(unblocked_count_mutex); - while(unblocked_count >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); } + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,0U); + + write_lock.unlock(); + + { + boost::mutex::scoped_lock lk(unblocked_count_mutex); + while(unblocked_count >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_reading_mutex,simultaneous_running_readers,max_simultaneous_readers)); - } - boost::thread::sleep(delay(1)); - for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_writing_mutex,simultaneous_running_writers,max_simultaneous_writers)); - } - { - boost::mutex::scoped_lock lk(unblocked_count_mutex); - while(unblocked_count >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_reading_mutex,simultaneous_running_readers,max_simultaneous_readers)); } - } - boost::thread::sleep(delay(1)); - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count); - - finish_reading_lock.unlock(); - - { - boost::mutex::scoped_lock lk(unblocked_count_mutex); - while(unblocked_count<(reader_count+1)) + boost::thread::sleep(delay(1)); + for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_writing_mutex,simultaneous_running_writers,max_simultaneous_writers)); } - } - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); + { + boost::mutex::scoped_lock lk(unblocked_count_mutex); + while(unblocked_count >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_mutex,simultaneous_running_count,max_simultaneous_running)); + for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); + } + + boost::thread::sleep(delay(1)); + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); + + finish_lock.unlock(); + + pool.join_all(); + } + catch(...) + { + pool.interrupt_all(); + pool.join_all(); + throw; } - - boost::thread::sleep(delay(1)); - - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); - - finish_lock.unlock(); - - pool.join_all(); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,number_of_threads); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1u); @@ -333,25 +391,36 @@ void test_can_lock_upgrade_if_currently_locked_shared() unsigned const reader_count=100; - for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_mutex,simultaneous_running_count,max_simultaneous_running)); - } - boost::thread::sleep(delay(1)); - pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_mutex,simultaneous_running_count,max_simultaneous_running)); - { - boost::mutex::scoped_lock lk(unblocked_count_mutex); - while(unblocked_count<(reader_count+1)) + for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); } - } - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); + boost::thread::sleep(delay(1)); + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); + { + boost::mutex::scoped_lock lk(unblocked_count_mutex); + while(unblocked_count<(reader_count+1)) + { + unblocked_condition.wait(lk); + } + } + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); + + finish_lock.unlock(); + pool.join_all(); + } + catch(...) + { + pool.interrupt_all(); + pool.join_all(); + throw; + } + - finish_lock.unlock(); - pool.join_all(); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,reader_count+1); } diff --git a/test/test_tss.cpp b/test/test_tss.cpp index 6ed570f1..7c166648 100644 --- a/test/test_tss.cpp +++ b/test/test_tss.cpp @@ -100,9 +100,19 @@ void do_test_tss() const int NUMTHREADS=5; boost::thread_group threads; - for (int i=0; i* pcleanup); +}; + +template +struct tss_adapter +{ + template + tss_adapter(const F& cleanup) : m_cleanup(cleanup) { } + void operator()(void* p) { m_cleanup(static_cast(p)); } + boost::function1 m_cleanup; +}; + +} // namespace detail + +template +class thread_specific_ptr : private noncopyable +{ +public: + thread_specific_ptr() + : m_tss(new boost::function1( + boost::detail::tss_adapter( + &thread_specific_ptr::cleanup))) + { + } + thread_specific_ptr(void (*clean)(T*)) + : m_tss(new boost::function1( + boost::detail::tss_adapter(clean))) + { + } + ~thread_specific_ptr() { reset(); } + + T* get() const { return static_cast(m_tss.get()); } + T* operator->() const { return get(); } + T& operator*() const { return *get(); } + T* release() { T* temp = get(); if (temp) m_tss.set(0); return temp; } + void reset(T* p=0) + { + T* cur = get(); + if (cur == p) return; + m_tss.set(p); + if (cur) m_tss.cleanup(cur); + } + +private: + static void cleanup(T* p) { delete p; } + detail::tss m_tss; +}; + +#ifdef BOOST_MSVC +# pragma warning(pop) +#endif + +} // namespace boost + + + +#endif diff --git a/include/boost/thread/tss.hpp b/include/boost/thread/tss.hpp index 1870324c..787700f5 100644 --- a/include/boost/thread/tss.hpp +++ b/include/boost/thread/tss.hpp @@ -1,130 +1,12 @@ -// Copyright (C) 2001-2003 William E. Kempf -// Copyright (C) 2006 Roland Schwarz // Copyright (C) 2007 Anthony Williams // // 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) -#ifndef BOOST_TSS_WEK070601_HPP -#define BOOST_TSS_WEK070601_HPP +#ifndef BOOST_THREAD_TSS_HPP +#define BOOST_THREAD_TSS_HPP -#include - -#ifdef BOOST_HAS_WINTHREADS #include #include BOOST_THREAD_PLATFORM(tss.hpp) -#else - -#include -#include -#include - -#if defined(BOOST_HAS_PTHREADS) -# include -#endif - -namespace boost { - -// disable warnings about non dll import -// see: http://www.boost.org/more/separate_compilation.html#dlls -#ifdef BOOST_MSVC -# pragma warning(push) -# pragma warning(disable: 4251 4231 4660 4275) -#endif - -namespace detail { - -class BOOST_THREAD_DECL tss : private noncopyable -{ -public: - tss(boost::function1* pcleanup) { - if (pcleanup == 0) throw boost::thread_resource_error(); - try - { - init(pcleanup); - } - catch (...) - { - delete pcleanup; - throw boost::thread_resource_error(); - } - } - - ~tss(); - void* get() const; - void set(void* value); - void cleanup(void* p); - -private: - unsigned int m_slot; //This is a "pseudo-slot", not a native slot - - void init(boost::function1* pcleanup); -}; - -template -struct tss_adapter -{ - template - tss_adapter(const F& cleanup) : m_cleanup(cleanup) { } - void operator()(void* p) { m_cleanup(static_cast(p)); } - boost::function1 m_cleanup; -}; - -} // namespace detail - -template -class thread_specific_ptr : private noncopyable -{ -public: - thread_specific_ptr() - : m_tss(new boost::function1( - boost::detail::tss_adapter( - &thread_specific_ptr::cleanup))) - { - } - thread_specific_ptr(void (*clean)(T*)) - : m_tss(new boost::function1( - boost::detail::tss_adapter(clean))) - { - } - ~thread_specific_ptr() { reset(); } - - T* get() const { return static_cast(m_tss.get()); } - T* operator->() const { return get(); } - T& operator*() const { return *get(); } - T* release() { T* temp = get(); if (temp) m_tss.set(0); return temp; } - void reset(T* p=0) - { - T* cur = get(); - if (cur == p) return; - m_tss.set(p); - if (cur) m_tss.cleanup(cur); - } - -private: - static void cleanup(T* p) { delete p; } - detail::tss m_tss; -}; - -#ifdef BOOST_MSVC -# pragma warning(pop) -#endif - -} // namespace boost #endif - -#endif //BOOST_TSS_WEK070601_HPP - -// Change Log: -// 6 Jun 01 -// WEKEMPF Initial version. -// 30 May 02 WEKEMPF -// Added interface to set specific cleanup handlers. -// Removed TLS slot limits from most implementations. -// 22 Mar 04 GlassfordM for WEKEMPF -// Fixed: thread_specific_ptr::reset() doesn't check error returned -// by tss::set(); tss::set() now throws if it fails. -// Fixed: calling thread_specific_ptr::reset() or -// thread_specific_ptr::release() causes double-delete: once on -// reset()/release() and once on ~thread_specific_ptr(). diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index f7fbd00a..1d8370de 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -493,9 +493,9 @@ namespace boost void thread_group::interrupt_all() { - boost::lock_guard guard(m); + boost::lock_guard guard(m_mutex); - for(std::list::iterator it=threads.begin(),end=threads.end(); + for(std::list::iterator it=m_threads.begin(),end=m_threads.end(); it!=end; ++it) { From 5e0b2d73700dc7abf38d50411aa524c24952973a Mon Sep 17 00:00:00 2001 From: Beman Dawes Date: Thu, 15 Nov 2007 15:20:27 +0000 Subject: [PATCH 055/214] Get rid of .cvsignore files [SVN r41107] --- build/.cvsignore | 2 -- example/.cvsignore | 2 -- test/.cvsignore | 2 -- 3 files changed, 6 deletions(-) delete mode 100644 build/.cvsignore delete mode 100644 example/.cvsignore delete mode 100644 test/.cvsignore diff --git a/build/.cvsignore b/build/.cvsignore deleted file mode 100644 index 76bf5bcd..00000000 --- a/build/.cvsignore +++ /dev/null @@ -1,2 +0,0 @@ -bin* -*.pdb diff --git a/example/.cvsignore b/example/.cvsignore deleted file mode 100644 index cdbfc82a..00000000 --- a/example/.cvsignore +++ /dev/null @@ -1,2 +0,0 @@ -bin -*.pdb diff --git a/test/.cvsignore b/test/.cvsignore deleted file mode 100644 index cdbfc82a..00000000 --- a/test/.cvsignore +++ /dev/null @@ -1,2 +0,0 @@ -bin -*.pdb From 3c191af34a434d814d8891e44a019ae740603d54 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 16 Nov 2007 22:51:52 +0000 Subject: [PATCH 056/214] New implementation of pthread_once based on Mike Burrows' algorithm [SVN r41160] --- build/Jamfile.v2 | 1 + include/boost/thread/pthread/once.hpp | 76 +++++++++++++------ .../pthread/pthread_mutex_scoped_lock.hpp | 16 ++++ src/pthread/once.cpp | 51 +++++++++++++ 4 files changed, 121 insertions(+), 23 deletions(-) create mode 100755 src/pthread/once.cpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 860542be..8abffe50 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -191,6 +191,7 @@ alias thread_sources pthread/thread.cpp pthread/exceptions.cpp pthread/tss.cpp + pthread/once.cpp : ## requirements ## pthread ; diff --git a/include/boost/thread/pthread/once.hpp b/include/boost/thread/pthread/once.hpp index 7d8e8eb3..90dc0c70 100644 --- a/include/boost/thread/pthread/once.hpp +++ b/include/boost/thread/pthread/once.hpp @@ -14,42 +14,72 @@ #include #include #include "pthread_mutex_scoped_lock.hpp" +#include +#include namespace boost { struct once_flag { - pthread_mutex_t mutex; - unsigned long flag; + boost::uintmax_t epoch; }; -#define BOOST_ONCE_INIT {PTHREAD_MUTEX_INITIALIZER,0} + namespace detail + { + BOOST_THREAD_DECL boost::uintmax_t& get_once_per_thread_epoch(); + extern BOOST_THREAD_DECL boost::uintmax_t once_global_epoch; + extern BOOST_THREAD_DECL pthread_mutex_t once_epoch_mutex; + extern BOOST_THREAD_DECL pthread_cond_t once_epoch_cv; + } + +#define BOOST_ONCE_INITIAL_FLAG_VALUE -1 +#define BOOST_ONCE_INIT {BOOST_ONCE_INITIAL_FLAG_VALUE} + + // Based on Mike Burrows fast_pthread_once algorithm as described in + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2444.html template void call_once(once_flag& flag,Function f) { - unsigned long const function_complete_flag_value=0xc15730e2ul; - -#ifdef BOOST_PTHREAD_HAS_ATOMICS - if(::boost::detail::interlocked_read_acquire(&flag.flag)!=function_complete_flag_value) + static boost::uintmax_t const uninitialized_flag=BOOST_ONCE_INITIAL_FLAG_VALUE; + static boost::uintmax_t const being_initialized=uninitialized_flag-1; + boost::uintmax_t const epoch=flag.epoch; + boost::uintmax_t& this_thread_epoch=detail::get_once_per_thread_epoch(); + + if(epoch>this_thread_epoch) { -#endif - pthread::pthread_mutex_scoped_lock const lock(&flag.mutex); - if(flag.flag!=function_complete_flag_value) - { - f(); -#ifdef BOOST_PTHREAD_HAS_ATOMICS - ::boost::detail::interlocked_write_release(&flag.flag,function_complete_flag_value); -#else - flag.flag=function_complete_flag_value; -#endif - } -#ifdef BOOST_PTHREAD_HAS_ATOMICS - } -#endif - } - + pthread::pthread_mutex_scoped_lock lk(&detail::once_epoch_mutex); + while(flag.epoch>=being_initialized) + { + if(flag.epoch==uninitialized_flag) + { + flag.epoch=being_initialized; + try + { + pthread::pthread_mutex_scoped_unlock relocker(&detail::once_epoch_mutex); + f(); + } + catch(...) + { + flag.epoch=uninitialized_flag; + BOOST_VERIFY(!pthread_cond_broadcast(&detail::once_epoch_cv)); + throw; + } + flag.epoch=++detail::once_global_epoch; + BOOST_VERIFY(!pthread_cond_broadcast(&detail::once_epoch_cv)); + } + else + { + while(flag.epoch==being_initialized) + { + BOOST_VERIFY(!pthread_cond_wait(&detail::once_epoch_cv,&detail::once_epoch_mutex)); + } + } + } + this_thread_epoch=detail::once_global_epoch; + } + } } #endif diff --git a/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp b/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp index 304718bc..6b474a95 100644 --- a/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp +++ b/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp @@ -28,6 +28,22 @@ namespace boost } }; + + class pthread_mutex_scoped_unlock + { + pthread_mutex_t* m; + public: + explicit pthread_mutex_scoped_unlock(pthread_mutex_t* m_): + m(m_) + { + BOOST_VERIFY(!pthread_mutex_unlock(m)); + } + ~pthread_mutex_scoped_unlock() + { + BOOST_VERIFY(!pthread_mutex_lock(m)); + } + + }; } } diff --git a/src/pthread/once.cpp b/src/pthread/once.cpp new file mode 100755 index 00000000..b893a0b5 --- /dev/null +++ b/src/pthread/once.cpp @@ -0,0 +1,51 @@ +// Copyright (C) 2007 Anthony Williams +// +// 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) + +#include +#include +#include +#include + +namespace boost +{ + namespace detail + { + boost::uintmax_t once_global_epoch=0; + pthread_mutex_t once_epoch_mutex=PTHREAD_MUTEX_INITIALIZER; + pthread_cond_t once_epoch_cv = PTHREAD_COND_INITIALIZER; + + namespace + { + pthread_key_t epoch_tss_key; + pthread_once_t epoch_tss_key_flag=PTHREAD_ONCE_INIT; + + extern "C" void delete_epoch_tss_data(void* data) + { + free(data); + } + + extern "C" void create_epoch_tss_key() + { + BOOST_VERIFY(!pthread_key_create(&epoch_tss_key,delete_epoch_tss_data)); + } + + } + + boost::uintmax_t& get_once_per_thread_epoch() + { + BOOST_VERIFY(!pthread_once(&epoch_tss_key_flag,create_epoch_tss_key)); + void* data=pthread_getspecific(epoch_tss_key); + if(!data) + { + data=malloc(sizeof(boost::uintmax_t)); + BOOST_VERIFY(!pthread_setspecific(epoch_tss_key,data)); + *static_cast(data)=0; + } + return *static_cast(data); + } + + } + +} From 9c8e512edd970ca47f0e451e0b861d2ebbb5f1a1 Mon Sep 17 00:00:00 2001 From: Beman Dawes Date: Sat, 17 Nov 2007 20:13:16 +0000 Subject: [PATCH 057/214] // Add or correct comment identifying Boost library this header is associated with. [SVN r41173] --- include/boost/thread.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/boost/thread.hpp b/include/boost/thread.hpp index b9a9ed68..625e69ec 100644 --- a/include/boost/thread.hpp +++ b/include/boost/thread.hpp @@ -4,6 +4,8 @@ // 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) +// See www.boost.org/libs/thread for documentation. + #if !defined(BOOST_THREAD_WEK01082003_HPP) #define BOOST_THREAD_WEK01082003_HPP From 72fcee4e5e143d777183c1c9c6aefd0027a595f1 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 19 Nov 2007 12:17:31 +0000 Subject: [PATCH 058/214] fixed TSS cleanup on 64-bit Windows [SVN r41222] --- src/win32/tss_pe.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/win32/tss_pe.cpp b/src/win32/tss_pe.cpp index 3aef63a1..4528b90f 100644 --- a/src/win32/tss_pe.cpp +++ b/src/win32/tss_pe.cpp @@ -120,6 +120,16 @@ extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata //The .CRT$Xxx information is taken from Codeguru: //http://www.codeguru.com/Cpp/misc/misc/threadsprocesses/article.php/c6945__2/ +#if (_MSC_VER >= 1400) +#pragma section(".CRT$XIU",long,read) +#pragma section(".CRT$XCU",long,read) +#pragma section(".CRT$XTU",long,read) +#pragma section(".CRT$XLC",long,read) + static __declspec(allocate(".CRT$XLC")) _TLSCB __xl_ca=on_tls_callback; + static __declspec(allocate(".CRT$XIU"))_PVFV p_tls_prepare = on_tls_prepare; + static __declspec(allocate(".CRT$XCU"))_PVFV p_process_init = on_process_init; + static __declspec(allocate(".CRT$XTU"))_PVFV p_process_term = on_process_term; +#else #if (_MSC_VER >= 1300) // 1300 == VC++ 7.0 # pragma data_seg(push, old_seg) #endif @@ -144,7 +154,6 @@ extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata #pragma data_seg(".CRT$XLB") _TLSCB p_thread_callback = on_tls_callback; #pragma data_seg() - //Callback for termination. #pragma data_seg(".CRT$XTU") @@ -153,6 +162,7 @@ extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata #if (_MSC_VER >= 1300) // 1300 == VC++ 7.0 # pragma data_seg(pop, old_seg) #endif +#endif PVAPI on_tls_prepare(void) { From c9140267a5e22336f03d5f88a8408934d00763ff Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 19 Nov 2007 12:29:14 +0000 Subject: [PATCH 059/214] fixed problems with TSS cleanup when using LoadLibrary and when threads finish after thread_specific_ptr instance has been destroyed [SVN r41223] --- .../boost/thread/win32/thread_heap_alloc.hpp | 16 +----- include/boost/thread/win32/tss.hpp | 50 ++++++++++++------- src/win32/thread.cpp | 40 ++++----------- 3 files changed, 46 insertions(+), 60 deletions(-) diff --git a/include/boost/thread/win32/thread_heap_alloc.hpp b/include/boost/thread/win32/thread_heap_alloc.hpp index 1835e35e..ee47e0e6 100644 --- a/include/boost/thread/win32/thread_heap_alloc.hpp +++ b/include/boost/thread/win32/thread_heap_alloc.hpp @@ -156,25 +156,13 @@ namespace boost } template - struct do_delete + struct do_heap_delete { - T* data; - - do_delete(T* data_): - data(data_) - {} - - void operator()() const + void operator()(T* data) const { detail::heap_delete(data); } }; - - template - do_delete make_heap_deleter(T* data) - { - return do_delete(data); - } } } diff --git a/include/boost/thread/win32/tss.hpp b/include/boost/thread/win32/tss.hpp index a99dfac4..665253ec 100644 --- a/include/boost/thread/win32/tss.hpp +++ b/include/boost/thread/win32/tss.hpp @@ -1,13 +1,21 @@ #ifndef BOOST_THREAD_WIN32_TSS_HPP #define BOOST_THREAD_WIN32_TSS_HPP +#include +#include "thread_heap_alloc.hpp" namespace boost { namespace detail { - typedef void(*tss_cleanup_function)(void const* key,void* value); + struct tss_cleanup_function + { + virtual ~tss_cleanup_function() + {} + + virtual void operator()(void* data)=0; + }; - BOOST_THREAD_DECL void set_tss_data(void const* key,tss_cleanup_function func,void* tss_data,bool cleanup_existing); + BOOST_THREAD_DECL void set_tss_data(void const* key,boost::shared_ptr func,void* tss_data,bool cleanup_existing); BOOST_THREAD_DECL void* get_tss_data(void const* key); } @@ -18,31 +26,39 @@ namespace boost thread_specific_ptr(thread_specific_ptr&); thread_specific_ptr& operator=(thread_specific_ptr&); - static void delete_data(void const* self,void* value) + struct delete_data: + detail::tss_cleanup_function { - static_cast(self)->cleanup((T*)value); - } + void operator()(void* data) + { + delete static_cast(data); + } + }; - void cleanup(T* data) const + struct run_custom_cleanup_function: + detail::tss_cleanup_function { - if(func) + void (*cleanup_function)(T*); + + explicit run_custom_cleanup_function(void (*cleanup_function_)(T*)): + cleanup_function(cleanup_function_) + {} + + void operator()(void* data) { - func(data); + cleanup_function(data); } - else - { - delete data; - } - } + }; - void (*func)(T*); + + boost::shared_ptr cleanup; public: thread_specific_ptr(): - func(0) + cleanup(detail::heap_new(),detail::do_heap_delete()) {} explicit thread_specific_ptr(void (*func_)(T*)): - func(func_) + cleanup(detail::heap_new(func_),detail::do_heap_delete()) {} ~thread_specific_ptr() { @@ -72,7 +88,7 @@ namespace boost T* const current_value=get(); if(current_value!=new_value) { - detail::set_tss_data(this,delete_data,new_value,true); + detail::set_tss_data(this,cleanup,new_value,true); } } }; diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index fee2b4fd..9df6a05a 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -20,28 +20,6 @@ namespace boost { namespace { -#if defined(_MSC_VER) && !defined(UNDER_CE) - __declspec(thread) detail::thread_data_base* current_thread_data=0; - detail::thread_data_base* get_current_thread_data() - { - return current_thread_data; - } - void set_current_thread_data(detail::thread_data_base* new_data) - { - current_thread_data=new_data; - } -#elif defined(__BORLANDC__) - detail::thread_data_base* __thread current_thread_data=0; - detail::thread_data_base* get_current_thread_data() - { - return current_thread_data; - } - void set_current_thread_data(detail::thread_data_base* new_data) - { - current_thread_data=new_data; - } -#else - boost::once_flag current_thread_tls_init_flag=BOOST_ONCE_INIT; DWORD current_thread_tls_key=0; @@ -62,7 +40,6 @@ namespace boost boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); BOOST_VERIFY(TlsSetValue(current_thread_tls_key,new_data)); } -#endif #ifdef BOOST_NO_THREADEX // Windows CE doesn't define _beginthreadex @@ -135,11 +112,11 @@ namespace boost struct tss_data_node { void const* key; - boost::detail::tss_cleanup_function func; + boost::shared_ptr func; void* value; tss_data_node* next; - tss_data_node(void const* key_,boost::detail::tss_cleanup_function func_,void* value_, + tss_data_node(void const* key_,boost::shared_ptr func_,void* value_, tss_data_node* next_): key(key_),func(func_),value(value_),next(next_) {} @@ -173,7 +150,7 @@ namespace boost current_thread_data->tss_data=current_node->next; if(current_node->func) { - (*current_node->func)(current_node->key,current_node->value); + (*current_node->func)(current_node->value); } boost::detail::heap_delete(current_node); } @@ -489,7 +466,12 @@ namespace boost { void add_thread_exit_function(thread_exit_function_base* func) { - detail::thread_data_base* const current_thread_data(get_current_thread_data()); + detail::thread_data_base* current_thread_data(get_current_thread_data()); + if(!current_thread_data) + { + make_external_thread_data(); + current_thread_data=get_current_thread_data(); + } thread_exit_callback_node* const new_node= heap_new(func, current_thread_data->thread_exit_callbacks); @@ -523,14 +505,14 @@ namespace boost return NULL; } - void set_tss_data(void const* key,tss_cleanup_function func,void* tss_data,bool cleanup_existing) + void set_tss_data(void const* key,boost::shared_ptr func,void* tss_data,bool cleanup_existing) { tss_cleanup_implemented(); // if anyone uses TSS, we need the cleanup linked in if(tss_data_node* const current_node=find_tss_data(key)) { if(cleanup_existing && current_node->func) { - (current_node->func)(current_node->key,current_node->value); + (*current_node->func)(current_node->value); } current_node->func=func; current_node->value=tss_data; From 4ff0a055d670ab5e17fb444b09eb493c3d272cb1 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 19 Nov 2007 14:29:22 +0000 Subject: [PATCH 060/214] added copyright [SVN r41226] --- include/boost/thread/win32/tss.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/boost/thread/win32/tss.hpp b/include/boost/thread/win32/tss.hpp index 665253ec..fcf792bd 100644 --- a/include/boost/thread/win32/tss.hpp +++ b/include/boost/thread/win32/tss.hpp @@ -1,5 +1,10 @@ #ifndef BOOST_THREAD_WIN32_TSS_HPP #define BOOST_THREAD_WIN32_TSS_HPP +// 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) +// (C) Copyright 2007 Anthony Williams + #include #include "thread_heap_alloc.hpp" From f6fd70245d31e6dce44fc1c6497fcd5e6257e60a Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 21 Nov 2007 10:44:22 +0000 Subject: [PATCH 061/214] changed platform split to allow bjam to track includes and check dependencies [SVN r41273] --- include/boost/thread/condition_variable.hpp | 8 +++++++- include/boost/thread/detail/platform.hpp | 9 ++++----- include/boost/thread/mutex.hpp | 8 +++++++- include/boost/thread/once.hpp | 8 +++++++- include/boost/thread/recursive_mutex.hpp | 8 +++++++- include/boost/thread/shared_mutex.hpp | 8 +++++++- include/boost/thread/thread.hpp | 9 ++++++++- include/boost/thread/tss.hpp | 8 +++++++- 8 files changed, 54 insertions(+), 12 deletions(-) diff --git a/include/boost/thread/condition_variable.hpp b/include/boost/thread/condition_variable.hpp index 2be3cd5a..8f8e9f2e 100644 --- a/include/boost/thread/condition_variable.hpp +++ b/include/boost/thread/condition_variable.hpp @@ -10,6 +10,12 @@ // http://www.boost.org/LICENSE_1_0.txt) #include -#include BOOST_THREAD_PLATFORM(condition_variable.hpp) +#if defined(BOOST_THREAD_PLATFORM_WIN32) +#include +#elif defined(BOOST_THREAD_PLATFORM_PTHREAD) +#include +#else +#error "Boost threads unavailable on this platform" +#endif #endif diff --git a/include/boost/thread/detail/platform.hpp b/include/boost/thread/detail/platform.hpp index b49a5208..32a8a22b 100644 --- a/include/boost/thread/detail/platform.hpp +++ b/include/boost/thread/detail/platform.hpp @@ -1,4 +1,5 @@ // Copyright 2006 Roland Schwarz. +// (C) Copyright 2007 Anthony Williams // 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) @@ -56,17 +57,15 @@ // available the preprocessor will fail with a diagnostic message. #if defined(BOOST_THREAD_POSIX) -# define BOOST_THREAD_PPFX pthread +# define BOOST_THREAD_PLATFORM_PTHREAD #else # if defined(BOOST_THREAD_WIN32) -# define BOOST_THREAD_PPFX win32 +# define BOOST_THREAD_PLATFORM_WIN32 # elif defined(BOOST_HAS_PTHREADS) -# define BOOST_THREAD_PPFX pthread +# define BOOST_THREAD_PLATFORM_PTHREAD # else # error "Sorry, no boost threads are available for this platform." # endif #endif -#define BOOST_THREAD_PLATFORM(header) - #endif // BOOST_THREAD_RS06040501_HPP diff --git a/include/boost/thread/mutex.hpp b/include/boost/thread/mutex.hpp index 98a1cbdd..4669886c 100644 --- a/include/boost/thread/mutex.hpp +++ b/include/boost/thread/mutex.hpp @@ -10,6 +10,12 @@ // http://www.boost.org/LICENSE_1_0.txt) #include -#include BOOST_THREAD_PLATFORM(mutex.hpp) +#if defined(BOOST_THREAD_PLATFORM_WIN32) +#include +#elif defined(BOOST_THREAD_PLATFORM_PTHREAD) +#include +#else +#error "Boost threads unavailable on this platform" +#endif #endif diff --git a/include/boost/thread/once.hpp b/include/boost/thread/once.hpp index d29efe8f..608b77d6 100644 --- a/include/boost/thread/once.hpp +++ b/include/boost/thread/once.hpp @@ -10,7 +10,13 @@ // http://www.boost.org/LICENSE_1_0.txt) #include -#include BOOST_THREAD_PLATFORM(once.hpp) +#if defined(BOOST_THREAD_PLATFORM_WIN32) +#include +#elif defined(BOOST_THREAD_PLATFORM_PTHREAD) +#include +#else +#error "Boost threads unavailable on this platform" +#endif namespace boost { diff --git a/include/boost/thread/recursive_mutex.hpp b/include/boost/thread/recursive_mutex.hpp index f01ae11b..d5f6116e 100644 --- a/include/boost/thread/recursive_mutex.hpp +++ b/include/boost/thread/recursive_mutex.hpp @@ -10,6 +10,12 @@ // http://www.boost.org/LICENSE_1_0.txt) #include -#include BOOST_THREAD_PLATFORM(recursive_mutex.hpp) +#if defined(BOOST_THREAD_PLATFORM_WIN32) +#include +#elif defined(BOOST_THREAD_PLATFORM_PTHREAD) +#include +#else +#error "Boost threads unavailable on this platform" +#endif #endif diff --git a/include/boost/thread/shared_mutex.hpp b/include/boost/thread/shared_mutex.hpp index 17f72d84..51eda0de 100644 --- a/include/boost/thread/shared_mutex.hpp +++ b/include/boost/thread/shared_mutex.hpp @@ -10,6 +10,12 @@ // http://www.boost.org/LICENSE_1_0.txt) #include -#include BOOST_THREAD_PLATFORM(shared_mutex.hpp) +#if defined(BOOST_THREAD_PLATFORM_WIN32) +#include +#elif defined(BOOST_THREAD_PLATFORM_PTHREAD) +#include +#else +#error "Boost threads unavailable on this platform" +#endif #endif diff --git a/include/boost/thread/thread.hpp b/include/boost/thread/thread.hpp index da4323f0..a1714981 100644 --- a/include/boost/thread/thread.hpp +++ b/include/boost/thread/thread.hpp @@ -10,6 +10,13 @@ // http://www.boost.org/LICENSE_1_0.txt) #include -#include BOOST_THREAD_PLATFORM(thread.hpp) + +#if defined(BOOST_THREAD_PLATFORM_WIN32) +#include +#elif defined(BOOST_THREAD_PLATFORM_PTHREAD) +#include +#else +#error "Boost threads unavailable on this platform" +#endif #endif diff --git a/include/boost/thread/tss.hpp b/include/boost/thread/tss.hpp index 787700f5..131bcde2 100644 --- a/include/boost/thread/tss.hpp +++ b/include/boost/thread/tss.hpp @@ -7,6 +7,12 @@ #define BOOST_THREAD_TSS_HPP #include -#include BOOST_THREAD_PLATFORM(tss.hpp) +#if defined(BOOST_THREAD_PLATFORM_WIN32) +#include +#elif defined(BOOST_THREAD_PLATFORM_PTHREAD) +#include +#else +#error "Boost threads unavailable on this platform" +#endif #endif From bce8db41d7f1efd9d89adfdea1338ac57a93f6e0 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 22 Nov 2007 22:01:30 +0000 Subject: [PATCH 062/214] Removed thread::self in favour of allowing interruption through a thread::id; no longer requires DuplicateHandle [SVN r41311] --- include/boost/thread/win32/thread.hpp | 101 ++++++++------------------ src/win32/thread.cpp | 69 ++++++------------ 2 files changed, 56 insertions(+), 114 deletions(-) diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index eab05119..16315b00 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -15,6 +15,7 @@ #include "thread_primitives.hpp" #include "thread_heap_alloc.hpp" #include +#include #include #include #include @@ -62,8 +63,16 @@ namespace boost } } + void interrupt() + { + BOOST_VERIFY(detail::win32::SetEvent(interruption_handle)!=0); + } + + virtual void run()=0; }; + + typedef boost::intrusive_ptr thread_data_ptr; } class BOOST_THREAD_DECL thread @@ -94,15 +103,15 @@ namespace boost }; mutable boost::mutex thread_info_mutex; - boost::intrusive_ptr thread_info; + detail::thread_data_ptr thread_info; static unsigned __stdcall thread_start_function(void* param); void start_thread(); - explicit thread(boost::intrusive_ptr data); + explicit thread(detail::thread_data_ptr data); - boost::intrusive_ptr get_thread_info() const; + detail::thread_data_ptr get_thread_info() const; public: thread(); ~thread(); @@ -155,12 +164,8 @@ namespace boost static void sleep(const system_time& xt); // extensions - class interruption_handle; - interruption_handle get_interruption_handle() const; void interrupt(); bool interruption_requested() const; - - static thread self(); }; template @@ -214,7 +219,6 @@ namespace boost void BOOST_THREAD_DECL interruption_point(); bool BOOST_THREAD_DECL interruption_enabled(); bool BOOST_THREAD_DECL interruption_requested(); - thread::interruption_handle BOOST_THREAD_DECL get_interruption_handle(); void BOOST_THREAD_DECL yield(); template @@ -227,54 +231,63 @@ namespace boost class thread::id { private: - unsigned thread_id; + detail::thread_data_ptr thread_data; - id(unsigned thread_id_): - thread_id(thread_id_) + id(detail::thread_data_ptr thread_data_): + thread_data(thread_data_) {} friend class thread; friend id this_thread::get_id(); public: id(): - thread_id(0) + thread_data(0) {} bool operator==(const id& y) const { - return thread_id==y.thread_id; + return thread_data==y.thread_data; } bool operator!=(const id& y) const { - return thread_id!=y.thread_id; + return thread_data!=y.thread_data; } bool operator<(const id& y) const { - return thread_id(const id& y) const { - return thread_id>y.thread_id; + return y.thread_data=(const id& y) const { - return thread_id>=y.thread_id; + return !(thread_data friend std::basic_ostream& operator<<(std::basic_ostream& os, const id& x) { - return os<interrupt(); + } + } + }; inline bool thread::operator==(const thread& other) const @@ -286,56 +299,6 @@ namespace boost { return get_id()!=other.get_id(); } - - class thread::interruption_handle - { - private: - boost::detail::win32::handle_manager handle; - friend class thread; - friend interruption_handle this_thread::get_interruption_handle(); - - interruption_handle(detail::win32::handle h_): - handle(h_) - {} - public: - interruption_handle(interruption_handle const& other): - handle(other.handle.duplicate()) - {} - interruption_handle(): - handle(0) - {} - - void swap(interruption_handle& other) - { - handle.swap(other.handle); - } - - interruption_handle& operator=(interruption_handle const& other) - { - interruption_handle temp(other); - swap(temp); - return *this; - } - - void reset() - { - handle=0; - } - - void interrupt() - { - if(handle) - { - detail::win32::SetEvent(handle); - } - } - - typedef void(interruption_handle::*bool_type)(); - operator bool_type() const - { - return handle?&interruption_handle::interrupt:0; - } - }; namespace detail { diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index 9df6a05a..fa83b7cf 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -128,7 +128,7 @@ namespace boost { void run_thread_exit_callbacks() { - boost::intrusive_ptr current_thread_data(get_current_thread_data(),false); + detail::thread_data_ptr current_thread_data(get_current_thread_data(),false); if(current_thread_data) { while(current_thread_data->tss_data || current_thread_data->thread_exit_callbacks) @@ -197,7 +197,7 @@ namespace boost ResumeThread(thread_info->thread_handle); } - thread::thread(boost::intrusive_ptr data): + thread::thread(detail::thread_data_ptr data): thread_info(data) {} @@ -210,7 +210,6 @@ namespace boost { ++count; interruption_enabled=false; - thread_handle=detail::win32::duplicate_handle(detail::win32::GetCurrentThread()); } void run() @@ -222,18 +221,20 @@ namespace boost externally_launched_thread* me=detail::heap_new(); set_current_thread_data(me); } + + detail::thread_data_base* get_or_make_current_thread_data() + { + detail::thread_data_base* current_thread_data(get_current_thread_data()); + if(!current_thread_data) + { + make_external_thread_data(); + current_thread_data=get_current_thread_data(); + } + return current_thread_data; + } } - thread thread::self() - { - if(!get_current_thread_data()) - { - make_external_thread_data(); - } - return thread(boost::intrusive_ptr(get_current_thread_data())); - } - thread::~thread() { detach(); @@ -273,16 +274,9 @@ namespace boost thread::id thread::get_id() const { - boost::intrusive_ptr local_thread_info=get_thread_info(); - return local_thread_info?thread::id(local_thread_info->id):thread::id(); + return thread::id(get_thread_info()); } - thread::interruption_handle thread::get_interruption_handle() const - { - boost::intrusive_ptr local_thread_info=get_thread_info(); - return local_thread_info?thread::interruption_handle(local_thread_info->interruption_handle.duplicate()):thread::interruption_handle(); - } - bool thread::joinable() const { return get_thread_info(); @@ -290,7 +284,7 @@ namespace boost void thread::join() { - boost::intrusive_ptr local_thread_info=get_thread_info(); + detail::thread_data_ptr local_thread_info=get_thread_info(); if(local_thread_info) { this_thread::interruptible_wait(local_thread_info->thread_handle,detail::win32::infinite); @@ -300,7 +294,7 @@ namespace boost bool thread::timed_join(boost::system_time const& wait_until) { - boost::intrusive_ptr local_thread_info=get_thread_info(); + detail::thread_data_ptr local_thread_info=get_thread_info(); if(local_thread_info) { if(!this_thread::interruptible_wait(local_thread_info->thread_handle,get_milliseconds_until(wait_until))) @@ -325,16 +319,16 @@ namespace boost void thread::interrupt() { - boost::intrusive_ptr local_thread_info=get_thread_info(); + detail::thread_data_ptr local_thread_info=get_thread_info(); if(local_thread_info) { - detail::win32::SetEvent(local_thread_info->interruption_handle); + local_thread_info->interrupt(); } } bool thread::interruption_requested() const { - boost::intrusive_ptr local_thread_info=get_thread_info(); + detail::thread_data_ptr local_thread_info=get_thread_info(); return local_thread_info.get() && (detail::win32::WaitForSingleObject(local_thread_info->interruption_handle,0)==0); } @@ -347,11 +341,11 @@ namespace boost thread::native_handle_type thread::native_handle() { - boost::intrusive_ptr local_thread_info=get_thread_info(); + detail::thread_data_ptr local_thread_info=get_thread_info(); return local_thread_info?(detail::win32::handle)local_thread_info->thread_handle:detail::win32::invalid_handle_value; } - boost::intrusive_ptr thread::get_thread_info() const + detail::thread_data_ptr thread::get_thread_info() const { boost::mutex::scoped_lock l(thread_info_mutex); return thread_info; @@ -359,11 +353,6 @@ namespace boost namespace this_thread { - thread::interruption_handle get_interruption_handle() - { - return get_current_thread_data()?thread::interruption_handle(get_current_thread_data()->interruption_handle.duplicate()):thread::interruption_handle(); - } - bool interruptible_wait(detail::win32::handle handle_to_wait_for,unsigned long milliseconds) { detail::win32::handle handles[2]={0}; @@ -401,7 +390,7 @@ namespace boost thread::id get_id() { - return thread::id(detail::win32::GetCurrentThreadId()); + return thread::id(get_or_make_current_thread_data()); } void interruption_point() @@ -466,12 +455,7 @@ namespace boost { void add_thread_exit_function(thread_exit_function_base* func) { - detail::thread_data_base* current_thread_data(get_current_thread_data()); - if(!current_thread_data) - { - make_external_thread_data(); - current_thread_data=get_current_thread_data(); - } + detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); thread_exit_callback_node* const new_node= heap_new(func, current_thread_data->thread_exit_callbacks); @@ -519,12 +503,7 @@ namespace boost } else { - detail::thread_data_base* current_thread_data(get_current_thread_data()); - if(!current_thread_data) - { - make_external_thread_data(); - current_thread_data=get_current_thread_data(); - } + detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); tss_data_node* const new_node=heap_new(key,func,tss_data,current_thread_data->tss_data); current_thread_data->tss_data=new_node; } From 332dd988e461fda0b5d7c038aa3110c61979b8d6 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 23 Nov 2007 23:09:36 +0000 Subject: [PATCH 063/214] Integrate TSS with thread data; test to ensure cleanup done for native threads as well as boost::thread-launched threads now runs for pthread API as well as win32 API [SVN r41320] --- build/Jamfile.v2 | 1 - include/boost/thread/pthread/thread_data.hpp | 4 +- include/boost/thread/pthread/tss.hpp | 187 +++++++++---------- src/pthread/thread.cpp | 139 +++++++++++++- src/pthread/tss.cpp | 187 ------------------- test/test_tss.cpp | 87 ++++++--- 6 files changed, 279 insertions(+), 326 deletions(-) delete mode 100644 src/pthread/tss.cpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 8abffe50..1ec9ffc6 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -190,7 +190,6 @@ alias thread_sources : ## pthread sources ## pthread/thread.cpp pthread/exceptions.cpp - pthread/tss.cpp pthread/once.cpp : ## requirements ## pthread diff --git a/include/boost/thread/pthread/thread_data.hpp b/include/boost/thread/pthread/thread_data.hpp index e055f8de..f11035f1 100644 --- a/include/boost/thread/pthread/thread_data.hpp +++ b/include/boost/thread/pthread/thread_data.hpp @@ -20,6 +20,7 @@ namespace boost namespace detail { struct thread_exit_callback_node; + struct tss_data_node; struct thread_data_base { @@ -33,13 +34,14 @@ namespace boost bool join_started; bool joined; boost::detail::thread_exit_callback_node* thread_exit_callbacks; + boost::detail::tss_data_node* tss_data; bool interrupt_enabled; bool interrupt_requested; pthread_cond_t* current_cond; thread_data_base(): done(false),join_started(false),joined(false), - thread_exit_callbacks(0), + thread_exit_callbacks(0),tss_data(0), interrupt_enabled(true), interrupt_requested(false), current_cond(0) diff --git a/include/boost/thread/pthread/tss.hpp b/include/boost/thread/pthread/tss.hpp index 462d6304..c33e6d34 100644 --- a/include/boost/thread/pthread/tss.hpp +++ b/include/boost/thread/pthread/tss.hpp @@ -1,109 +1,102 @@ #ifndef BOOST_THREAD_PTHREAD_TSS_HPP #define BOOST_THREAD_PTHREAD_TSS_HPP -// Copyright (C) 2001-2003 William E. Kempf -// Copyright (C) 2006 Roland Schwarz -// Copyright (C) 2007 Anthony Williams -// -// 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) +// 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) +// (C) Copyright 2007 Anthony Williams -#include -#include -#include -#include +#include -#include - -namespace boost { - -// disable warnings about non dll import -// see: http://www.boost.org/more/separate_compilation.html#dlls -#ifdef BOOST_MSVC -# pragma warning(push) -# pragma warning(disable: 4251 4231 4660 4275) -#endif - -namespace detail { - -class BOOST_THREAD_DECL tss : private noncopyable +namespace boost { -public: - tss(boost::function1* pcleanup) { - if (pcleanup == 0) throw boost::thread_resource_error(); - try + namespace detail + { + struct tss_cleanup_function { - init(pcleanup); - } - catch (...) + virtual ~tss_cleanup_function() + {} + + virtual void operator()(void* data)=0; + }; + + BOOST_THREAD_DECL void set_tss_data(void const* key,boost::shared_ptr func,void* tss_data,bool cleanup_existing); + BOOST_THREAD_DECL void* get_tss_data(void const* key); + } + + template + class thread_specific_ptr + { + private: + thread_specific_ptr(thread_specific_ptr&); + thread_specific_ptr& operator=(thread_specific_ptr&); + + struct delete_data: + detail::tss_cleanup_function { - delete pcleanup; - throw boost::thread_resource_error(); + void operator()(void* data) + { + delete static_cast(data); + } + }; + + struct run_custom_cleanup_function: + detail::tss_cleanup_function + { + void (*cleanup_function)(T*); + + explicit run_custom_cleanup_function(void (*cleanup_function_)(T*)): + cleanup_function(cleanup_function_) + {} + + void operator()(void* data) + { + cleanup_function(data); + } + }; + + + boost::shared_ptr cleanup; + + public: + thread_specific_ptr(): + cleanup(new delete_data) + {} + explicit thread_specific_ptr(void (*func_)(T*)): + cleanup(new run_custom_cleanup_function(func_)) + {} + ~thread_specific_ptr() + { + reset(); } - } - - ~tss(); - void* get() const; - void set(void* value); - void cleanup(void* p); - -private: - unsigned int m_slot; //This is a "pseudo-slot", not a native slot - - void init(boost::function1* pcleanup); -}; - -template -struct tss_adapter -{ - template - tss_adapter(const F& cleanup) : m_cleanup(cleanup) { } - void operator()(void* p) { m_cleanup(static_cast(p)); } - boost::function1 m_cleanup; -}; - -} // namespace detail - -template -class thread_specific_ptr : private noncopyable -{ -public: - thread_specific_ptr() - : m_tss(new boost::function1( - boost::detail::tss_adapter( - &thread_specific_ptr::cleanup))) - { - } - thread_specific_ptr(void (*clean)(T*)) - : m_tss(new boost::function1( - boost::detail::tss_adapter(clean))) - { - } - ~thread_specific_ptr() { reset(); } - - T* get() const { return static_cast(m_tss.get()); } - T* operator->() const { return get(); } - T& operator*() const { return *get(); } - T* release() { T* temp = get(); if (temp) m_tss.set(0); return temp; } - void reset(T* p=0) - { - T* cur = get(); - if (cur == p) return; - m_tss.set(p); - if (cur) m_tss.cleanup(cur); - } - -private: - static void cleanup(T* p) { delete p; } - detail::tss m_tss; -}; - -#ifdef BOOST_MSVC -# pragma warning(pop) -#endif - -} // namespace boost - + T* get() const + { + return static_cast(detail::get_tss_data(this)); + } + T* operator->() const + { + return get(); + } + T& operator*() const + { + return *get(); + } + T* release() + { + T* const temp=get(); + detail::set_tss_data(this,0,0,false); + return temp; + } + void reset(T* new_value=0) + { + T* const current_value=get(); + if(current_value!=new_value) + { + detail::set_tss_data(this,cleanup,new_value,true); + } + } + }; +} #endif diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 1d8370de..8a074db4 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "timeconv.inl" @@ -23,6 +24,24 @@ namespace boost { boost::detail::thread_exit_function_base* func; thread_exit_callback_node* next; + + thread_exit_callback_node(boost::detail::thread_exit_function_base* func_, + thread_exit_callback_node* next_): + func(func_),next(next_) + {} + }; + + struct tss_data_node + { + void const* key; + boost::shared_ptr func; + void* value; + tss_data_node* next; + + tss_data_node(void const* key_,boost::shared_ptr func_,void* value_, + tss_data_node* next_): + key(key_),func(func_),value(value_),next(next_) + {} }; namespace @@ -37,17 +56,31 @@ namespace boost boost::detail::thread_data_base* thread_info=static_cast(data); if(thread_info) { - while(thread_info->thread_exit_callbacks) + while(thread_info->tss_data || thread_info->thread_exit_callbacks) { - boost::detail::thread_exit_callback_node* const current_node=thread_info->thread_exit_callbacks; - thread_info->thread_exit_callbacks=current_node->next; - if(current_node->func) + while(thread_info->thread_exit_callbacks) { - (*current_node->func)(); - delete current_node->func; + detail::thread_exit_callback_node* const current_node=thread_info->thread_exit_callbacks; + thread_info->thread_exit_callbacks=current_node->next; + if(current_node->func) + { + (*current_node->func)(); + delete current_node->func; + } + delete current_node; + } + while(thread_info->tss_data) + { + detail::tss_data_node* const current_node=thread_info->tss_data; + thread_info->tss_data=current_node->next; + if(current_node->func) + { + (*current_node->func)(current_node->value); + } + delete current_node; } - delete current_node; } + thread_info->self.reset(); } } } @@ -55,7 +88,7 @@ namespace boost void create_current_thread_tls_key() { - BOOST_VERIFY(!pthread_key_create(¤t_thread_tls_key,NULL)); + BOOST_VERIFY(!pthread_key_create(¤t_thread_tls_key,&tls_destructor)); } } @@ -100,8 +133,39 @@ namespace boost thread_info->done_condition.notify_all(); return 0; } - } + + struct externally_launched_thread: + detail::thread_data_base + { + externally_launched_thread() + { + interrupt_enabled=false; + } + + void run() + {} + }; + + detail::thread_data_base* make_external_thread_data() + { + detail::thread_data_base* const me(new externally_launched_thread()); + me->self.reset(me); + set_current_thread_data(me); + return me; + } + + + detail::thread_data_base* get_or_make_current_thread_data() + { + detail::thread_data_base* current_thread_data(detail::get_current_thread_data()); + if(!current_thread_data) + { + current_thread_data=make_external_thread_data(); + } + return current_thread_data; + } + } @@ -428,6 +492,63 @@ namespace boost } } + namespace detail + { + void add_thread_exit_function(thread_exit_function_base* func) + { + detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); + thread_exit_callback_node* const new_node= + new thread_exit_callback_node(func,current_thread_data->thread_exit_callbacks); + current_thread_data->thread_exit_callbacks=new_node; + } + + tss_data_node* find_tss_data(void const* key) + { + detail::thread_data_base* const current_thread_data(get_current_thread_data()); + if(current_thread_data) + { + detail::tss_data_node* current_node=current_thread_data->tss_data; + while(current_node) + { + if(current_node->key==key) + { + return current_node; + } + current_node=current_node->next; + } + } + return NULL; + } + + void* get_tss_data(void const* key) + { + if(tss_data_node* const current_node=find_tss_data(key)) + { + return current_node->value; + } + return NULL; + } + + void set_tss_data(void const* key,boost::shared_ptr func,void* tss_data,bool cleanup_existing) + { + if(tss_data_node* const current_node=find_tss_data(key)) + { + if(cleanup_existing && current_node->func) + { + (*current_node->func)(current_node->value); + } + current_node->func=func; + current_node->value=tss_data; + } + else + { + detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); + tss_data_node* const new_node=new tss_data_node(key,func,tss_data,current_thread_data->tss_data); + current_thread_data->tss_data=new_node; + } + } + } + thread_group::thread_group() { } diff --git a/src/pthread/tss.cpp b/src/pthread/tss.cpp deleted file mode 100644 index 2f3a4903..00000000 --- a/src/pthread/tss.cpp +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (C) 2001-2003 William E. Kempf -// Copyright (C) 2006 Roland Schwarz -// -// 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) - -#include - -#include -#ifndef BOOST_THREAD_NO_TSS_CLEANUP - -#include -#include -#include -#include -#include -#include -#include - -namespace { - -typedef std::vector tss_slots; -typedef std::vector*> tss_data_cleanup_handlers_type; - -boost::once_flag tss_data_once = BOOST_ONCE_INIT; -boost::mutex* tss_data_mutex = 0; -tss_data_cleanup_handlers_type* tss_data_cleanup_handlers = 0; - pthread_key_t tss_data_native_key; -int tss_data_use = 0; - -void tss_data_inc_use(boost::mutex::scoped_lock& lk) -{ - ++tss_data_use; -} - -void tss_data_dec_use(boost::mutex::scoped_lock& lk) -{ - if (0 == --tss_data_use) - { - tss_data_cleanup_handlers_type::size_type i; - for (i = 0; i < tss_data_cleanup_handlers->size(); ++i) - { - delete (*tss_data_cleanup_handlers)[i]; - } - delete tss_data_cleanup_handlers; - tss_data_cleanup_handlers = 0; - lk.unlock(); - delete tss_data_mutex; - tss_data_mutex = 0; - pthread_key_delete(tss_data_native_key); - } -} - -extern "C" void cleanup_slots(void* p) -{ - tss_slots* slots = static_cast(p); - boost::mutex::scoped_lock lock(*tss_data_mutex); - for (tss_slots::size_type i = 0; i < slots->size(); ++i) - { - (*(*tss_data_cleanup_handlers)[i])((*slots)[i]); - (*slots)[i] = 0; - } - tss_data_dec_use(lock); - delete slots; -} - -void init_tss_data() -{ - std::auto_ptr - temp(new tss_data_cleanup_handlers_type); - - std::auto_ptr temp_mutex(new boost::mutex); - if (temp_mutex.get() == 0) - throw boost::thread_resource_error(); - - int res = pthread_key_create(&tss_data_native_key, &cleanup_slots); - if (res != 0) - return; - - // The life time of cleanup handlers and mutex is beeing - // managed by a reference counting technique. - // This avoids a memory leak by releasing the global data - // after last use only, since the execution order of cleanup - // handlers is unspecified on any platform with regards to - // C++ destructor ordering rules. - tss_data_cleanup_handlers = temp.release(); - tss_data_mutex = temp_mutex.release(); -} - - -tss_slots* get_slots(bool alloc) -{ - tss_slots* slots = 0; - - slots = static_cast( - pthread_getspecific(tss_data_native_key)); - - if (slots == 0 && alloc) - { - std::auto_ptr temp(new tss_slots); - - if (pthread_setspecific(tss_data_native_key, temp.get()) != 0) - return 0; - { - boost::mutex::scoped_lock lock(*tss_data_mutex); - tss_data_inc_use(lock); - } - slots = temp.release(); - } - - return slots; -} - -} // namespace - -namespace boost { - -namespace detail { -void tss::init(boost::function1* pcleanup) -{ - boost::call_once(tss_data_once, &init_tss_data); - if (tss_data_cleanup_handlers == 0) - throw thread_resource_error(); - boost::mutex::scoped_lock lock(*tss_data_mutex); - try - { - tss_data_cleanup_handlers->push_back(pcleanup); - m_slot = tss_data_cleanup_handlers->size() - 1; - tss_data_inc_use(lock); - } - catch (...) - { - throw thread_resource_error(); - } -} - -tss::~tss() -{ - boost::mutex::scoped_lock lock(*tss_data_mutex); - tss_data_dec_use(lock); -} - -void* tss::get() const -{ - tss_slots* slots = get_slots(false); - - if (!slots) - return 0; - - if (m_slot >= slots->size()) - return 0; - - return (*slots)[m_slot]; -} - -void tss::set(void* value) -{ - tss_slots* slots = get_slots(true); - - if (!slots) - throw boost::thread_resource_error(); - - if (m_slot >= slots->size()) - { - try - { - slots->resize(m_slot + 1); - } - catch (...) - { - throw boost::thread_resource_error(); - } - } - - (*slots)[m_slot] = value; -} - -void tss::cleanup(void* value) -{ - boost::mutex::scoped_lock lock(*tss_data_mutex); - (*(*tss_data_cleanup_handlers)[m_slot])(value); -} - -} // namespace detail -} // namespace boost - -#endif //BOOST_THREAD_NO_TSS_CLEANUP diff --git a/test/test_tss.cpp b/test/test_tss.cpp index 7c166648..d53c1d34 100644 --- a/test/test_tss.cpp +++ b/test/test_tss.cpp @@ -62,7 +62,7 @@ void test_tss_thread() } } -#if defined(BOOST_HAS_WINTHREADS) +#if defined(BOOST_THREAD_PLATFORM_WIN32) typedef HANDLE native_thread_t; DWORD WINAPI test_tss_thread_native(LPVOID lpParameter) @@ -91,6 +91,33 @@ void test_tss_thread() res = CloseHandle(thread); BOOST_CHECK(SUCCEEDED(res)); } +#elif defined(BOOST_THREAD_PLATFORM_PTHREAD) + typedef pthread_t native_thread_t; + +extern "C" +{ + void* test_tss_thread_native(void* lpParameter) + { + test_tss_thread(); + return 0; + } +} + + native_thread_t create_native_thread() + { + native_thread_t thread_handle; + + int const res = pthread_create(&thread_handle, 0, &test_tss_thread_native, 0); + BOOST_CHECK(!res); + return thread_handle; + } + + void join_native_thread(native_thread_t thread) + { + void* result=0; + int const res=pthread_join(thread,&result); + BOOST_CHECK(!res); + } #endif void do_test_tss() @@ -123,44 +150,42 @@ void do_test_tss() BOOST_CHECK_EQUAL(tss_instances, 0); BOOST_CHECK_EQUAL(tss_total, 5); - #if defined(BOOST_HAS_WINTHREADS) - tss_instances = 0; - tss_total = 0; + tss_instances = 0; + tss_total = 0; - native_thread_t thread1 = create_native_thread(); - BOOST_CHECK(thread1 != 0); + native_thread_t thread1 = create_native_thread(); + BOOST_CHECK(thread1 != 0); - native_thread_t thread2 = create_native_thread(); - BOOST_CHECK(thread2 != 0); + native_thread_t thread2 = create_native_thread(); + BOOST_CHECK(thread2 != 0); - native_thread_t thread3 = create_native_thread(); - BOOST_CHECK(thread3 != 0); + native_thread_t thread3 = create_native_thread(); + BOOST_CHECK(thread3 != 0); - native_thread_t thread4 = create_native_thread(); - BOOST_CHECK(thread3 != 0); + native_thread_t thread4 = create_native_thread(); + BOOST_CHECK(thread3 != 0); - native_thread_t thread5 = create_native_thread(); - BOOST_CHECK(thread3 != 0); + native_thread_t thread5 = create_native_thread(); + BOOST_CHECK(thread3 != 0); - join_native_thread(thread5); - join_native_thread(thread4); - join_native_thread(thread3); - join_native_thread(thread2); - join_native_thread(thread1); + join_native_thread(thread5); + join_native_thread(thread4); + join_native_thread(thread3); + join_native_thread(thread2); + join_native_thread(thread1); - std::cout - << "tss_instances = " << tss_instances - << "; tss_total = " << tss_total - << "\n"; - std::cout.flush(); + std::cout + << "tss_instances = " << tss_instances + << "; tss_total = " << tss_total + << "\n"; + std::cout.flush(); - // The following is not really an error. TSS cleanup support still is available for boost threads. - // Also this usually will be triggered only when bound to the static version of thread lib. - // 2006-10-02 Roland Schwarz - //BOOST_CHECK_EQUAL(tss_instances, 0); - BOOST_CHECK_MESSAGE(tss_instances == 0, "Support of automatic tss cleanup for native threading API not available"); - BOOST_CHECK_EQUAL(tss_total, 5); - #endif + // The following is not really an error. TSS cleanup support still is available for boost threads. + // Also this usually will be triggered only when bound to the static version of thread lib. + // 2006-10-02 Roland Schwarz + //BOOST_CHECK_EQUAL(tss_instances, 0); + BOOST_CHECK_MESSAGE(tss_instances == 0, "Support of automatic tss cleanup for native threading API not available"); + BOOST_CHECK_EQUAL(tss_total, 5); } void test_tss() From 5ded171247340446b95975d229341f451dd8111e Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 26 Nov 2007 12:17:45 +0000 Subject: [PATCH 064/214] workaround for Borland compiler [SVN r41395] --- src/win32/thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index fa83b7cf..c7b40602 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -494,7 +494,7 @@ namespace boost tss_cleanup_implemented(); // if anyone uses TSS, we need the cleanup linked in if(tss_data_node* const current_node=find_tss_data(key)) { - if(cleanup_existing && current_node->func) + if(cleanup_existing && current_node->func.get()) { (*current_node->func)(current_node->value); } From 270e88edd7fe2d4f7edeef5686c32d6b3e794071 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 26 Nov 2007 13:29:15 +0000 Subject: [PATCH 065/214] Don't compare native_handle_t against 0 --- do appropriate checks in create_native_thread for platforms where pthread_t is not comparable [SVN r41396] --- test/test_tss.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/test/test_tss.cpp b/test/test_tss.cpp index d53c1d34..462c5523 100644 --- a/test/test_tss.cpp +++ b/test/test_tss.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf +// Copyright (C) 2007 Anthony Williams // // 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) @@ -73,7 +74,7 @@ void test_tss_thread() native_thread_t create_native_thread(void) { - return CreateThread( + native_thread_t const res=CreateThread( 0, //security attributes (0 = not inheritable) 0, //stack size (0 = default) &test_tss_thread_native, //function to execute @@ -81,6 +82,8 @@ void test_tss_thread() 0, //creation flags (0 = run immediately) 0 //thread id (0 = thread id not returned) ); + BOOST_CHECK(res!=0); + return res; } void join_native_thread(native_thread_t thread) @@ -154,19 +157,10 @@ void do_test_tss() tss_total = 0; native_thread_t thread1 = create_native_thread(); - BOOST_CHECK(thread1 != 0); - native_thread_t thread2 = create_native_thread(); - BOOST_CHECK(thread2 != 0); - native_thread_t thread3 = create_native_thread(); - BOOST_CHECK(thread3 != 0); - native_thread_t thread4 = create_native_thread(); - BOOST_CHECK(thread3 != 0); - native_thread_t thread5 = create_native_thread(); - BOOST_CHECK(thread3 != 0); join_native_thread(thread5); join_native_thread(thread4); From 4f35e256880aac0c7550b61ee3bd6c304a5d0964 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 26 Nov 2007 15:44:07 +0000 Subject: [PATCH 066/214] fixed import/export declarations so new once code works with pthread-win32 [SVN r41398] --- build/Jamfile.v2 | 5 ++++- include/boost/thread/detail/config.hpp | 5 ++--- include/boost/thread/pthread/condition_variable.hpp | 6 ++++++ .../boost/thread/pthread/condition_variable_fwd.hpp | 6 ++++++ include/boost/thread/pthread/once.hpp | 6 +++--- include/boost/thread/win32/condition_variable.hpp | 11 +++++++++++ src/pthread/once.cpp | 7 +++---- test/test_once.cpp | 1 + 8 files changed, 36 insertions(+), 11 deletions(-) diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 1ec9ffc6..50168a91 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -157,6 +157,7 @@ rule usage-requirements ( properties * ) rule requirements ( properties * ) { local result ; + if pthread in $(properties) { result += BOOST_THREAD_POSIX ; @@ -201,5 +202,7 @@ lib boost_thread : thread_sources : @requirements : - : @usage-requirements + : shared:BOOST_THREAD_USE_DLL=1 + static:BOOST_THREAD_USE_LIB=1 + @usage-requirements ; diff --git a/include/boost/thread/detail/config.hpp b/include/boost/thread/detail/config.hpp index 94fb6b88..7661507f 100644 --- a/include/boost/thread/detail/config.hpp +++ b/include/boost/thread/detail/config.hpp @@ -17,8 +17,7 @@ # pragma warn -8066 // Unreachable code #endif -// insist on threading support being available: -#include +#include "platform.hpp" // compatibility with the rest of Boost's auto-linking code: #if defined(BOOST_THREAD_DYN_DLL) || defined(BOOST_ALL_DYN_LINK) @@ -31,7 +30,7 @@ #elif defined(BOOST_THREAD_USE_DLL) //Use dll #elif defined(BOOST_THREAD_USE_LIB) //Use lib #else //Use default -# if defined(BOOST_HAS_WINTHREADS) +# if defined(BOOST_THREAD_PLATFORM_WIN32) # if defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN) //For compilers supporting auto-tss cleanup //with Boost.Threads lib, use Boost.Threads lib diff --git a/include/boost/thread/pthread/condition_variable.hpp b/include/boost/thread/pthread/condition_variable.hpp index 7306ae3e..7c467f65 100644 --- a/include/boost/thread/pthread/condition_variable.hpp +++ b/include/boost/thread/pthread/condition_variable.hpp @@ -150,6 +150,12 @@ namespace boost return true; } + template + bool timed_wait(lock_type& m,duration_type const& wait_duration,predicate_type pred) + { + return timed_wait(m,get_system_time()+wait_duration,pred); + } + void notify_one() { boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); diff --git a/include/boost/thread/pthread/condition_variable_fwd.hpp b/include/boost/thread/pthread/condition_variable_fwd.hpp index d9d589db..29b5a128 100644 --- a/include/boost/thread/pthread/condition_variable_fwd.hpp +++ b/include/boost/thread/pthread/condition_variable_fwd.hpp @@ -44,6 +44,12 @@ namespace boost return true; } + template + bool timed_wait(unique_lock& m,duration_type const& wait_duration,predicate_type pred) + { + return timed_wait(m,get_system_time()+wait_duration,pred); + } + void notify_one(); void notify_all(); }; diff --git a/include/boost/thread/pthread/once.hpp b/include/boost/thread/pthread/once.hpp index 90dc0c70..de0dc7a5 100644 --- a/include/boost/thread/pthread/once.hpp +++ b/include/boost/thread/pthread/once.hpp @@ -27,9 +27,9 @@ namespace boost { namespace detail { BOOST_THREAD_DECL boost::uintmax_t& get_once_per_thread_epoch(); - extern BOOST_THREAD_DECL boost::uintmax_t once_global_epoch; - extern BOOST_THREAD_DECL pthread_mutex_t once_epoch_mutex; - extern BOOST_THREAD_DECL pthread_cond_t once_epoch_cv; + BOOST_THREAD_DECL extern boost::uintmax_t once_global_epoch; + BOOST_THREAD_DECL extern pthread_mutex_t once_epoch_mutex; + BOOST_THREAD_DECL extern pthread_cond_t once_epoch_cv; } #define BOOST_ONCE_INITIAL_FLAG_VALUE -1 diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index c3861dd9..66c45ab1 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -263,6 +263,11 @@ namespace boost } return true; } + template + bool timed_wait(unique_lock& m,duration_type const& wait_duration,predicate_type pred) + { + return timed_wait(m,get_system_time()+wait_duration,pred); + } }; class condition_variable_any: @@ -297,6 +302,12 @@ namespace boost } return true; } + + template + bool timed_wait(lock_type& m,duration_type const& wait_duration,predicate_type pred) + { + return timed_wait(m,get_system_time()+wait_duration,pred); + } }; } diff --git a/src/pthread/once.cpp b/src/pthread/once.cpp index b893a0b5..6203def2 100755 --- a/src/pthread/once.cpp +++ b/src/pthread/once.cpp @@ -12,9 +12,9 @@ namespace boost { namespace detail { - boost::uintmax_t once_global_epoch=0; - pthread_mutex_t once_epoch_mutex=PTHREAD_MUTEX_INITIALIZER; - pthread_cond_t once_epoch_cv = PTHREAD_COND_INITIALIZER; + BOOST_THREAD_DECL boost::uintmax_t once_global_epoch=0; + BOOST_THREAD_DECL pthread_mutex_t once_epoch_mutex=PTHREAD_MUTEX_INITIALIZER; + BOOST_THREAD_DECL pthread_cond_t once_epoch_cv = PTHREAD_COND_INITIALIZER; namespace { @@ -45,7 +45,6 @@ namespace boost } return *static_cast(data); } - } } diff --git a/test/test_once.cpp b/test/test_once.cpp index 634b4fd7..f0e2c1e5 100644 --- a/test/test_once.cpp +++ b/test/test_once.cpp @@ -19,6 +19,7 @@ void initialize_variable() ++var_to_init; } + void call_once_thread() { unsigned const loop_count=100; From 40f3b1b4c8541b0e9ab47c0b4dc16f60472a9354 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 26 Nov 2007 17:01:08 +0000 Subject: [PATCH 067/214] once_flag uses zero-initialization on POSIX as well as windows [SVN r41401] --- include/boost/thread/pthread/once.hpp | 12 ++++++------ src/pthread/once.cpp | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/boost/thread/pthread/once.hpp b/include/boost/thread/pthread/once.hpp index de0dc7a5..f342ed1d 100644 --- a/include/boost/thread/pthread/once.hpp +++ b/include/boost/thread/pthread/once.hpp @@ -14,8 +14,8 @@ #include #include #include "pthread_mutex_scoped_lock.hpp" -#include #include +#include namespace boost { @@ -32,7 +32,7 @@ namespace boost { BOOST_THREAD_DECL extern pthread_cond_t once_epoch_cv; } -#define BOOST_ONCE_INITIAL_FLAG_VALUE -1 +#define BOOST_ONCE_INITIAL_FLAG_VALUE 0 #define BOOST_ONCE_INIT {BOOST_ONCE_INITIAL_FLAG_VALUE} @@ -42,15 +42,15 @@ namespace boost { void call_once(once_flag& flag,Function f) { static boost::uintmax_t const uninitialized_flag=BOOST_ONCE_INITIAL_FLAG_VALUE; - static boost::uintmax_t const being_initialized=uninitialized_flag-1; + static boost::uintmax_t const being_initialized=uninitialized_flag+1; boost::uintmax_t const epoch=flag.epoch; boost::uintmax_t& this_thread_epoch=detail::get_once_per_thread_epoch(); - if(epoch>this_thread_epoch) + if(epoch=being_initialized) + while(flag.epoch<=being_initialized) { if(flag.epoch==uninitialized_flag) { @@ -66,7 +66,7 @@ namespace boost { BOOST_VERIFY(!pthread_cond_broadcast(&detail::once_epoch_cv)); throw; } - flag.epoch=++detail::once_global_epoch; + flag.epoch=--detail::once_global_epoch; BOOST_VERIFY(!pthread_cond_broadcast(&detail::once_epoch_cv)); } else diff --git a/src/pthread/once.cpp b/src/pthread/once.cpp index 6203def2..6e3722a8 100755 --- a/src/pthread/once.cpp +++ b/src/pthread/once.cpp @@ -3,6 +3,7 @@ // 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) +#define __STDC_CONSTANT_MACROS #include #include #include @@ -12,7 +13,7 @@ namespace boost { namespace detail { - BOOST_THREAD_DECL boost::uintmax_t once_global_epoch=0; + BOOST_THREAD_DECL boost::uintmax_t once_global_epoch=UINTMAX_C(~0); BOOST_THREAD_DECL pthread_mutex_t once_epoch_mutex=PTHREAD_MUTEX_INITIALIZER; BOOST_THREAD_DECL pthread_cond_t once_epoch_cv = PTHREAD_COND_INITIALIZER; @@ -41,7 +42,7 @@ namespace boost { data=malloc(sizeof(boost::uintmax_t)); BOOST_VERIFY(!pthread_setspecific(epoch_tss_key,data)); - *static_cast(data)=0; + *static_cast(data)=UINTMAX_C(~0); } return *static_cast(data); } From 36abb421759640ddae82e7d04fd759d6a58f39ae Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 26 Nov 2007 21:15:04 +0000 Subject: [PATCH 068/214] reverted accidental checkin of new timed_wait functions on condition_variable [SVN r41405] --- include/boost/thread/pthread/condition_variable.hpp | 6 ------ .../boost/thread/pthread/condition_variable_fwd.hpp | 6 ------ include/boost/thread/win32/condition_variable.hpp | 11 ----------- 3 files changed, 23 deletions(-) diff --git a/include/boost/thread/pthread/condition_variable.hpp b/include/boost/thread/pthread/condition_variable.hpp index 7c467f65..7306ae3e 100644 --- a/include/boost/thread/pthread/condition_variable.hpp +++ b/include/boost/thread/pthread/condition_variable.hpp @@ -150,12 +150,6 @@ namespace boost return true; } - template - bool timed_wait(lock_type& m,duration_type const& wait_duration,predicate_type pred) - { - return timed_wait(m,get_system_time()+wait_duration,pred); - } - void notify_one() { boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); diff --git a/include/boost/thread/pthread/condition_variable_fwd.hpp b/include/boost/thread/pthread/condition_variable_fwd.hpp index 29b5a128..d9d589db 100644 --- a/include/boost/thread/pthread/condition_variable_fwd.hpp +++ b/include/boost/thread/pthread/condition_variable_fwd.hpp @@ -44,12 +44,6 @@ namespace boost return true; } - template - bool timed_wait(unique_lock& m,duration_type const& wait_duration,predicate_type pred) - { - return timed_wait(m,get_system_time()+wait_duration,pred); - } - void notify_one(); void notify_all(); }; diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index 66c45ab1..c3861dd9 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -263,11 +263,6 @@ namespace boost } return true; } - template - bool timed_wait(unique_lock& m,duration_type const& wait_duration,predicate_type pred) - { - return timed_wait(m,get_system_time()+wait_duration,pred); - } }; class condition_variable_any: @@ -302,12 +297,6 @@ namespace boost } return true; } - - template - bool timed_wait(lock_type& m,duration_type const& wait_duration,predicate_type pred) - { - return timed_wait(m,get_system_time()+wait_duration,pred); - } }; } From f827709d42cc237470d6e6ced054454fb9843bc9 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 27 Nov 2007 14:24:29 +0000 Subject: [PATCH 069/214] add support for relative timeouts to condition timed_wait [SVN r41413] --- .../thread/pthread/condition_variable.hpp | 14 +- .../thread/pthread/condition_variable_fwd.hpp | 14 ++ .../boost/thread/win32/condition_variable.hpp | 141 +++++++++++++++--- .../boost/thread/win32/thread_primitives.hpp | 3 + test/test_condition.cpp | 50 ++++++- 5 files changed, 201 insertions(+), 21 deletions(-) diff --git a/include/boost/thread/pthread/condition_variable.hpp b/include/boost/thread/pthread/condition_variable.hpp index 7306ae3e..754f0ac3 100644 --- a/include/boost/thread/pthread/condition_variable.hpp +++ b/include/boost/thread/pthread/condition_variable.hpp @@ -5,11 +5,9 @@ // http://www.boost.org/LICENSE_1_0.txt) // (C) Copyright 2007 Anthony Williams -#include #include #include #include -#include #include #include "timespec.hpp" #include "pthread_mutex_scoped_lock.hpp" @@ -150,6 +148,18 @@ namespace boost return true; } + template + bool timed_wait(lock_type& m,xtime const& wait_until,predicate_type pred) + { + return timed_wait(m,system_time(wait_until),pred); + } + + template + bool timed_wait(lock_type& m,duration_type const& wait_duration,predicate_type pred) + { + return timed_wait(m,get_system_time()+wait_duration,pred); + } + void notify_one() { boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); diff --git a/include/boost/thread/pthread/condition_variable_fwd.hpp b/include/boost/thread/pthread/condition_variable_fwd.hpp index d9d589db..ca45e54b 100644 --- a/include/boost/thread/pthread/condition_variable_fwd.hpp +++ b/include/boost/thread/pthread/condition_variable_fwd.hpp @@ -6,8 +6,10 @@ // (C) Copyright 2007 Anthony Williams #include +#include #include #include +#include namespace boost { @@ -44,6 +46,18 @@ namespace boost return true; } + template + bool timed_wait(unique_lock& m,xtime const& wait_until,predicate_type pred) + { + return timed_wait(m,system_time(wait_until),pred); + } + + template + bool timed_wait(unique_lock& m,duration_type const& wait_duration,predicate_type pred) + { + return timed_wait(m,get_system_time()+wait_duration,pred); + } + void notify_one(); void notify_all(); }; diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index c3861dd9..18db1363 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -13,6 +13,8 @@ #include #include #include "interlocked_read.hpp" +#include +#include namespace boost { @@ -111,8 +113,78 @@ namespace boost protected: + struct timeout + { + unsigned long start; + uintmax_t milliseconds; + bool relative; + boost::system_time abs_time; + + static unsigned long const max_non_infinite_wait=0xfffffffe; + + timeout(uintmax_t milliseconds_): + start(win32::GetTickCount()), + milliseconds(milliseconds_), + relative(true), + abs_time(boost::get_system_time()) + {} + + timeout(boost::system_time const& abs_time_): + start(win32::GetTickCount()), + milliseconds(0), + relative(false), + abs_time(abs_time_) + {} + + struct remaining_time + { + bool more; + unsigned long milliseconds; + + remaining_time(uintmax_t remaining): + more(remaining>max_non_infinite_wait), + milliseconds(more?max_non_infinite_wait:(unsigned long)remaining) + {} + }; + + remaining_time remaining_milliseconds() const + { + if(milliseconds==~uintmax_t(0)) + { + return remaining_time(win32::infinite); + } + else if(relative) + { + unsigned long const now=win32::GetTickCount(); + unsigned long const elapsed=now-start; + return remaining_time((elapsed - bool do_wait(lock_type& lock,::boost::system_time const& wait_until) + bool do_wait(lock_type& lock,timeout wait_until) { detail::win32::handle_manager local_wake_sem; detail::win32::handle_manager sem; @@ -155,9 +227,21 @@ namespace boost ++generations[0].count; sem=detail::win32::duplicate_handle(generations[0].semaphore); } - if(!this_thread::interruptible_wait(sem,::boost::detail::get_milliseconds_until(wait_until))) + while(true) { - break; + timeout::remaining_time const remaining=wait_until.remaining_milliseconds(); + if(this_thread::interruptible_wait(sem,remaining.milliseconds)) + { + break; + } + else if(!remaining.more) + { + return false; + } + if(wait_until.relative) + { + wait_until.milliseconds-=timeout::max_non_infinite_wait; + } } unsigned long const woken_result=detail::win32::WaitForSingleObject(local_wake_sem,0); @@ -167,6 +251,17 @@ namespace boost } return woken; } + + template + bool do_wait(lock_type& m,timeout const& wait_until,predicate_type pred) + { + while (!pred()) + { + if(!do_wait(m, wait_until)) + return false; + } + return true; + } basic_condition_variable(const basic_condition_variable& other); basic_condition_variable& operator=(const basic_condition_variable& other); @@ -238,7 +333,7 @@ namespace boost public: void wait(unique_lock& m) { - do_wait(m,::boost::detail::get_system_time_sentinel()); + do_wait(m,timeout::sentinel()); } template @@ -256,12 +351,17 @@ namespace boost template bool timed_wait(unique_lock& m,boost::system_time const& wait_until,predicate_type pred) { - while (!pred()) - { - if(!timed_wait(m, wait_until)) - return false; - } - return true; + return do_wait(m,wait_until,pred); + } + template + bool timed_wait(unique_lock& m,boost::xtime const& wait_until,predicate_type pred) + { + return do_wait(m,system_time(wait_until),pred); + } + template + bool timed_wait(unique_lock& m,duration_type const& wait_duration,predicate_type pred) + { + return do_wait(m,wait_duration.total_milliseconds(),pred); } }; @@ -272,7 +372,7 @@ namespace boost template void wait(lock_type& m) { - do_wait(m,::boost::detail::get_system_time_sentinel()); + do_wait(m,timeout::sentinel()); } template @@ -290,12 +390,19 @@ namespace boost template bool timed_wait(lock_type& m,boost::system_time const& wait_until,predicate_type pred) { - while (!pred()) - { - if(!timed_wait(m, wait_until)) - return false; - } - return true; + return do_wait(m,wait_until,pred); + } + + template + bool timed_wait(lock_type& m,boost::xtime const& wait_until,predicate_type pred) + { + return do_wait(m,system_time(wait_until),pred); + } + + template + bool timed_wait(lock_type& m,duration_type const& wait_duration,predicate_type pred) + { + return timed_wait(m,wait_duration.total_milliseconds(),pred); } }; diff --git a/include/boost/thread/win32/thread_primitives.hpp b/include/boost/thread/win32/thread_primitives.hpp index 8f6bc0ed..c8d55d29 100644 --- a/include/boost/thread/win32/thread_primitives.hpp +++ b/include/boost/thread/win32/thread_primitives.hpp @@ -53,6 +53,7 @@ namespace boost using ::SleepEx; using ::Sleep; using ::QueueUserAPC; + using ::GetTickCount; } } } @@ -120,6 +121,8 @@ namespace boost typedef void (__stdcall *queue_user_apc_callback_function)(ulong_ptr); __declspec(dllimport) unsigned long __stdcall QueueUserAPC(queue_user_apc_callback_function,void*,ulong_ptr); + __declspec(dllimport) unsigned long __stdcall GetTickCount(); + # ifndef UNDER_CE __declspec(dllimport) unsigned long __stdcall GetCurrentProcessId(); __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(); diff --git a/test/test_condition.cpp b/test/test_condition.cpp index 2dbe32d0..17182e9f 100644 --- a/test/test_condition.cpp +++ b/test/test_condition.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf +// Copyright (C) 2007 Anthony Williams // // 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) @@ -19,7 +20,7 @@ struct condition_test_data condition_test_data() : notified(0), awoken(0) { } boost::mutex mutex; - boost::condition condition; + boost::condition_variable condition; int notified; int awoken; }; @@ -82,6 +83,15 @@ void condition_test_waits(condition_test_data* data) BOOST_CHECK_EQUAL(data->notified, 4); data->awoken++; data->condition.notify_one(); + + // Test predicate timed_wait with relative timeout + cond_predicate pred_rel(data->notified, 5); + BOOST_CHECK(data->condition.timed_wait(lock, boost::posix_time::seconds(10), pred_rel)); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK(pred_rel()); + BOOST_CHECK_EQUAL(data->notified, 5); + data->awoken++; + data->condition.notify_one(); } void do_test_condition_notify_one() @@ -185,10 +195,19 @@ void do_test_condition_waits() data.condition.wait(lock); BOOST_CHECK(lock ? true : false); BOOST_CHECK_EQUAL(data.awoken, 4); + + + boost::thread::sleep(delay(1)); + data.notified++; + data.condition.notify_one(); + while (data.awoken != 5) + data.condition.wait(lock); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data.awoken, 5); } thread.join(); - BOOST_CHECK_EQUAL(data.awoken, 4); + BOOST_CHECK_EQUAL(data.awoken, 5); } void test_condition_waits() @@ -216,6 +235,32 @@ void test_condition_wait_is_a_interruption_point() timed_test(&do_test_condition_wait_is_a_interruption_point, 1); } +bool fake_predicate() +{ + return false; +} + + +void do_test_timed_wait_times_out() +{ + boost::condition_variable cond; + boost::mutex m; + + boost::posix_time::seconds const delay(5); + boost::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + bool const res=cond.timed_wait(lock,delay,fake_predicate); + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK(!res); + BOOST_CHECK((delay-boost::posix_time::milliseconds(10))<=(end-start)); +} + + +void test_timed_wait_times_out() +{ + timed_test(&do_test_timed_wait_times_out, 15); +} + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { @@ -226,6 +271,7 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) test->add(BOOST_TEST_CASE(&test_condition_notify_all)); test->add(BOOST_TEST_CASE(&test_condition_waits)); test->add(BOOST_TEST_CASE(&test_condition_wait_is_a_interruption_point)); + test->add(BOOST_TEST_CASE(&test_timed_wait_times_out)); return test; } From b50a7ccb61ee08493e9740df07d8a29675ab2199 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 30 Nov 2007 18:38:21 +0000 Subject: [PATCH 070/214] interruptible_wait (and hence condition timed_wait) now uses a WaitableTimer where possible, to be robust in the face of clock changes [SVN r41505] --- .../boost/thread/win32/condition_variable.hpp | 158 +++++---------- include/boost/thread/win32/shared_mutex.hpp | 8 +- include/boost/thread/win32/thread.hpp | 79 +++++++- src/win32/thread.cpp | 135 +++++++++++-- src/win32/tss_pe.cpp | 3 - test/Jamfile.v2 | 4 + test/condition_test_common.hpp | 95 +++++++++ test/test_barrier.cpp | 2 +- test/test_condition.cpp | 90 --------- test/test_condition_notify_all.cpp | 180 ++++++++++++++++++ test/test_condition_notify_one.cpp | 113 +++++++++++ test/test_condition_timed_wait_times_out.cpp | 89 +++++++++ test/test_shared_mutex.cpp | 2 +- test/util.inl | 21 ++ 14 files changed, 748 insertions(+), 231 deletions(-) create mode 100644 test/condition_test_common.hpp create mode 100644 test/test_condition_notify_all.cpp create mode 100644 test/test_condition_notify_one.cpp create mode 100644 test/test_condition_timed_wait_times_out.cpp diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index 18db1363..ccf29b22 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -13,7 +13,6 @@ #include #include #include "interlocked_read.hpp" -#include #include namespace boost @@ -112,136 +111,65 @@ namespace boost }; - protected: - struct timeout + template + void start_wait_loop_first_time(relocker& locker, + detail::win32::handle_manager& local_wake_sem) { - unsigned long start; - uintmax_t milliseconds; - bool relative; - boost::system_time abs_time; - - static unsigned long const max_non_infinite_wait=0xfffffffe; - - timeout(uintmax_t milliseconds_): - start(win32::GetTickCount()), - milliseconds(milliseconds_), - relative(true), - abs_time(boost::get_system_time()) - {} - - timeout(boost::system_time const& abs_time_): - start(win32::GetTickCount()), - milliseconds(0), - relative(false), - abs_time(abs_time_) - {} - - struct remaining_time + locker.unlock(); + if(!wake_sem) { - bool more; - unsigned long milliseconds; - - remaining_time(uintmax_t remaining): - more(remaining>max_non_infinite_wait), - milliseconds(more?max_non_infinite_wait:(unsigned long)remaining) - {} - }; - - remaining_time remaining_milliseconds() const - { - if(milliseconds==~uintmax_t(0)) - { - return remaining_time(win32::infinite); - } - else if(relative) - { - unsigned long const now=win32::GetTickCount(); - unsigned long const elapsed=now-start; - return remaining_time((elapsed + void start_wait_loop(relocker& locker, + detail::win32::handle_manager& local_wake_sem, + detail::win32::handle_manager& sem) + { + boost::mutex::scoped_lock internal_lock(internal_mutex); + detail::interlocked_write_release(&total_count,total_count+1); + if(!local_wake_sem) + { + start_wait_loop_first_time(locker,local_wake_sem); + } + if(!generations[0].semaphore) + { + generations[0].semaphore=detail::win32::create_anonymous_semaphore(0,LONG_MAX); + BOOST_ASSERT(generations[0].semaphore); + } + ++generations[0].count; + sem=detail::win32::duplicate_handle(generations[0].semaphore); + } + protected: template bool do_wait(lock_type& lock,timeout wait_until) { detail::win32::handle_manager local_wake_sem; detail::win32::handle_manager sem; - bool first_loop=true; bool woken=false; relocker locker(lock); while(!woken) { + start_wait_loop(locker,local_wake_sem,sem); + + if(!this_thread::interruptible_wait(sem,wait_until)) { - boost::mutex::scoped_lock internal_lock(internal_mutex); - detail::interlocked_write_release(&total_count,total_count+1); - if(first_loop) - { - locker.unlock(); - if(!wake_sem) - { - wake_sem=detail::win32::create_anonymous_semaphore(0,LONG_MAX); - BOOST_ASSERT(wake_sem); - } - local_wake_sem=detail::win32::duplicate_handle(wake_sem); - - if(generations[0].notified) - { - shift_generations_down(); - } - else if(!active_generation_count) - { - active_generation_count=1; - } - - first_loop=false; - } - if(!generations[0].semaphore) - { - generations[0].semaphore=detail::win32::create_anonymous_semaphore(0,LONG_MAX); - BOOST_ASSERT(generations[0].semaphore); - } - ++generations[0].count; - sem=detail::win32::duplicate_handle(generations[0].semaphore); - } - while(true) - { - timeout::remaining_time const remaining=wait_until.remaining_milliseconds(); - if(this_thread::interruptible_wait(sem,remaining.milliseconds)) - { - break; - } - else if(!remaining.more) - { - return false; - } - if(wait_until.relative) - { - wait_until.milliseconds-=timeout::max_non_infinite_wait; - } + return false; } unsigned long const woken_result=detail::win32::WaitForSingleObject(local_wake_sem,0); @@ -333,7 +261,7 @@ namespace boost public: void wait(unique_lock& m) { - do_wait(m,timeout::sentinel()); + do_wait(m,detail::timeout::sentinel()); } template @@ -372,7 +300,7 @@ namespace boost template void wait(lock_type& m) { - do_wait(m,timeout::sentinel()); + do_wait(m,detail::timeout::sentinel()); } template diff --git a/include/boost/thread/win32/shared_mutex.hpp b/include/boost/thread/win32/shared_mutex.hpp index 992491d5..73cdc490 100644 --- a/include/boost/thread/win32/shared_mutex.hpp +++ b/include/boost/thread/win32/shared_mutex.hpp @@ -48,10 +48,10 @@ namespace boost } state_data state; - void* semaphores[2]; - void* &unlock_sem; - void* &exclusive_sem; - void* upgrade_sem; + detail::win32::handle semaphores[2]; + detail::win32::handle &unlock_sem; + detail::win32::handle &exclusive_sem; + detail::win32::handle upgrade_sem; void release_waiters(state_data old_state) { diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 16315b00..93a44792 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace boost { @@ -73,6 +74,82 @@ namespace boost }; typedef boost::intrusive_ptr thread_data_ptr; + + struct timeout + { + unsigned long start; + uintmax_t milliseconds; + bool relative; + boost::system_time abs_time; + + static unsigned long const max_non_infinite_wait=0xfffffffe; + + timeout(uintmax_t milliseconds_): + start(win32::GetTickCount()), + milliseconds(milliseconds_), + relative(true), + abs_time(boost::get_system_time()) + {} + + timeout(boost::system_time const& abs_time_): + start(win32::GetTickCount()), + milliseconds(0), + relative(false), + abs_time(abs_time_) + {} + + struct remaining_time + { + bool more; + unsigned long milliseconds; + + remaining_time(uintmax_t remaining): + more(remaining>max_non_infinite_wait), + milliseconds(more?max_non_infinite_wait:(unsigned long)remaining) + {} + }; + + remaining_time remaining_milliseconds() const + { + if(is_sentinel()) + { + return remaining_time(win32::infinite); + } + else if(relative) + { + unsigned long const now=win32::GetTickCount(); + unsigned long const elapsed=now-start; + return remaining_time((elapsed #include #include @@ -15,6 +18,7 @@ #include #include #include +#include namespace boost { @@ -63,11 +67,11 @@ namespace boost typedef void* uintptr_t; inline uintptr_t const _beginthreadex(void* security, unsigned stack_size, unsigned (__stdcall* start_address)(void*), - void* arglist, unsigned initflag, unsigned* thrdaddr) + void* arglist, unsigned initflag, unsigned* thrdaddr) { DWORD threadID; HANDLE hthread=CreateThread(static_cast(security),stack_size,ThreadProxy, - new ThreadProxyData(start_address,arglist),initflag,&threadID); + new ThreadProxyData(start_address,arglist),initflag,&threadID); if (hthread!=0) *thrdaddr=threadID; return reinterpret_cast(hthread); @@ -287,7 +291,7 @@ namespace boost detail::thread_data_ptr local_thread_info=get_thread_info(); if(local_thread_info) { - this_thread::interruptible_wait(local_thread_info->thread_handle,detail::win32::infinite); + this_thread::interruptible_wait(local_thread_info->thread_handle,detail::timeout::sentinel()); release_handle(); } } @@ -353,13 +357,57 @@ namespace boost namespace this_thread { - bool interruptible_wait(detail::win32::handle handle_to_wait_for,unsigned long milliseconds) + namespace { - detail::win32::handle handles[2]={0}; + LARGE_INTEGER get_due_time(detail::timeout const& target_time) + { + LARGE_INTEGER due_time={0}; + if(target_time.relative) + { + unsigned long const elapsed_milliseconds=GetTickCount()-target_time.start; + LONGLONG const remaining_milliseconds=(target_time.milliseconds-elapsed_milliseconds); + LONGLONG const hundred_nanoseconds_in_one_millisecond=10000; + + if(remaining_milliseconds>0) + { + due_time.QuadPart=-(remaining_milliseconds*hundred_nanoseconds_in_one_millisecond); + } + } + else + { + SYSTEMTIME target_system_time={0}; + target_system_time.wYear=target_time.abs_time.date().year(); + target_system_time.wMonth=target_time.abs_time.date().month(); + target_system_time.wDay=target_time.abs_time.date().day(); + target_system_time.wHour=target_time.abs_time.time_of_day().hours(); + target_system_time.wMinute=target_time.abs_time.time_of_day().minutes(); + target_system_time.wSecond=target_time.abs_time.time_of_day().seconds(); + + if(!SystemTimeToFileTime(&target_system_time,((FILETIME*)&due_time))) + { + due_time.QuadPart=0; + } + else + { + long const hundred_nanoseconds_in_one_second=10000000; + due_time.QuadPart+=target_time.abs_time.time_of_day().fractional_seconds()*(hundred_nanoseconds_in_one_second/target_time.abs_time.time_of_day().ticks_per_second()); + } + } + return due_time; + } + } + + + bool interruptible_wait(detail::win32::handle handle_to_wait_for,detail::timeout target_time) + { + detail::win32::handle handles[3]={0}; unsigned handle_count=0; + unsigned wait_handle_index=~0U; unsigned interruption_index=~0U; + unsigned timeout_index=~0U; if(handle_to_wait_for!=detail::win32::invalid_handle_value) { + wait_handle_index=handle_count; handles[handle_count++]=handle_to_wait_for; } if(get_current_thread_data() && get_current_thread_data()->interruption_enabled) @@ -367,24 +415,79 @@ namespace boost interruption_index=handle_count; handles[handle_count++]=get_current_thread_data()->interruption_handle; } + + detail::win32::handle_manager timer_handle; + +#ifndef UNDER_CE + unsigned const min_timer_wait_period=20; + + if(!target_time.is_sentinel()) + { + detail::timeout::remaining_time const time_left=target_time.remaining_milliseconds(); + if(time_left.milliseconds > min_timer_wait_period) + { + // for a long-enough timeout, use a waitable timer (which tracks clock changes) + timer_handle=CreateWaitableTimer(NULL,false,NULL); + if(timer_handle!=0) + { + LARGE_INTEGER due_time=get_due_time(target_time); + + bool const set_time_succeeded=SetWaitableTimer(timer_handle,&due_time,0,0,0,false)!=0; + if(set_time_succeeded) + { + timeout_index=handle_count; + handles[handle_count++]=timer_handle; + } + } + } + else if(!target_time.relative) + { + // convert short absolute-time timeouts into relative ones, so we don't race against clock changes + target_time=detail::timeout(time_left.milliseconds); + } + } +#endif - if(handle_count) + bool const using_timer=timeout_index!=~0u; + detail::timeout::remaining_time time_left(0); + + do { - unsigned long const notified_index=detail::win32::WaitForMultipleObjects(handle_count,handles,false,milliseconds); - if((handle_to_wait_for!=detail::win32::invalid_handle_value) && !notified_index) + if(!using_timer) { - return true; + time_left=target_time.remaining_milliseconds(); } - else if(notified_index==interruption_index) + + if(handle_count) { - detail::win32::ResetEvent(get_current_thread_data()->interruption_handle); - throw thread_interrupted(); + unsigned long const notified_index=detail::win32::WaitForMultipleObjects(handle_count,handles,false,using_timer?INFINITE:time_left.milliseconds); + if(notified_indexinterruption_handle); + throw thread_interrupted(); + } + else if(notified_index==timeout_index) + { + return false; + } + } + } + else + { + detail::win32::Sleep(time_left.milliseconds); + } + if(target_time.relative) + { + target_time.milliseconds-=detail::timeout::max_non_infinite_wait; } } - else - { - detail::win32::Sleep(milliseconds); - } + while(time_left.more); return false; } diff --git a/src/win32/tss_pe.cpp b/src/win32/tss_pe.cpp index 4528b90f..dec71c3b 100644 --- a/src/win32/tss_pe.cpp +++ b/src/win32/tss_pe.cpp @@ -230,13 +230,10 @@ extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata void NTAPI on_tls_callback(HINSTANCE h, DWORD dwReason, PVOID pv) { - OutputDebugString("on_tls_callback\n"); - switch (dwReason) { case DLL_THREAD_DETACH: { - OutputDebugString("on_tls_callback: thread_exit\n"); on_thread_exit(); break; } diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 209e6b07..be5fea7b 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1,4 +1,5 @@ # (C) Copyright William E. Kempf 2001. +# (C) Copyright 2007 Anthony Williams. # 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) # @@ -35,6 +36,9 @@ rule thread-run ( sources ) test-suite "threads" : [ thread-run test_thread.cpp ] [ thread-run test_mutex.cpp ] + [ thread-run test_condition_notify_one.cpp ] + [ thread-run test_condition_timed_wait_times_out.cpp ] + [ thread-run test_condition_notify_all.cpp ] [ thread-run test_condition.cpp ] [ thread-run test_tss.cpp ] [ thread-run test_once.cpp ] diff --git a/test/condition_test_common.hpp b/test/condition_test_common.hpp new file mode 100644 index 00000000..df7d5edc --- /dev/null +++ b/test/condition_test_common.hpp @@ -0,0 +1,95 @@ +#ifndef CONDITION_TEST_COMMON_HPP +#define CONDITION_TEST_COMMON_HPP +// Copyright (C) 2007 Anthony Williams +// +// 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) + +#include +#include +#include + +unsigned const timeout_seconds=5; + +struct wait_for_flag +{ + boost::mutex mutex; + boost::condition_variable cond_var; + bool flag; + unsigned woken; + + wait_for_flag(): + flag(false),woken(0) + {} + + struct check_flag + { + bool const& flag; + + check_flag(bool const& flag_): + flag(flag_) + {} + + bool operator()() const + { + return flag; + } + }; + + + void wait_without_predicate() + { + boost::mutex::scoped_lock lock(mutex); + while(!flag) + { + cond_var.wait(lock); + } + ++woken; + } + + void wait_with_predicate() + { + boost::mutex::scoped_lock lock(mutex); + cond_var.wait(lock,check_flag(flag)); + if(flag) + { + ++woken; + } + } + + void timed_wait_without_predicate() + { + boost::system_time const timeout=boost::get_system_time()+boost::posix_time::seconds(timeout_seconds); + + boost::mutex::scoped_lock lock(mutex); + while(!flag) + { + if(!cond_var.timed_wait(lock,timeout)) + { + return; + } + } + ++woken; + } + + void timed_wait_with_predicate() + { + boost::system_time const timeout=boost::get_system_time()+boost::posix_time::seconds(timeout_seconds); + boost::mutex::scoped_lock lock(mutex); + if(cond_var.timed_wait(lock,timeout,check_flag(flag)) && flag) + { + ++woken; + } + } + void relative_timed_wait_with_predicate() + { + boost::mutex::scoped_lock lock(mutex); + if(cond_var.timed_wait(lock,boost::posix_time::seconds(timeout_seconds),check_flag(flag)) && flag) + { + ++woken; + } + } +}; + + +#endif diff --git a/test/test_barrier.cpp b/test/test_barrier.cpp index bccd346f..e4879704 100644 --- a/test/test_barrier.cpp +++ b/test/test_barrier.cpp @@ -52,7 +52,7 @@ void test_barrier() throw; } - BOOST_CHECK(global_parameter == 5); + BOOST_CHECK_EQUAL(global_parameter,5); } boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) diff --git a/test/test_condition.cpp b/test/test_condition.cpp index 17182e9f..96b1e86e 100644 --- a/test/test_condition.cpp +++ b/test/test_condition.cpp @@ -94,66 +94,6 @@ void condition_test_waits(condition_test_data* data) data->condition.notify_one(); } -void do_test_condition_notify_one() -{ - condition_test_data data; - - boost::thread thread(bind(&condition_test_thread, &data)); - - { - boost::mutex::scoped_lock lock(data.mutex); - BOOST_CHECK(lock ? true : false); - data.notified++; - data.condition.notify_one(); - } - - thread.join(); - BOOST_CHECK_EQUAL(data.awoken, 1); -} - -void test_condition_notify_one() -{ - timed_test(&do_test_condition_notify_one, 100, execution_monitor::use_mutex); -} - -void do_test_condition_notify_all() -{ - const int NUMTHREADS = 5; - boost::thread_group threads; - condition_test_data data; - - try - { - for (int i = 0; i < NUMTHREADS; ++i) - threads.create_thread(bind(&condition_test_thread, &data)); - - { - boost::mutex::scoped_lock lock(data.mutex); - BOOST_CHECK(lock ? true : false); - data.notified++; - data.condition.notify_all(); - } - - threads.join_all(); - } - catch(...) - { - threads.interrupt_all(); - threads.join_all(); - throw; - } - - BOOST_CHECK_EQUAL(data.awoken, NUMTHREADS); -} - -void test_condition_notify_all() -{ - // We should have already tested notify_one here, so - // a timed test with the default execution_monitor::use_condition - // should be OK, and gives the fastest performance - timed_test(&do_test_condition_notify_all, 100); -} - void do_test_condition_waits() { condition_test_data data; @@ -235,43 +175,13 @@ void test_condition_wait_is_a_interruption_point() timed_test(&do_test_condition_wait_is_a_interruption_point, 1); } -bool fake_predicate() -{ - return false; -} - - -void do_test_timed_wait_times_out() -{ - boost::condition_variable cond; - boost::mutex m; - - boost::posix_time::seconds const delay(5); - boost::mutex::scoped_lock lock(m); - boost::system_time const start=boost::get_system_time(); - bool const res=cond.timed_wait(lock,delay,fake_predicate); - boost::system_time const end=boost::get_system_time(); - BOOST_CHECK(!res); - BOOST_CHECK((delay-boost::posix_time::milliseconds(10))<=(end-start)); -} - - -void test_timed_wait_times_out() -{ - timed_test(&do_test_timed_wait_times_out, 15); -} - - boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = BOOST_TEST_SUITE("Boost.Threads: condition test suite"); - test->add(BOOST_TEST_CASE(&test_condition_notify_one)); - test->add(BOOST_TEST_CASE(&test_condition_notify_all)); test->add(BOOST_TEST_CASE(&test_condition_waits)); test->add(BOOST_TEST_CASE(&test_condition_wait_is_a_interruption_point)); - test->add(BOOST_TEST_CASE(&test_timed_wait_times_out)); return test; } diff --git a/test/test_condition_notify_all.cpp b/test/test_condition_notify_all.cpp new file mode 100644 index 00000000..22074b44 --- /dev/null +++ b/test/test_condition_notify_all.cpp @@ -0,0 +1,180 @@ +// Copyright (C) 2007 Anthony Williams +// +// 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) + +#include + +#include + +#include + +#include +#include "condition_test_common.hpp" + +unsigned const number_of_test_threads=5; + +void do_test_condition_notify_all_wakes_from_wait() +{ + wait_for_flag data; + + boost::thread_group group; + + try + { + for(unsigned i=0;iadd(BOOST_TEST_CASE(&test_condition_notify_all)); + + return test; +} diff --git a/test/test_condition_notify_one.cpp b/test/test_condition_notify_one.cpp new file mode 100644 index 00000000..45447bd4 --- /dev/null +++ b/test/test_condition_notify_one.cpp @@ -0,0 +1,113 @@ +// Copyright (C) 2007 Anthony Williams +// +// 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) + +#include + +#include + +#include + +#include +#include "condition_test_common.hpp" + +void do_test_condition_notify_one_wakes_from_wait() +{ + wait_for_flag data; + + boost::thread thread(bind(&wait_for_flag::wait_without_predicate, data)); + + { + boost::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_one(); + } + + thread.join(); + BOOST_CHECK(data.woken); +} + +void do_test_condition_notify_one_wakes_from_wait_with_predicate() +{ + wait_for_flag data; + + boost::thread thread(bind(&wait_for_flag::wait_with_predicate, data)); + + { + boost::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_one(); + } + + thread.join(); + BOOST_CHECK(data.woken); +} + +void do_test_condition_notify_one_wakes_from_timed_wait() +{ + wait_for_flag data; + + boost::thread thread(bind(&wait_for_flag::timed_wait_without_predicate, data)); + + { + boost::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_one(); + } + + thread.join(); + BOOST_CHECK(data.woken); +} + +void do_test_condition_notify_one_wakes_from_timed_wait_with_predicate() +{ + wait_for_flag data; + + boost::thread thread(bind(&wait_for_flag::timed_wait_with_predicate, data)); + + { + boost::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_one(); + } + + thread.join(); + BOOST_CHECK(data.woken); +} + +void do_test_condition_notify_one_wakes_from_relative_timed_wait_with_predicate() +{ + wait_for_flag data; + + boost::thread thread(bind(&wait_for_flag::relative_timed_wait_with_predicate, data)); + + { + boost::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_one(); + } + + thread.join(); + BOOST_CHECK(data.woken); +} + +void test_condition_notify_one() +{ + timed_test(&do_test_condition_notify_one_wakes_from_wait, timeout_seconds, execution_monitor::use_mutex); + timed_test(&do_test_condition_notify_one_wakes_from_wait_with_predicate, timeout_seconds, execution_monitor::use_mutex); + timed_test(&do_test_condition_notify_one_wakes_from_timed_wait, timeout_seconds, execution_monitor::use_mutex); + timed_test(&do_test_condition_notify_one_wakes_from_timed_wait_with_predicate, timeout_seconds, execution_monitor::use_mutex); + timed_test(&do_test_condition_notify_one_wakes_from_relative_timed_wait_with_predicate, timeout_seconds, execution_monitor::use_mutex); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: condition test suite"); + + test->add(BOOST_TEST_CASE(&test_condition_notify_one)); + + return test; +} diff --git a/test/test_condition_timed_wait_times_out.cpp b/test/test_condition_timed_wait_times_out.cpp new file mode 100644 index 00000000..128922a4 --- /dev/null +++ b/test/test_condition_timed_wait_times_out.cpp @@ -0,0 +1,89 @@ +// Copyright (C) 2007 Anthony Williams +// +// 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) + +#include + +#include +#include + +#include +#include "util.inl" + +bool fake_predicate() +{ + return false; +} + +unsigned const timeout_seconds=5; +unsigned const timeout_grace=1; +boost::posix_time::milliseconds const timeout_resolution(100); + + +void do_test_timed_wait_times_out() +{ + boost::condition_variable cond; + boost::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + boost::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+delay; + + while(cond.timed_wait(lock,timeout)); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_timed_wait_with_predicate_times_out() +{ + boost::condition_variable cond; + boost::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + boost::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+delay; + + bool const res=cond.timed_wait(lock,timeout,fake_predicate); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK(!res); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_relative_timed_wait_with_predicate_times_out() +{ + boost::condition_variable cond; + boost::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + boost::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + + bool const res=cond.timed_wait(lock,delay,fake_predicate); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK(!res); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + + +void test_timed_wait_times_out() +{ + timed_test(&do_test_timed_wait_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_timed_wait_with_predicate_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_relative_timed_wait_with_predicate_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); +} + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: condition test suite"); + + test->add(BOOST_TEST_CASE(&test_timed_wait_times_out)); + + return test; +} diff --git a/test/test_shared_mutex.cpp b/test/test_shared_mutex.cpp index b43a186e..a5627835 100644 --- a/test/test_shared_mutex.cpp +++ b/test/test_shared_mutex.cpp @@ -466,7 +466,7 @@ void test_if_other_thread_has_write_lock_try_lock_shared_returns_false() unsigned unblocked_count=0; boost::mutex::scoped_lock finish_lock(finish_mutex); boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); - boost::thread::sleep(delay(1)); + boost::this_thread::sleep(boost::posix_time::seconds(1)); CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); bool const try_succeeded=rw_mutex.try_lock_shared(); diff --git a/test/util.inl b/test/util.inl index 32e7c77a..417b255d 100644 --- a/test/util.inl +++ b/test/util.inl @@ -1,5 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf +// Copyright (C) 2007 Anthony Williams // // 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) @@ -154,6 +155,26 @@ thread_binder bind(const F& func, const T& param) { return thread_binder(func, param); } + +template +class thread_member_binder +{ +public: + thread_member_binder(R (T::*func)(), T& param) + : func(func), param(param) { } + void operator()() const { (param.*func)(); } + +private: + R (T::*func)(); + T& param; +}; + + +template +thread_member_binder bind(R (T::*func)(), T& param) +{ + return thread_member_binder(func, param); +} } // namespace #endif From 914e67dc0455c9567be6645efc859b898afed2d3 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 3 Dec 2007 22:00:26 +0000 Subject: [PATCH 071/214] check predicate before returning if we time out on a predicated version of timed_wait [SVN r41668] --- include/boost/thread/pthread/condition_variable.hpp | 2 +- include/boost/thread/pthread/condition_variable_fwd.hpp | 2 +- include/boost/thread/win32/condition_variable.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/boost/thread/pthread/condition_variable.hpp b/include/boost/thread/pthread/condition_variable.hpp index 754f0ac3..52c156bc 100644 --- a/include/boost/thread/pthread/condition_variable.hpp +++ b/include/boost/thread/pthread/condition_variable.hpp @@ -143,7 +143,7 @@ namespace boost while (!pred()) { if(!timed_wait(m, wait_until)) - return false; + return pred(); } return true; } diff --git a/include/boost/thread/pthread/condition_variable_fwd.hpp b/include/boost/thread/pthread/condition_variable_fwd.hpp index ca45e54b..1d1fbdf4 100644 --- a/include/boost/thread/pthread/condition_variable_fwd.hpp +++ b/include/boost/thread/pthread/condition_variable_fwd.hpp @@ -41,7 +41,7 @@ namespace boost while (!pred()) { if(!timed_wait(m, wait_until)) - return false; + return pred(); } return true; } diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index ccf29b22..beebcf62 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -186,7 +186,7 @@ namespace boost while (!pred()) { if(!do_wait(m, wait_until)) - return false; + return pred(); } return true; } From 792958e6933f6c735b18746ec77ed2c9644091c9 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 4 Dec 2007 07:57:23 +0000 Subject: [PATCH 072/214] fixed typo in condition_variable_any::timed_wait [SVN r41679] --- include/boost/thread/win32/condition_variable.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index beebcf62..3a29ef1c 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -330,7 +330,7 @@ namespace boost template bool timed_wait(lock_type& m,duration_type const& wait_duration,predicate_type pred) { - return timed_wait(m,wait_duration.total_milliseconds(),pred); + return do_wait(m,wait_duration.total_milliseconds(),pred); } }; From 56d660b7fd8309e6a3e8ea8776353f5ca498c131 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 4 Dec 2007 09:15:37 +0000 Subject: [PATCH 073/214] changed boost::move to boost::detail::thread_move to fix issue #1492 [SVN r41681] --- include/boost/thread/detail/move.hpp | 34 +++++++++++--------- include/boost/thread/locks.hpp | 42 ++++++++++++------------- include/boost/thread/pthread/thread.hpp | 12 +++---- include/boost/thread/win32/thread.hpp | 12 +++---- src/win32/thread.cpp | 10 +++--- 5 files changed, 57 insertions(+), 53 deletions(-) diff --git a/include/boost/thread/detail/move.hpp b/include/boost/thread/detail/move.hpp index ba4bb655..4c90abc3 100644 --- a/include/boost/thread/detail/move.hpp +++ b/include/boost/thread/detail/move.hpp @@ -8,25 +8,29 @@ namespace boost { - template - struct move_t + namespace detail { - T& t; - move_t(T& t_): - t(t_) - {} - - T* operator->() const + template + struct thread_move_t { - return &t; - } - }; + T& t; + thread_move_t(T& t_): + t(t_) + {} - template - move_t move(T& t) - { - return move_t(t); + T* operator->() const + { + return &t; + } + }; + + template + thread_move_t thread_move(T& t) + { + return thread_move_t(t); + } } + } diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index b56c996e..999b87f8 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -86,21 +86,21 @@ namespace boost { timed_lock(target_time); } - unique_lock(boost::move_t > other): + unique_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; } - unique_lock(boost::move_t > other); + unique_lock(detail::thread_move_t > other); - unique_lock& operator=(boost::move_t > other) + unique_lock& operator=(detail::thread_move_t > other) { unique_lock temp(other); swap(temp); return *this; } - unique_lock& operator=(boost::move_t > other) + unique_lock& operator=(detail::thread_move_t > other) { unique_lock temp(other); swap(temp); @@ -112,7 +112,7 @@ namespace boost std::swap(m,other.m); std::swap(is_locked,other.is_locked); } - void swap(boost::move_t > other) + void swap(detail::thread_move_t > other) { std::swap(m,other->m); std::swap(is_locked,other->is_locked); @@ -228,13 +228,13 @@ namespace boost timed_lock(target_time); } - shared_lock(boost::move_t > other): + shared_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; } - shared_lock(boost::move_t > other): + shared_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; @@ -244,7 +244,7 @@ namespace boost } } - shared_lock(boost::move_t > other): + shared_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; @@ -254,21 +254,21 @@ namespace boost } } - shared_lock& operator=(boost::move_t > other) + shared_lock& operator=(detail::thread_move_t > other) { shared_lock temp(other); swap(temp); return *this; } - shared_lock& operator=(boost::move_t > other) + shared_lock& operator=(detail::thread_move_t > other) { shared_lock temp(other); swap(temp); return *this; } - shared_lock& operator=(boost::move_t > other) + shared_lock& operator=(detail::thread_move_t > other) { shared_lock temp(other); swap(temp); @@ -364,13 +364,13 @@ namespace boost lock(); } } - upgrade_lock(boost::move_t > other): + upgrade_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; } - upgrade_lock(boost::move_t > other): + upgrade_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; @@ -380,14 +380,14 @@ namespace boost } } - upgrade_lock& operator=(boost::move_t > other) + upgrade_lock& operator=(detail::thread_move_t > other) { upgrade_lock temp(other); swap(temp); return *this; } - upgrade_lock& operator=(boost::move_t > other) + upgrade_lock& operator=(detail::thread_move_t > other) { upgrade_lock temp(other); swap(temp); @@ -453,7 +453,7 @@ namespace boost }; template - unique_lock::unique_lock(boost::move_t > other): + unique_lock::unique_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; @@ -474,23 +474,23 @@ namespace boost upgrade_to_unique_lock& operator=(upgrade_to_unique_lock&); public: explicit upgrade_to_unique_lock(upgrade_lock& m_): - source(&m_),exclusive(boost::move(*source)) + source(&m_),exclusive(detail::thread_move(*source)) {} ~upgrade_to_unique_lock() { if(source) { - *source=boost::move(exclusive); + *source=detail::thread_move(exclusive); } } - upgrade_to_unique_lock(boost::move_t > other): - source(other->source),exclusive(boost::move(other->exclusive)) + upgrade_to_unique_lock(detail::thread_move_t > other): + source(other->source),exclusive(detail::thread_move(other->exclusive)) { other->source=0; } - upgrade_to_unique_lock& operator=(boost::move_t > other) + upgrade_to_unique_lock& operator=(detail::thread_move_t > other) { upgrade_to_unique_lock temp(other); swap(temp); diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index d86e3465..1873ea38 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -97,7 +97,7 @@ namespace boost thread_data(F f_): f(f_) {} - thread_data(boost::move_t f_): + thread_data(detail::thread_move_t f_): f(f_) {} @@ -127,16 +127,16 @@ namespace boost start_thread(); } template - thread(boost::move_t f): + thread(detail::thread_move_t f): thread_info(new thread_data(f)) { start_thread(); } - explicit thread(boost::move_t x); - thread& operator=(boost::move_t x); - operator boost::move_t(); - boost::move_t move(); + explicit thread(detail::thread_move_t x); + thread& operator=(detail::thread_move_t x); + operator detail::thread_move_t(); + detail::thread_move_t move(); void swap(thread& x); diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 93a44792..8d7e9809 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -169,7 +169,7 @@ namespace boost thread_data(F f_): f(f_) {} - thread_data(boost::move_t f_): + thread_data(detail::thread_move_t f_): f(f_) {} @@ -200,16 +200,16 @@ namespace boost start_thread(); } template - explicit thread(boost::move_t f): + explicit thread(detail::thread_move_t f): thread_info(detail::heap_new >(f)) { start_thread(); } - thread(boost::move_t x); - thread& operator=(boost::move_t x); - operator boost::move_t(); - boost::move_t move(); + thread(detail::thread_move_t x); + thread& operator=(detail::thread_move_t x); + operator detail::thread_move_t(); + detail::thread_move_t move(); void swap(thread& x); diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index d2ec1dbe..b762fdbf 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -244,7 +244,7 @@ namespace boost detach(); } - thread::thread(boost::move_t x) + thread::thread(detail::thread_move_t x) { { boost::mutex::scoped_lock l(x->thread_info_mutex); @@ -253,21 +253,21 @@ namespace boost x->release_handle(); } - thread& thread::operator=(boost::move_t x) + thread& thread::operator=(detail::thread_move_t x) { thread new_thread(x); swap(new_thread); return *this; } - thread::operator boost::move_t() + thread::operator detail::thread_move_t() { return move(); } - boost::move_t thread::move() + detail::thread_move_t thread::move() { - boost::move_t x(*this); + detail::thread_move_t x(*this); return x; } From d15ee57cd16666fbff41268a768512f3413ba451 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 4 Dec 2007 10:04:30 +0000 Subject: [PATCH 074/214] split shared mutex tests in two to take less time [SVN r41682] --- test/Jamfile.v2 | 1 + test/shared_mutex_locking_thread.hpp | 62 ++++++ test/test_shared_mutex.cpp | 310 +-------------------------- test/test_shared_mutex_part_2.cpp | 270 +++++++++++++++++++++++ 4 files changed, 334 insertions(+), 309 deletions(-) create mode 100644 test/shared_mutex_locking_thread.hpp create mode 100644 test/test_shared_mutex_part_2.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index be5fea7b..a17207a7 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -45,6 +45,7 @@ rule thread-run ( sources ) [ thread-run test_xtime.cpp ] [ thread-run test_barrier.cpp ] [ thread-run test_shared_mutex.cpp ] + [ thread-run test_shared_mutex_part_2.cpp ] [ thread-run test_lock_concept.cpp ] ; } diff --git a/test/shared_mutex_locking_thread.hpp b/test/shared_mutex_locking_thread.hpp new file mode 100644 index 00000000..14b0bdbc --- /dev/null +++ b/test/shared_mutex_locking_thread.hpp @@ -0,0 +1,62 @@ +#ifndef SHARED_MUTEX_LOCKING_THREAD_HPP +#define SHARED_MUTEX_LOCKING_THREAD_HPP + +#include +#include +#include + +template +class locking_thread +{ + boost::shared_mutex& rw_mutex; + unsigned& unblocked_count; + unsigned& simultaneous_running_count; + unsigned& max_simultaneous_running; + boost::mutex& unblocked_count_mutex; + boost::condition_variable& unblocked_condition; + boost::mutex& finish_mutex; +public: + locking_thread(boost::shared_mutex& rw_mutex_, + unsigned& unblocked_count_, + boost::mutex& unblocked_count_mutex_, + boost::condition_variable& unblocked_condition_, + boost::mutex& finish_mutex_, + unsigned& simultaneous_running_count_, + unsigned& max_simultaneous_running_): + rw_mutex(rw_mutex_), + unblocked_count(unblocked_count_), + unblocked_condition(unblocked_condition_), + simultaneous_running_count(simultaneous_running_count_), + max_simultaneous_running(max_simultaneous_running_), + unblocked_count_mutex(unblocked_count_mutex_), + finish_mutex(finish_mutex_) + {} + + void operator()() + { + // acquire lock + lock_type lock(rw_mutex); + + // increment count to show we're unblocked + { + boost::mutex::scoped_lock ublock(unblocked_count_mutex); + ++unblocked_count; + unblocked_condition.notify_one(); + ++simultaneous_running_count; + if(simultaneous_running_count>max_simultaneous_running) + { + max_simultaneous_running=simultaneous_running_count; + } + } + + // wait to finish + boost::mutex::scoped_lock finish_lock(finish_mutex); + { + boost::mutex::scoped_lock ublock(unblocked_count_mutex); + --simultaneous_running_count; + } + } +}; + + +#endif diff --git a/test/test_shared_mutex.cpp b/test/test_shared_mutex.cpp index a5627835..a8daef4b 100644 --- a/test/test_shared_mutex.cpp +++ b/test/test_shared_mutex.cpp @@ -5,13 +5,9 @@ #include #include -#include -#include -#include #include #include "util.inl" -#include -#include +#include "shared_mutex_locking_thread.hpp" #define CHECK_LOCKED_VALUE_EQUAL(mutex_name,value,expected_value) \ { \ @@ -19,65 +15,6 @@ BOOST_CHECK_EQUAL(value,expected_value); \ } - -namespace -{ - template - class locking_thread - { - boost::shared_mutex& rw_mutex; - unsigned& unblocked_count; - unsigned& simultaneous_running_count; - unsigned& max_simultaneous_running; - boost::mutex& unblocked_count_mutex; - boost::condition_variable& unblocked_condition; - boost::mutex& finish_mutex; - public: - locking_thread(boost::shared_mutex& rw_mutex_, - unsigned& unblocked_count_, - boost::mutex& unblocked_count_mutex_, - boost::condition_variable& unblocked_condition_, - boost::mutex& finish_mutex_, - unsigned& simultaneous_running_count_, - unsigned& max_simultaneous_running_): - rw_mutex(rw_mutex_), - unblocked_count(unblocked_count_), - unblocked_condition(unblocked_condition_), - simultaneous_running_count(simultaneous_running_count_), - max_simultaneous_running(max_simultaneous_running_), - unblocked_count_mutex(unblocked_count_mutex_), - finish_mutex(finish_mutex_) - {} - - void operator()() - { - // acquire lock - lock_type lock(rw_mutex); - - // increment count to show we're unblocked - { - boost::mutex::scoped_lock ublock(unblocked_count_mutex); - ++unblocked_count; - unblocked_condition.notify_one(); - ++simultaneous_running_count; - if(simultaneous_running_count>max_simultaneous_running) - { - max_simultaneous_running=simultaneous_running_count; - } - } - - // wait to finish - boost::mutex::scoped_lock finish_lock(finish_mutex); - { - boost::mutex::scoped_lock ublock(unblocked_count_mutex); - --simultaneous_running_count; - } - } - }; - -} - - void test_multiple_readers() { unsigned const number_of_threads=100; @@ -328,250 +265,11 @@ void test_unlocking_last_reader_only_unblocks_one_writer() throw; } - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+writer_count); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_readers,reader_count); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_writers,1u); } -void test_only_one_upgrade_lock_permitted() -{ - unsigned const number_of_threads=100; - - boost::thread_group pool; - - boost::shared_mutex rw_mutex; - unsigned unblocked_count=0; - unsigned simultaneous_running_count=0; - unsigned max_simultaneous_running=0; - boost::mutex unblocked_count_mutex; - boost::condition_variable unblocked_condition; - boost::mutex finish_mutex; - boost::mutex::scoped_lock finish_lock(finish_mutex); - - try - { - for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_mutex,simultaneous_running_count,max_simultaneous_running)); - } - - boost::thread::sleep(delay(1)); - - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); - - finish_lock.unlock(); - - pool.join_all(); - } - catch(...) - { - pool.interrupt_all(); - pool.join_all(); - throw; - } - - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,number_of_threads); - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1u); -} - -void test_can_lock_upgrade_if_currently_locked_shared() -{ - boost::thread_group pool; - - boost::shared_mutex rw_mutex; - unsigned unblocked_count=0; - unsigned simultaneous_running_count=0; - unsigned max_simultaneous_running=0; - boost::mutex unblocked_count_mutex; - boost::condition_variable unblocked_condition; - boost::mutex finish_mutex; - boost::mutex::scoped_lock finish_lock(finish_mutex); - - unsigned const reader_count=100; - - try - { - for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_mutex,simultaneous_running_count,max_simultaneous_running)); - } - boost::thread::sleep(delay(1)); - pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_mutex,simultaneous_running_count,max_simultaneous_running)); - { - boost::mutex::scoped_lock lk(unblocked_count_mutex); - while(unblocked_count<(reader_count+1)) - { - unblocked_condition.wait(lk); - } - } - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); - - finish_lock.unlock(); - pool.join_all(); - } - catch(...) - { - pool.interrupt_all(); - pool.join_all(); - throw; - } - - - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,reader_count+1); -} - -namespace -{ - class simple_writing_thread - { - boost::shared_mutex& rwm; - boost::mutex& finish_mutex; - boost::mutex& unblocked_mutex; - unsigned& unblocked_count; - - public: - simple_writing_thread(boost::shared_mutex& rwm_, - boost::mutex& finish_mutex_, - boost::mutex& unblocked_mutex_, - unsigned& unblocked_count_): - rwm(rwm_),finish_mutex(finish_mutex_), - unblocked_mutex(unblocked_mutex_),unblocked_count(unblocked_count_) - {} - - void operator()() - { - boost::unique_lock lk(rwm); - - { - boost::mutex::scoped_lock ulk(unblocked_mutex); - ++unblocked_count; - } - - boost::mutex::scoped_lock flk(finish_mutex); - } - }; -} - -void test_if_other_thread_has_write_lock_try_lock_shared_returns_false() -{ - - boost::shared_mutex rw_mutex; - boost::mutex finish_mutex; - boost::mutex unblocked_mutex; - unsigned unblocked_count=0; - boost::mutex::scoped_lock finish_lock(finish_mutex); - boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); - boost::this_thread::sleep(boost::posix_time::seconds(1)); - CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); - - bool const try_succeeded=rw_mutex.try_lock_shared(); - BOOST_CHECK(!try_succeeded); - if(try_succeeded) - { - rw_mutex.unlock_shared(); - } - - finish_lock.unlock(); - writer.join(); -} - -void test_if_no_thread_has_lock_try_lock_shared_returns_true() -{ - boost::shared_mutex rw_mutex; - bool const try_succeeded=rw_mutex.try_lock_shared(); - BOOST_CHECK(try_succeeded); - if(try_succeeded) - { - rw_mutex.unlock_shared(); - } -} - -namespace -{ - class simple_reading_thread - { - boost::shared_mutex& rwm; - boost::mutex& finish_mutex; - boost::mutex& unblocked_mutex; - unsigned& unblocked_count; - - public: - simple_reading_thread(boost::shared_mutex& rwm_, - boost::mutex& finish_mutex_, - boost::mutex& unblocked_mutex_, - unsigned& unblocked_count_): - rwm(rwm_),finish_mutex(finish_mutex_), - unblocked_mutex(unblocked_mutex_),unblocked_count(unblocked_count_) - {} - - void operator()() - { - boost::shared_lock lk(rwm); - - { - boost::mutex::scoped_lock ulk(unblocked_mutex); - ++unblocked_count; - } - - boost::mutex::scoped_lock flk(finish_mutex); - } - }; -} - -void test_if_other_thread_has_shared_lock_try_lock_shared_returns_true() -{ - - boost::shared_mutex rw_mutex; - boost::mutex finish_mutex; - boost::mutex unblocked_mutex; - unsigned unblocked_count=0; - boost::mutex::scoped_lock finish_lock(finish_mutex); - boost::thread writer(simple_reading_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); - boost::thread::sleep(delay(1)); - CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); - - bool const try_succeeded=rw_mutex.try_lock_shared(); - BOOST_CHECK(try_succeeded); - if(try_succeeded) - { - rw_mutex.unlock_shared(); - } - - finish_lock.unlock(); - writer.join(); -} - -void test_timed_lock_shared_times_out_if_write_lock_held() -{ - boost::shared_mutex rw_mutex; - boost::mutex finish_mutex; - boost::mutex unblocked_mutex; - unsigned unblocked_count=0; - boost::mutex::scoped_lock finish_lock(finish_mutex); - boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); - boost::thread::sleep(delay(1)); - CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); - - boost::system_time const start=boost::get_system_time(); - boost::system_time const timeout=start+boost::posix_time::milliseconds(2000); - bool const timed_lock_succeeded=rw_mutex.timed_lock_shared(timeout); - BOOST_CHECK(in_range(boost::get_xtime(timeout),1)); - BOOST_CHECK(!timed_lock_succeeded); - if(timed_lock_succeeded) - { - rw_mutex.unlock_shared(); - } - - finish_lock.unlock(); - writer.join(); -} - - boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = @@ -582,12 +280,6 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) test->add(BOOST_TEST_CASE(&test_reader_blocks_writer)); test->add(BOOST_TEST_CASE(&test_unlocking_writer_unblocks_all_readers)); test->add(BOOST_TEST_CASE(&test_unlocking_last_reader_only_unblocks_one_writer)); - test->add(BOOST_TEST_CASE(&test_only_one_upgrade_lock_permitted)); - test->add(BOOST_TEST_CASE(&test_can_lock_upgrade_if_currently_locked_shared)); - test->add(BOOST_TEST_CASE(&test_if_other_thread_has_write_lock_try_lock_shared_returns_false)); - test->add(BOOST_TEST_CASE(&test_if_no_thread_has_lock_try_lock_shared_returns_true)); - test->add(BOOST_TEST_CASE(&test_if_other_thread_has_shared_lock_try_lock_shared_returns_true)); - test->add(BOOST_TEST_CASE(&test_timed_lock_shared_times_out_if_write_lock_held)); return test; } diff --git a/test/test_shared_mutex_part_2.cpp b/test/test_shared_mutex_part_2.cpp new file mode 100644 index 00000000..e477b6f4 --- /dev/null +++ b/test/test_shared_mutex_part_2.cpp @@ -0,0 +1,270 @@ +// (C) Copyright 2006-7 Anthony Williams +// 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) + +#include +#include +#include +#include "util.inl" +#include "shared_mutex_locking_thread.hpp" + +#define CHECK_LOCKED_VALUE_EQUAL(mutex_name,value,expected_value) \ + { \ + boost::mutex::scoped_lock lock(mutex_name); \ + BOOST_CHECK_EQUAL(value,expected_value); \ + } + + +void test_only_one_upgrade_lock_permitted() +{ + unsigned const number_of_threads=100; + + boost::thread_group pool; + + boost::shared_mutex rw_mutex; + unsigned unblocked_count=0; + unsigned simultaneous_running_count=0; + unsigned max_simultaneous_running=0; + boost::mutex unblocked_count_mutex; + boost::condition_variable unblocked_condition; + boost::mutex finish_mutex; + boost::mutex::scoped_lock finish_lock(finish_mutex); + + try + { + for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); + } + + boost::thread::sleep(delay(1)); + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); + + finish_lock.unlock(); + + pool.join_all(); + } + catch(...) + { + pool.interrupt_all(); + pool.join_all(); + throw; + } + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,number_of_threads); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1u); +} + +void test_can_lock_upgrade_if_currently_locked_shared() +{ + boost::thread_group pool; + + boost::shared_mutex rw_mutex; + unsigned unblocked_count=0; + unsigned simultaneous_running_count=0; + unsigned max_simultaneous_running=0; + boost::mutex unblocked_count_mutex; + boost::condition_variable unblocked_condition; + boost::mutex finish_mutex; + boost::mutex::scoped_lock finish_lock(finish_mutex); + + unsigned const reader_count=100; + + try + { + for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); + } + boost::thread::sleep(delay(1)); + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); + { + boost::mutex::scoped_lock lk(unblocked_count_mutex); + while(unblocked_count<(reader_count+1)) + { + unblocked_condition.wait(lk); + } + } + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); + + finish_lock.unlock(); + pool.join_all(); + } + catch(...) + { + pool.interrupt_all(); + pool.join_all(); + throw; + } + + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,reader_count+1); +} + +namespace +{ + class simple_writing_thread + { + boost::shared_mutex& rwm; + boost::mutex& finish_mutex; + boost::mutex& unblocked_mutex; + unsigned& unblocked_count; + + public: + simple_writing_thread(boost::shared_mutex& rwm_, + boost::mutex& finish_mutex_, + boost::mutex& unblocked_mutex_, + unsigned& unblocked_count_): + rwm(rwm_),finish_mutex(finish_mutex_), + unblocked_mutex(unblocked_mutex_),unblocked_count(unblocked_count_) + {} + + void operator()() + { + boost::unique_lock lk(rwm); + + { + boost::mutex::scoped_lock ulk(unblocked_mutex); + ++unblocked_count; + } + + boost::mutex::scoped_lock flk(finish_mutex); + } + }; +} + +void test_if_other_thread_has_write_lock_try_lock_shared_returns_false() +{ + + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + unsigned unblocked_count=0; + boost::mutex::scoped_lock finish_lock(finish_mutex); + boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); + boost::this_thread::sleep(boost::posix_time::seconds(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); + + bool const try_succeeded=rw_mutex.try_lock_shared(); + BOOST_CHECK(!try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_shared(); + } + + finish_lock.unlock(); + writer.join(); +} + +void test_if_no_thread_has_lock_try_lock_shared_returns_true() +{ + boost::shared_mutex rw_mutex; + bool const try_succeeded=rw_mutex.try_lock_shared(); + BOOST_CHECK(try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_shared(); + } +} + +namespace +{ + class simple_reading_thread + { + boost::shared_mutex& rwm; + boost::mutex& finish_mutex; + boost::mutex& unblocked_mutex; + unsigned& unblocked_count; + + public: + simple_reading_thread(boost::shared_mutex& rwm_, + boost::mutex& finish_mutex_, + boost::mutex& unblocked_mutex_, + unsigned& unblocked_count_): + rwm(rwm_),finish_mutex(finish_mutex_), + unblocked_mutex(unblocked_mutex_),unblocked_count(unblocked_count_) + {} + + void operator()() + { + boost::shared_lock lk(rwm); + + { + boost::mutex::scoped_lock ulk(unblocked_mutex); + ++unblocked_count; + } + + boost::mutex::scoped_lock flk(finish_mutex); + } + }; +} + +void test_if_other_thread_has_shared_lock_try_lock_shared_returns_true() +{ + + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + unsigned unblocked_count=0; + boost::mutex::scoped_lock finish_lock(finish_mutex); + boost::thread writer(simple_reading_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); + + bool const try_succeeded=rw_mutex.try_lock_shared(); + BOOST_CHECK(try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_shared(); + } + + finish_lock.unlock(); + writer.join(); +} + +void test_timed_lock_shared_times_out_if_write_lock_held() +{ + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + unsigned unblocked_count=0; + boost::mutex::scoped_lock finish_lock(finish_mutex); + boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); + + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+boost::posix_time::milliseconds(2000); + bool const timed_lock_succeeded=rw_mutex.timed_lock_shared(timeout); + BOOST_CHECK(in_range(boost::get_xtime(timeout),1)); + BOOST_CHECK(!timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock_shared(); + } + + finish_lock.unlock(); + writer.join(); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: shared_mutex test suite"); + + test->add(BOOST_TEST_CASE(&test_only_one_upgrade_lock_permitted)); + test->add(BOOST_TEST_CASE(&test_can_lock_upgrade_if_currently_locked_shared)); + test->add(BOOST_TEST_CASE(&test_if_other_thread_has_write_lock_try_lock_shared_returns_false)); + test->add(BOOST_TEST_CASE(&test_if_no_thread_has_lock_try_lock_shared_returns_true)); + test->add(BOOST_TEST_CASE(&test_if_other_thread_has_shared_lock_try_lock_shared_returns_true)); + test->add(BOOST_TEST_CASE(&test_timed_lock_shared_times_out_if_write_lock_held)); + + return test; +} From 11e0435a4b3440a149772d50116cbfcb5727fec7 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 4 Dec 2007 11:44:25 +0000 Subject: [PATCH 075/214] don't dllexport/dllimport inline functions [SVN r41683] --- include/boost/thread/pthread/thread.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index 1873ea38..8c1b2140 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -208,7 +208,7 @@ namespace boost ~restore_interruption(); }; - BOOST_THREAD_DECL inline thread::id get_id() + inline thread::id get_id() { return thread::id(pthread_self()); } @@ -217,13 +217,13 @@ namespace boost BOOST_THREAD_DECL bool interruption_enabled(); BOOST_THREAD_DECL bool interruption_requested(); - BOOST_THREAD_DECL inline void yield() + inline void yield() { thread::yield(); } template - BOOST_THREAD_DECL inline void sleep(TimeDuration const& rel_time) + inline void sleep(TimeDuration const& rel_time) { thread::sleep(get_system_time()+rel_time); } From 7ebf5ea3d186bdf6c7df65f1d5ad8763abafe506 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 4 Dec 2007 12:08:38 +0000 Subject: [PATCH 076/214] add explicit casts to remove warnings [SVN r41684] --- src/win32/thread.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index b762fdbf..ad071d01 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -379,9 +379,9 @@ namespace boost target_system_time.wYear=target_time.abs_time.date().year(); target_system_time.wMonth=target_time.abs_time.date().month(); target_system_time.wDay=target_time.abs_time.date().day(); - target_system_time.wHour=target_time.abs_time.time_of_day().hours(); - target_system_time.wMinute=target_time.abs_time.time_of_day().minutes(); - target_system_time.wSecond=target_time.abs_time.time_of_day().seconds(); + target_system_time.wHour=(WORD)target_time.abs_time.time_of_day().hours(); + target_system_time.wMinute=(WORD)target_time.abs_time.time_of_day().minutes(); + target_system_time.wSecond=(WORD)target_time.abs_time.time_of_day().seconds(); if(!SystemTimeToFileTime(&target_system_time,((FILETIME*)&due_time))) { From 7c68e190a9f637a27c0cefa7543ccdc8ff4360cd Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 4 Dec 2007 13:02:58 +0000 Subject: [PATCH 077/214] Added test for thread move constructor; implemented move on pthreads [SVN r41686] --- include/boost/thread/pthread/thread.hpp | 2 +- include/boost/thread/win32/thread.hpp | 2 +- src/pthread/thread.cpp | 31 +++++++++++++++++++++ src/win32/thread.cpp | 10 +++---- test/Jamfile.v2 | 1 + test/test_thread_move.cpp | 37 +++++++++++++++++++++++++ 6 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 test/test_thread_move.cpp diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index 8c1b2140..ad497230 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -133,7 +133,7 @@ namespace boost start_thread(); } - explicit thread(detail::thread_move_t x); + thread(detail::thread_move_t x); thread& operator=(detail::thread_move_t x); operator detail::thread_move_t(); detail::thread_move_t move(); diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 8d7e9809..14d93f83 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -200,7 +200,7 @@ namespace boost start_thread(); } template - explicit thread(detail::thread_move_t f): + thread(detail::thread_move_t f): thread_info(detail::heap_new >(f)) { start_thread(); diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 8a074db4..77aebcfd 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -188,6 +188,37 @@ namespace boost detach(); } + thread::thread(detail::thread_move_t x) + { + lock_guard lock(x->thread_info_mutex); + thread_info=x->thread_info; + x->thread_info.reset(); + } + + thread& thread::operator=(detail::thread_move_t x) + { + thread new_thread(x); + swap(new_thread); + return *this; + } + + thread::operator detail::thread_move_t() + { + return move(); + } + + detail::thread_move_t thread::move() + { + detail::thread_move_t x(*this); + return x; + } + + void thread::swap(thread& x) + { + thread_info.swap(x.thread_info); + } + + bool thread::operator==(const thread& other) const { return get_id()==other.get_id(); diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index ad071d01..d64c78dd 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -246,11 +246,9 @@ namespace boost thread::thread(detail::thread_move_t x) { - { - boost::mutex::scoped_lock l(x->thread_info_mutex); - thread_info=x->thread_info; - } - x->release_handle(); + lock_guard lock(x->thread_info_mutex); + thread_info=x->thread_info; + x->thread_info=0; } thread& thread::operator=(detail::thread_move_t x) @@ -317,7 +315,7 @@ namespace boost void thread::release_handle() { - boost::mutex::scoped_lock l1(thread_info_mutex); + lock_guard l1(thread_info_mutex); thread_info=0; } diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index a17207a7..4ff2d48f 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -35,6 +35,7 @@ rule thread-run ( sources ) { test-suite "threads" : [ thread-run test_thread.cpp ] + [ thread-run test_thread_move.cpp ] [ thread-run test_mutex.cpp ] [ thread-run test_condition_notify_one.cpp ] [ thread-run test_condition_timed_wait_times_out.cpp ] diff --git a/test/test_thread_move.cpp b/test/test_thread_move.cpp new file mode 100644 index 00000000..dc8f964f --- /dev/null +++ b/test/test_thread_move.cpp @@ -0,0 +1,37 @@ +// Copyright (C) 2007 Anthony Williams +// +// 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) +#include +#include + +void do_nothing() +{} + +void test_move_on_construction() +{ + boost::thread x=boost::thread(do_nothing); + x.join(); +} + +boost::thread make_thread() +{ + return boost::thread(do_nothing); +} + +void test_move_from_function_return() +{ + boost::thread x=make_thread(); + x.join(); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: thread move test suite"); + + test->add(BOOST_TEST_CASE(test_move_on_construction)); + test->add(BOOST_TEST_CASE(test_move_from_function_return)); + return test; +} From 8bec3637105505b4d861a58004b9e121d88fa6fa Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 4 Dec 2007 14:07:01 +0000 Subject: [PATCH 078/214] changed order of declaration to eliminate warnings [SVN r41687] --- test/shared_mutex_locking_thread.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/shared_mutex_locking_thread.hpp b/test/shared_mutex_locking_thread.hpp index 14b0bdbc..1fccbddf 100644 --- a/test/shared_mutex_locking_thread.hpp +++ b/test/shared_mutex_locking_thread.hpp @@ -10,10 +10,10 @@ class locking_thread { boost::shared_mutex& rw_mutex; unsigned& unblocked_count; + boost::condition_variable& unblocked_condition; unsigned& simultaneous_running_count; unsigned& max_simultaneous_running; boost::mutex& unblocked_count_mutex; - boost::condition_variable& unblocked_condition; boost::mutex& finish_mutex; public: locking_thread(boost::shared_mutex& rw_mutex_, From ed050d753d0b9d519824e345b46d0680685afabd Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 5 Dec 2007 08:27:44 +0000 Subject: [PATCH 079/214] added missing include of detail/config.hpp [SVN r41738] --- include/boost/thread/pthread/tss.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/boost/thread/pthread/tss.hpp b/include/boost/thread/pthread/tss.hpp index c33e6d34..36c385c5 100644 --- a/include/boost/thread/pthread/tss.hpp +++ b/include/boost/thread/pthread/tss.hpp @@ -6,6 +6,7 @@ // http://www.boost.org/LICENSE_1_0.txt) // (C) Copyright 2007 Anthony Williams +#include #include namespace boost From 9a1da141167979523a42a88ac282b0af25c1d343 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 5 Dec 2007 10:58:45 +0000 Subject: [PATCH 080/214] improved timeout checks [SVN r41741] --- test/test_mutex.cpp | 14 +++++++++++--- test/test_shared_mutex_part_2.cpp | 3 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/test/test_mutex.cpp b/test/test_mutex.cpp index 08170fd2..ed2c2571 100644 --- a/test/test_mutex.cpp +++ b/test/test_mutex.cpp @@ -102,6 +102,11 @@ struct test_timedlock typedef M mutex_type; typedef typename M::scoped_timed_lock timed_lock_type; + static bool fake_predicate() + { + return false; + } + void operator()() { mutex_type mutex; @@ -123,14 +128,17 @@ struct test_timedlock BOOST_CHECK(lock ? true : false); // Construct and initialize an xtime for a fast time out. - boost::xtime xt = delay(0, 100); + boost::system_time timeout = boost::get_system_time()+boost::posix_time::milliseconds(100); // Test the lock and the mutex with condition variables. // No one is going to notify this condition variable. We expect to // time out. - BOOST_CHECK(!condition.timed_wait(lock, xt)); + BOOST_CHECK(!condition.timed_wait(lock, timeout, fake_predicate)); BOOST_CHECK(lock ? true : false); - BOOST_CHECK(in_range(xt)); + + boost::system_time now=boost::get_system_time(); + boost::posix_time::milliseconds const timeout_resolution(20); + BOOST_CHECK((now-timeout_resolution) Date: Fri, 7 Dec 2007 08:11:11 +0000 Subject: [PATCH 081/214] fixed order of comparison in timeout check [SVN r41819] --- test/test_mutex.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_mutex.cpp b/test/test_mutex.cpp index ed2c2571..5dae38ee 100644 --- a/test/test_mutex.cpp +++ b/test/test_mutex.cpp @@ -138,7 +138,7 @@ struct test_timedlock boost::system_time now=boost::get_system_time(); boost::posix_time::milliseconds const timeout_resolution(20); - BOOST_CHECK((now-timeout_resolution) Date: Sat, 15 Dec 2007 22:34:30 +0000 Subject: [PATCH 082/214] added timed_wait overloads that take a duration [SVN r42086] --- .../boost/thread/win32/condition_variable.hpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index 3a29ef1c..bfd1f1ff 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -276,6 +276,16 @@ namespace boost return do_wait(m,wait_until); } + bool timed_wait(unique_lock& m,boost::xtime const& wait_until) + { + return do_wait(m,system_time(wait_until)); + } + template + bool timed_wait(unique_lock& m,duration_type const& wait_duration) + { + return do_wait(m,wait_duration.total_milliseconds()); + } + template bool timed_wait(unique_lock& m,boost::system_time const& wait_until,predicate_type pred) { @@ -315,6 +325,18 @@ namespace boost return do_wait(m,wait_until); } + template + bool timed_wait(lock_type& m,boost::xtime const& wait_until) + { + return do_wait(m,system_time(wait_until)); + } + + template + bool timed_wait(lock_type& m,duration_type const& wait_duration) + { + return do_wait(m,wait_duration.total_milliseconds()); + } + template bool timed_wait(lock_type& m,boost::system_time const& wait_until,predicate_type pred) { From a89c4f01ad02b70cd76528bc61ae682f62addd55 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Sat, 15 Dec 2007 22:36:43 +0000 Subject: [PATCH 083/214] explicit move functions for threads, with a test [SVN r42087] --- include/boost/thread/detail/move.hpp | 6 ---- include/boost/thread/pthread/thread.hpp | 11 +++++++ include/boost/thread/win32/thread.hpp | 10 ++++++ test/Jamfile.v2 | 1 + test/test_move_function.cpp | 42 +++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 test/test_move_function.cpp diff --git a/include/boost/thread/detail/move.hpp b/include/boost/thread/detail/move.hpp index 4c90abc3..011ae4ea 100644 --- a/include/boost/thread/detail/move.hpp +++ b/include/boost/thread/detail/move.hpp @@ -23,12 +23,6 @@ namespace boost return &t; } }; - - template - thread_move_t thread_move(T& t) - { - return thread_move_t(t); - } } } diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index ad497230..f0df9476 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -169,6 +169,17 @@ namespace boost bool interruption_requested() const; }; + inline detail::thread_move_t move(thread& x) + { + return x.move(); + } + + inline detail::thread_move_t move(detail::thread_move_t x) + { + return x; + } + + template struct thread::thread_data >: detail::thread_data_base diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 14d93f83..574eacaa 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -245,6 +245,16 @@ namespace boost bool interruption_requested() const; }; + inline detail::thread_move_t move(thread& x) + { + return x.move(); + } + + inline detail::thread_move_t move(detail::thread_move_t x) + { + return x; + } + template struct thread::thread_data >: detail::thread_data_base diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 4ff2d48f..022e5574 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -36,6 +36,7 @@ rule thread-run ( sources ) test-suite "threads" : [ thread-run test_thread.cpp ] [ thread-run test_thread_move.cpp ] + [ thread-run test_move_function.cpp ] [ thread-run test_mutex.cpp ] [ thread-run test_condition_notify_one.cpp ] [ thread-run test_condition_timed_wait_times_out.cpp ] diff --git a/test/test_move_function.cpp b/test/test_move_function.cpp new file mode 100644 index 00000000..8e71dd7a --- /dev/null +++ b/test/test_move_function.cpp @@ -0,0 +1,42 @@ +// Copyright (C) 2007 Anthony Williams +// +// 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) +#include +#include + +void do_nothing() +{} + +void test_thread_move_from_lvalue_on_construction() +{ + boost::thread src(do_nothing); + boost::thread::id src_id=src.get_id(); + boost::thread dest=boost::move(src); + boost::thread::id dest_id=dest.get_id(); + BOOST_CHECK(src_id==dest_id); + BOOST_CHECK(src.get_id()==boost::thread::id()); + dest.join(); +} + +boost::thread make_thread() +{ + return boost::thread(do_nothing); +} + +void test_thread_move_from_function_return() +{ + boost::thread x=boost::move(make_thread()); + x.join(); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: thread move test suite"); + + test->add(BOOST_TEST_CASE(test_thread_move_from_lvalue_on_construction)); + test->add(BOOST_TEST_CASE(test_thread_move_from_function_return)); + return test; +} From df0197b617d40a5b3c9e86db6bccb62b4ecc28fd Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 17 Dec 2007 11:24:13 +0000 Subject: [PATCH 084/214] Updated move function test to be fair to Borland [SVN r42117] --- test/test_move_function.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/test/test_move_function.cpp b/test/test_move_function.cpp index 8e71dd7a..01c821bc 100644 --- a/test/test_move_function.cpp +++ b/test/test_move_function.cpp @@ -12,21 +12,17 @@ void test_thread_move_from_lvalue_on_construction() { boost::thread src(do_nothing); boost::thread::id src_id=src.get_id(); - boost::thread dest=boost::move(src); + boost::thread dest(boost::move(src)); boost::thread::id dest_id=dest.get_id(); BOOST_CHECK(src_id==dest_id); BOOST_CHECK(src.get_id()==boost::thread::id()); dest.join(); } -boost::thread make_thread() +void test_thread_move_from_rvalue() { - return boost::thread(do_nothing); -} - -void test_thread_move_from_function_return() -{ - boost::thread x=boost::move(make_thread()); + boost::thread x(boost::move(boost::thread(do_nothing))); + BOOST_CHECK(x.get_id()!=boost::thread::id()); x.join(); } @@ -37,6 +33,6 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) BOOST_TEST_SUITE("Boost.Threads: thread move test suite"); test->add(BOOST_TEST_CASE(test_thread_move_from_lvalue_on_construction)); - test->add(BOOST_TEST_CASE(test_thread_move_from_function_return)); + test->add(BOOST_TEST_CASE(test_thread_move_from_rvalue)); return test; } From 25ad6e3f8f1b6bffccb8232f7fb716e428cb5198 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 17 Dec 2007 12:52:50 +0000 Subject: [PATCH 085/214] boost::move support for locks [SVN r42118] --- include/boost/thread/locks.hpp | 78 ++++++++++++++++++++++++++++++++-- test/test_move_function.cpp | 20 ++++++++- 2 files changed, 93 insertions(+), 5 deletions(-) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 999b87f8..7d9e8693 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -90,9 +90,20 @@ namespace boost m(other->m),is_locked(other->is_locked) { other->is_locked=false; + other->m=0; } unique_lock(detail::thread_move_t > other); + operator detail::thread_move_t >() + { + return move(); + } + + detail::thread_move_t > move() + { + return detail::thread_move_t >(*this); + } + unique_lock& operator=(detail::thread_move_t > other) { unique_lock temp(other); @@ -196,6 +207,18 @@ namespace boost friend class upgrade_lock; }; + template + inline detail::thread_move_t > move(unique_lock & x) + { + return x.move(); + } + + template + inline detail::thread_move_t > move(detail::thread_move_t > x) + { + return x; + } + template class shared_lock { @@ -254,6 +277,17 @@ namespace boost } } + operator detail::thread_move_t >() + { + return move(); + } + + detail::thread_move_t > move() + { + return detail::thread_move_t >(*this); + } + + shared_lock& operator=(detail::thread_move_t > other) { shared_lock temp(other); @@ -341,6 +375,19 @@ namespace boost }; + template + inline detail::thread_move_t > move(shared_lock & x) + { + return x.move(); + } + + template + inline detail::thread_move_t > move(detail::thread_move_t > x) + { + return x; + } + + template class upgrade_lock { @@ -380,6 +427,17 @@ namespace boost } } + operator detail::thread_move_t >() + { + return move(); + } + + detail::thread_move_t > move() + { + return detail::thread_move_t >(*this); + } + + upgrade_lock& operator=(detail::thread_move_t > other) { upgrade_lock temp(other); @@ -452,6 +510,19 @@ namespace boost friend class unique_lock; }; + + template + inline detail::thread_move_t > move(upgrade_lock & x) + { + return x.move(); + } + + template + inline detail::thread_move_t > move(detail::thread_move_t > x) + { + return x; + } + template unique_lock::unique_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) @@ -474,18 +545,18 @@ namespace boost upgrade_to_unique_lock& operator=(upgrade_to_unique_lock&); public: explicit upgrade_to_unique_lock(upgrade_lock& m_): - source(&m_),exclusive(detail::thread_move(*source)) + source(&m_),exclusive(move(*source)) {} ~upgrade_to_unique_lock() { if(source) { - *source=detail::thread_move(exclusive); + *source=move(exclusive); } } upgrade_to_unique_lock(detail::thread_move_t > other): - source(other->source),exclusive(detail::thread_move(other->exclusive)) + source(other->source),exclusive(move(other->exclusive)) { other->source=0; } @@ -515,6 +586,7 @@ namespace boost return exclusive.owns_lock(); } }; + } #endif diff --git a/test/test_move_function.cpp b/test/test_move_function.cpp index 01c821bc..153a4cf1 100644 --- a/test/test_move_function.cpp +++ b/test/test_move_function.cpp @@ -4,6 +4,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include #include +#include void do_nothing() {} @@ -19,7 +20,7 @@ void test_thread_move_from_lvalue_on_construction() dest.join(); } -void test_thread_move_from_rvalue() +void test_thread_move_from_rvalue_on_construction() { boost::thread x(boost::move(boost::thread(do_nothing))); BOOST_CHECK(x.get_id()!=boost::thread::id()); @@ -27,12 +28,27 @@ void test_thread_move_from_rvalue() } +void test_unique_lock_move_from_lvalue_on_construction() +{ + boost::mutex m; + boost::unique_lock l(m); + BOOST_CHECK(l.owns_lock()); + BOOST_CHECK(l.mutex()==&m); + + boost::unique_lock l2(boost::move(l)); + BOOST_CHECK(!l.owns_lock()); + BOOST_CHECK(!l.mutex()); + BOOST_CHECK(l2.owns_lock()); + BOOST_CHECK(l2.mutex()==&m); +} + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = BOOST_TEST_SUITE("Boost.Threads: thread move test suite"); test->add(BOOST_TEST_CASE(test_thread_move_from_lvalue_on_construction)); - test->add(BOOST_TEST_CASE(test_thread_move_from_rvalue)); + test->add(BOOST_TEST_CASE(test_thread_move_from_rvalue_on_construction)); + test->add(BOOST_TEST_CASE(test_unique_lock_move_from_lvalue_on_construction)); return test; } From 713d0c7ace8148f5bd37f80c9eb9b92de4c5df90 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 19 Dec 2007 10:39:45 +0000 Subject: [PATCH 086/214] Updated thread ID, and added tests [SVN r42166] --- include/boost/thread/pthread/thread.hpp | 59 +++++--- include/boost/thread/pthread/thread_data.hpp | 9 +- include/boost/thread/win32/thread.hpp | 9 +- src/pthread/thread.cpp | 22 ++- test/Jamfile.v2 | 1 + test/test_thread_id.cpp | 147 +++++++++++++++++++ 6 files changed, 214 insertions(+), 33 deletions(-) create mode 100644 test/test_thread_id.cpp diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index f0df9476..24437ba6 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -40,44 +40,62 @@ namespace boost { class thread_id { - boost::optional id; - + private: + detail::thread_data_ptr thread_data; + + thread_id(detail::thread_data_ptr thread_data_): + thread_data(thread_data_) + {} friend class boost::thread; - friend thread_id this_thread::get_id(); - - thread_id(pthread_t id_): - id(id_) - {} - public: - thread_id() + thread_id(): + thread_data() {} - + bool operator==(const thread_id& y) const { - return (id && y.id) && (pthread_equal(*id,*y.id)!=0); + return thread_data==y.thread_data; } bool operator!=(const thread_id& y) const { - return !(*this==y); + return thread_data!=y.thread_data; + } + + bool operator<(const thread_id& y) const + { + return thread_data(const thread_id& y) const + { + return y.thread_data=(const thread_id& y) const + { + return !(thread_data friend std::basic_ostream& operator<<(std::basic_ostream& os, const thread_id& x) { - if(x.id) + if(x.thread_data) { - return os<<*x.id; + return os< thread_info; + detail::thread_data_ptr thread_info; void start_thread(); - explicit thread(boost::shared_ptr data); + explicit thread(detail::thread_data_ptr data); - boost::shared_ptr get_thread_info() const; + detail::thread_data_ptr get_thread_info() const; public: thread(); @@ -219,10 +237,7 @@ namespace boost ~restore_interruption(); }; - inline thread::id get_id() - { - return thread::id(pthread_self()); - } + BOOST_THREAD_DECL thread::id get_id(); BOOST_THREAD_DECL void interruption_point(); BOOST_THREAD_DECL bool interruption_enabled(); diff --git a/include/boost/thread/pthread/thread_data.hpp b/include/boost/thread/pthread/thread_data.hpp index f11035f1..234ba882 100644 --- a/include/boost/thread/pthread/thread_data.hpp +++ b/include/boost/thread/pthread/thread_data.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -21,10 +22,14 @@ namespace boost { struct thread_exit_callback_node; struct tss_data_node; + + struct thread_data_base; + typedef boost::shared_ptr thread_data_ptr; - struct thread_data_base + struct thread_data_base: + enable_shared_from_this { - boost::shared_ptr self; + thread_data_ptr self; pthread_t thread_handle; boost::mutex data_mutex; boost::condition_variable done_condition; diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 574eacaa..9ff339ec 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -364,7 +364,14 @@ namespace boost friend std::basic_ostream& operator<<(std::basic_ostream& os, const id& x) { - return os< thread::get_thread_info() const + detail::thread_data_ptr thread::get_thread_info() const { lock_guard l(thread_info_mutex); return thread_info; @@ -237,7 +237,7 @@ namespace boost void thread::join() { - boost::shared_ptr const local_thread_info=get_thread_info(); + detail::thread_data_ptr const local_thread_info=get_thread_info(); if(local_thread_info) { bool do_join=false; @@ -281,7 +281,7 @@ namespace boost bool thread::timed_join(system_time const& wait_until) { - boost::shared_ptr const local_thread_info=get_thread_info(); + detail::thread_data_ptr const local_thread_info=get_thread_info(); if(local_thread_info) { bool do_join=false; @@ -335,7 +335,7 @@ namespace boost void thread::detach() { - boost::shared_ptr local_thread_info; + detail::thread_data_ptr local_thread_info; { lock_guard l1(thread_info_mutex); thread_info.swap(local_thread_info); @@ -413,10 +413,10 @@ namespace boost thread::id thread::get_id() const { - boost::shared_ptr local_thread_info=get_thread_info(); + detail::thread_data_ptr const local_thread_info=get_thread_info(); if(local_thread_info) { - return id(local_thread_info->thread_handle); + return id(local_thread_info); } else { @@ -426,7 +426,7 @@ namespace boost void thread::interrupt() { - boost::shared_ptr local_thread_info=get_thread_info(); + detail::thread_data_ptr const local_thread_info=get_thread_info(); if(local_thread_info) { lock_guard lk(local_thread_info->data_mutex); @@ -440,7 +440,7 @@ namespace boost bool thread::interruption_requested() const { - boost::shared_ptr local_thread_info=get_thread_info(); + detail::thread_data_ptr const local_thread_info=get_thread_info(); if(local_thread_info) { lock_guard lk(local_thread_info->data_mutex); @@ -455,6 +455,12 @@ namespace boost namespace this_thread { + thread::id get_id() + { + boost::detail::thread_data_base* const thread_info=get_or_make_current_thread_data(); + return thread::id(thread_info?thread_info->shared_from_this():detail::thread_data_ptr()); + } + void interruption_point() { boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data(); diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 022e5574..85eb14c3 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -35,6 +35,7 @@ rule thread-run ( sources ) { test-suite "threads" : [ thread-run test_thread.cpp ] + [ thread-run test_thread_id.cpp ] [ thread-run test_thread_move.cpp ] [ thread-run test_move_function.cpp ] [ thread-run test_mutex.cpp ] diff --git a/test/test_thread_id.cpp b/test/test_thread_id.cpp new file mode 100644 index 00000000..117c78c2 --- /dev/null +++ b/test/test_thread_id.cpp @@ -0,0 +1,147 @@ +// Copyright (C) 2007 Anthony Williams +// +// 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) +#include +#include +#include + +void do_nothing() +{} + +void test_thread_id_for_default_constructed_thread_is_default_constructed_id() +{ + boost::thread t; + BOOST_CHECK(t.get_id()==boost::thread::id()); +} + +void test_thread_id_for_running_thread_is_not_default_constructed_id() +{ + boost::thread t(do_nothing); + BOOST_CHECK(t.get_id()!=boost::thread::id()); + t.join(); +} + +void test_different_threads_have_different_ids() +{ + boost::thread t(do_nothing); + boost::thread t2(do_nothing); + BOOST_CHECK(t.get_id()!=t2.get_id()); + t.join(); + t2.join(); +} + +void test_thread_ids_have_a_total_order() +{ + boost::thread t(do_nothing); + boost::thread t2(do_nothing); + boost::thread t3(do_nothing); + BOOST_CHECK(t.get_id()!=t2.get_id()); + BOOST_CHECK(t.get_id()!=t3.get_id()); + BOOST_CHECK(t2.get_id()!=t3.get_id()); + + BOOST_CHECK((t.get_id()t2.get_id()) != (t2.get_id()>t.get_id())); + BOOST_CHECK((t.get_id()>t3.get_id()) != (t3.get_id()>t.get_id())); + BOOST_CHECK((t2.get_id()>t3.get_id()) != (t3.get_id()>t2.get_id())); + + BOOST_CHECK((t.get_id()t.get_id())); + BOOST_CHECK((t2.get_id()t2.get_id())); + BOOST_CHECK((t.get_id()t.get_id())); + BOOST_CHECK((t3.get_id()t3.get_id())); + BOOST_CHECK((t2.get_id()t2.get_id())); + BOOST_CHECK((t3.get_id()t3.get_id())); + + BOOST_CHECK((t.get_id()=t.get_id())); + BOOST_CHECK((t2.get_id()=t2.get_id())); + BOOST_CHECK((t.get_id()=t.get_id())); + BOOST_CHECK((t3.get_id()=t3.get_id())); + BOOST_CHECK((t2.get_id()=t2.get_id())); + BOOST_CHECK((t3.get_id()=t3.get_id())); + + BOOST_CHECK((t.get_id()<=t2.get_id()) == (t2.get_id()>t.get_id())); + BOOST_CHECK((t2.get_id()<=t.get_id()) == (t.get_id()>t2.get_id())); + BOOST_CHECK((t.get_id()<=t3.get_id()) == (t3.get_id()>t.get_id())); + BOOST_CHECK((t3.get_id()<=t.get_id()) == (t.get_id()>t3.get_id())); + BOOST_CHECK((t2.get_id()<=t3.get_id()) == (t3.get_id()>t2.get_id())); + BOOST_CHECK((t3.get_id()<=t2.get_id()) == (t2.get_id()>t3.get_id())); + + if((t.get_id() t.get_id())); + BOOST_CHECK(!(boost::thread::id() > t2.get_id())); + BOOST_CHECK(!(boost::thread::id() > t2.get_id())); + + BOOST_CHECK(!(boost::thread::id() >= t.get_id())); + BOOST_CHECK(!(boost::thread::id() >= t2.get_id())); + BOOST_CHECK(!(boost::thread::id() >= t2.get_id())); + + t.join(); + t2.join(); + t3.join(); +} + +void get_thread_id(boost::thread::id* id) +{ + *id=boost::this_thread::get_id(); +} + +void test_thread_id_of_running_thread_returned_by_this_thread_get_id() +{ + boost::thread::id id; + boost::thread t(boost::bind(get_thread_id,&id)); + boost::thread::id t_id=t.get_id(); + t.join(); + BOOST_CHECK(id==t_id); +} + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: thread move test suite"); + + test->add(BOOST_TEST_CASE(test_thread_id_for_default_constructed_thread_is_default_constructed_id)); + test->add(BOOST_TEST_CASE(test_thread_id_for_running_thread_is_not_default_constructed_id)); + test->add(BOOST_TEST_CASE(test_different_threads_have_different_ids)); + test->add(BOOST_TEST_CASE(test_thread_ids_have_a_total_order)); + test->add(BOOST_TEST_CASE(test_thread_id_of_running_thread_returned_by_this_thread_get_id)); + return test; +} From 82e503339be053415436000be04207a6c13bb098 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 19 Dec 2007 10:45:01 +0000 Subject: [PATCH 087/214] Implement hardware_concurrency for pthread [SVN r42168] --- src/pthread/thread.cpp | 11 ++++++++++- test/Jamfile.v2 | 1 + test/test_hardware_concurrency.cpp | 21 +++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 test/test_hardware_concurrency.cpp diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 9f11f6d9..a91fd348 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -13,6 +13,9 @@ #include #include #include +#ifdef __linux__ +#include +#endif #include "timeconv.inl" @@ -408,7 +411,13 @@ namespace boost unsigned thread::hardware_concurrency() { - return 1; +#if defined(PTW32_VERSION) || defined(__hpux) + return pthread_num_processors_np(); +#elif defined(__linux__) + return get_nprocs; +#else + return 0; +#endif } thread::id thread::get_id() const diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 85eb14c3..d9e4bcb9 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -36,6 +36,7 @@ rule thread-run ( sources ) test-suite "threads" : [ thread-run test_thread.cpp ] [ thread-run test_thread_id.cpp ] + [ thread-run test_hardware_concurrency.cpp ] [ thread-run test_thread_move.cpp ] [ thread-run test_move_function.cpp ] [ thread-run test_mutex.cpp ] diff --git a/test/test_hardware_concurrency.cpp b/test/test_hardware_concurrency.cpp new file mode 100644 index 00000000..7c1c148b --- /dev/null +++ b/test/test_hardware_concurrency.cpp @@ -0,0 +1,21 @@ +// Copyright (C) 2007 Anthony Williams +// +// 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) +#include +#include +#include + +void test_hardware_concurrency_is_non_zero() +{ + BOOST_CHECK(boost::thread::hardware_concurrency()!=0); +} + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: hardware concurrency test suite"); + + test->add(BOOST_TEST_CASE(test_hardware_concurrency_is_non_zero)); + return test; +} From 56ded87ad2b58c45665fb2b01c42aebd391a7fe6 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 20 Dec 2007 07:46:00 +0000 Subject: [PATCH 088/214] added missing parentheses [SVN r42194] --- src/pthread/thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index a91fd348..fc6c58c0 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -414,7 +414,7 @@ namespace boost #if defined(PTW32_VERSION) || defined(__hpux) return pthread_num_processors_np(); #elif defined(__linux__) - return get_nprocs; + return get_nprocs(); #else return 0; #endif From 7afd9efcc544a73b47b1ec32bd3576c9ca0fd536 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 20 Dec 2007 08:37:02 +0000 Subject: [PATCH 089/214] added hardware_concurrency support for apple, freebsd and sun platforms [SVN r42195] --- src/pthread/thread.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index fc6c58c0..0add81c6 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -15,6 +15,11 @@ #include #ifdef __linux__ #include +#elif defined(__APPLE__) || defined(__FreeBSD__) +#include +#include +#elif defined(__sun) +#include #endif #include "timeconv.inl" @@ -415,6 +420,13 @@ namespace boost return pthread_num_processors_np(); #elif defined(__linux__) return get_nprocs(); +#elif defined(__APPLE__) || defined(__FreeBSD__) + int count; + size_t size=sizeof(count); + return sysctlbyname("hw.ncpu",&count,&size,NULL,0)?0:count; +#elif defined(__sun) + int const count=sysconf(_SC_NPROCESSORS_ONLN); + return (count>0)?count:0; #else return 0; #endif From c8d8a108a7a57336c441bac245d46dcc5fdb444e Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 21 Dec 2007 10:54:59 +0000 Subject: [PATCH 090/214] Updated thread ID test [SVN r42228] --- test/test_thread_id.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/test/test_thread_id.cpp b/test/test_thread_id.cpp index 117c78c2..777bc718 100644 --- a/test/test_thread_id.cpp +++ b/test/test_thread_id.cpp @@ -98,21 +98,23 @@ void test_thread_ids_have_a_total_order() BOOST_CHECK(false); } - BOOST_CHECK(boost::thread::id() < t.get_id()); - BOOST_CHECK(boost::thread::id() < t2.get_id()); - BOOST_CHECK(boost::thread::id() < t3.get_id()); + boost::thread::id default_id; - BOOST_CHECK(boost::thread::id() <= t.get_id()); - BOOST_CHECK(boost::thread::id() <= t2.get_id()); - BOOST_CHECK(boost::thread::id() <= t3.get_id()); + BOOST_CHECK(default_id < t.get_id()); + BOOST_CHECK(default_id < t2.get_id()); + BOOST_CHECK(default_id < t3.get_id()); - BOOST_CHECK(!(boost::thread::id() > t.get_id())); - BOOST_CHECK(!(boost::thread::id() > t2.get_id())); - BOOST_CHECK(!(boost::thread::id() > t2.get_id())); + BOOST_CHECK(default_id <= t.get_id()); + BOOST_CHECK(default_id <= t2.get_id()); + BOOST_CHECK(default_id <= t3.get_id()); + + BOOST_CHECK(!(default_id > t.get_id())); + BOOST_CHECK(!(default_id > t2.get_id())); + BOOST_CHECK(!(default_id > t3.get_id())); - BOOST_CHECK(!(boost::thread::id() >= t.get_id())); - BOOST_CHECK(!(boost::thread::id() >= t2.get_id())); - BOOST_CHECK(!(boost::thread::id() >= t2.get_id())); + BOOST_CHECK(!(default_id >= t.get_id())); + BOOST_CHECK(!(default_id >= t2.get_id())); + BOOST_CHECK(!(default_id >= t3.get_id())); t.join(); t2.join(); From ab01ab1e4d98a28810b39f698816b8c57d313377 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 10 Jan 2008 14:19:36 +0000 Subject: [PATCH 091/214] removed references to NULL [SVN r42657] --- include/boost/thread/win32/once.hpp | 4 ++-- include/boost/thread/win32/shared_mutex.hpp | 6 +++--- include/boost/thread/win32/thread_primitives.hpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/boost/thread/win32/once.hpp b/include/boost/thread/win32/once.hpp index d1a525de..b11be97a 100644 --- a/include/boost/thread/win32/once.hpp +++ b/include/boost/thread/win32/once.hpp @@ -94,9 +94,9 @@ namespace boost detail::int_to_string(win32::GetCurrentProcessId(), mutex_name + once_mutex_name_fixed_length + sizeof(void*)*2); #ifdef BOOST_NO_ANSI_APIS - return win32::CreateMutexW(NULL, 0, mutex_name); + return win32::CreateMutexW(0, 0, mutex_name); #else - return win32::CreateMutexA(NULL, 0, mutex_name); + return win32::CreateMutexA(0, 0, mutex_name); #endif } diff --git a/include/boost/thread/win32/shared_mutex.hpp b/include/boost/thread/win32/shared_mutex.hpp index 73cdc490..3b322b1f 100644 --- a/include/boost/thread/win32/shared_mutex.hpp +++ b/include/boost/thread/win32/shared_mutex.hpp @@ -57,12 +57,12 @@ namespace boost { if(old_state.exclusive_waiting) { - BOOST_VERIFY(detail::win32::ReleaseSemaphore(exclusive_sem,1,NULL)!=0); + BOOST_VERIFY(detail::win32::ReleaseSemaphore(exclusive_sem,1,0)!=0); } if(old_state.shared_waiting || old_state.exclusive_waiting) { - BOOST_VERIFY(detail::win32::ReleaseSemaphore(unlock_sem,old_state.shared_waiting + (old_state.exclusive_waiting?1:0),NULL)!=0); + BOOST_VERIFY(detail::win32::ReleaseSemaphore(unlock_sem,old_state.shared_waiting + (old_state.exclusive_waiting?1:0),0)!=0); } } @@ -215,7 +215,7 @@ namespace boost { if(old_state.upgrade) { - BOOST_VERIFY(detail::win32::ReleaseSemaphore(upgrade_sem,1,NULL)!=0); + BOOST_VERIFY(detail::win32::ReleaseSemaphore(upgrade_sem,1,0)!=0); } else { diff --git a/include/boost/thread/win32/thread_primitives.hpp b/include/boost/thread/win32/thread_primitives.hpp index c8d55d29..f8f66b15 100644 --- a/include/boost/thread/win32/thread_primitives.hpp +++ b/include/boost/thread/win32/thread_primitives.hpp @@ -181,9 +181,9 @@ namespace boost inline handle create_anonymous_semaphore(long initial_count,long max_count) { #if !defined(BOOST_NO_ANSI_APIS) - handle const res=CreateSemaphoreA(NULL,initial_count,max_count,NULL); + handle const res=CreateSemaphoreA(0,initial_count,max_count,0); #else - handle const res=CreateSemaphoreW(NULL,initial_count,max_count,NULL); + handle const res=CreateSemaphoreW(0,initial_count,max_count,0); #endif if(!res) { From eef695bdf0a7b0dfab15250fefd96d661cd609dc Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 16 Jan 2008 15:23:36 +0000 Subject: [PATCH 092/214] Provide tss_cleanup_implemented as a dummy function on Windows CE to allow tests to run [SVN r42818] --- src/tss_null.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tss_null.cpp b/src/tss_null.cpp index 3288c679..ff13b30f 100644 --- a/src/tss_null.cpp +++ b/src/tss_null.cpp @@ -6,7 +6,7 @@ #include -#if defined(BOOST_HAS_WINTHREADS) && (defined(BOOST_THREAD_BUILD_LIB) || defined(BOOST_THREAD_TEST)) && (!defined(_MSC_VER) || defined(UNDER_CE)) +#if defined(BOOST_HAS_WINTHREADS) && (defined(BOOST_THREAD_BUILD_LIB) || defined(BOOST_THREAD_TEST) || defined(UNDER_CE)) && (!defined(_MSC_VER) || defined(UNDER_CE)) /* This file is a "null" implementation of tss cleanup; it's From 3809321037bd74b454ecc7f0d5a8f606844e3353 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 4 Feb 2008 13:16:32 +0000 Subject: [PATCH 093/214] added test for duration overloads of timed_lock, and added missing implementation to win32 version [SVN r43094] --- include/boost/thread/win32/basic_recursive_mutex.hpp | 6 ++++++ include/boost/thread/win32/basic_timed_mutex.hpp | 6 ++++++ test/test_mutex.cpp | 11 +++++++++++ 3 files changed, 23 insertions(+) diff --git a/include/boost/thread/win32/basic_recursive_mutex.hpp b/include/boost/thread/win32/basic_recursive_mutex.hpp index e5f79981..d2b8e77d 100644 --- a/include/boost/thread/win32/basic_recursive_mutex.hpp +++ b/include/boost/thread/win32/basic_recursive_mutex.hpp @@ -56,6 +56,12 @@ namespace boost long const current_thread_id=win32::GetCurrentThreadId(); return try_recursive_lock(current_thread_id) || try_timed_lock(current_thread_id,target); } + template + bool timed_lock(Duration const& timeout) + { + return timed_lock(get_system_time()+timeout); + } + long get_active_count() { return mutex.get_active_count(); diff --git a/include/boost/thread/win32/basic_timed_mutex.hpp b/include/boost/thread/win32/basic_timed_mutex.hpp index fef19d7b..8ee199df 100644 --- a/include/boost/thread/win32/basic_timed_mutex.hpp +++ b/include/boost/thread/win32/basic_timed_mutex.hpp @@ -104,6 +104,12 @@ namespace boost return true; } + template + bool timed_lock(Duration const& timeout) + { + return timed_lock(get_system_time()+timeout); + } + long get_active_count() { return ::boost::detail::interlocked_read_acquire(&active_count); diff --git a/test/test_mutex.cpp b/test/test_mutex.cpp index 5dae38ee..e85459c4 100644 --- a/test/test_mutex.cpp +++ b/test/test_mutex.cpp @@ -150,6 +150,17 @@ struct test_timedlock boost::system_time target = boost::get_system_time()+boost::posix_time::milliseconds(100); BOOST_CHECK(lock.timed_lock(target)); BOOST_CHECK(lock ? true : false); + lock.unlock(); + BOOST_CHECK(!lock); + + BOOST_CHECK(mutex.timed_lock(boost::posix_time::milliseconds(100))); + mutex.unlock(); + + BOOST_CHECK(lock.timed_lock(boost::posix_time::milliseconds(100))); + BOOST_CHECK(lock ? true : false); + lock.unlock(); + BOOST_CHECK(!lock); + } }; From 6a0d3e98bc68d2e970afaea83c8db9e38a51fbf9 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Wed, 27 Feb 2008 18:51:14 +0000 Subject: [PATCH 094/214] Fix broken copyright urls. Fixes #1573. [SVN r43422] --- doc/acknowledgements.xml | 2 +- doc/barrier-ref.xml | 2 +- doc/bibliography.xml | 2 +- doc/build.xml | 2 +- doc/concepts.xml | 2 +- doc/condition-ref.xml | 2 +- doc/configuration.xml | 2 +- doc/design.xml | 2 +- doc/entities.xml | 2 +- doc/exceptions-ref.xml | 2 +- doc/faq.xml | 2 +- doc/glossary.xml | 2 +- doc/implementation_notes.xml | 2 +- doc/index.html | 2 +- doc/mutex-ref.xml | 2 +- doc/once-ref.xml | 2 +- doc/overview.xml | 2 +- doc/rationale.xml | 2 +- doc/read_write_mutex-ref.xml | 2 +- doc/recursive_mutex-ref.xml | 2 +- doc/reference.xml | 2 +- doc/release_notes.xml | 2 +- doc/thread-ref.xml | 2 +- doc/thread.xml | 4 ++-- doc/tss-ref.xml | 2 +- doc/xtime-ref.xml | 2 +- index.html | 2 +- 27 files changed, 28 insertions(+), 28 deletions(-) diff --git a/doc/acknowledgements.xml b/doc/acknowledgements.xml index 6e8c610a..60ad9198 100644 --- a/doc/acknowledgements.xml +++ b/doc/acknowledgements.xml @@ -6,7 +6,7 @@ ]>
diff --git a/doc/barrier-ref.xml b/doc/barrier-ref.xml index 5f4073f9..10b5519b 100644 --- a/doc/barrier-ref.xml +++ b/doc/barrier-ref.xml @@ -6,7 +6,7 @@ ]>
diff --git a/doc/bibliography.xml b/doc/bibliography.xml index e604f384..458d6392 100644 --- a/doc/bibliography.xml +++ b/doc/bibliography.xml @@ -6,7 +6,7 @@ ]> diff --git a/doc/build.xml b/doc/build.xml index bed1c7a7..7f426771 100644 --- a/doc/build.xml +++ b/doc/build.xml @@ -7,7 +7,7 @@
Build diff --git a/doc/concepts.xml b/doc/concepts.xml index 2004236c..30386f70 100644 --- a/doc/concepts.xml +++ b/doc/concepts.xml @@ -6,7 +6,7 @@ ]>
Concepts diff --git a/doc/condition-ref.xml b/doc/condition-ref.xml index b5882d27..51b3b1d0 100644 --- a/doc/condition-ref.xml +++ b/doc/condition-ref.xml @@ -6,7 +6,7 @@ ]>
diff --git a/doc/configuration.xml b/doc/configuration.xml index e54542a4..bb42d665 100644 --- a/doc/configuration.xml +++ b/doc/configuration.xml @@ -6,7 +6,7 @@ ]>
Configuration diff --git a/doc/design.xml b/doc/design.xml index b1d4d51a..c9b58480 100644 --- a/doc/design.xml +++ b/doc/design.xml @@ -6,7 +6,7 @@ ]>
Design diff --git a/doc/entities.xml b/doc/entities.xml index a056e193..fbfa6d21 100644 --- a/doc/entities.xml +++ b/doc/entities.xml @@ -1,6 +1,6 @@ Boost"> Boost.Thread"> diff --git a/doc/exceptions-ref.xml b/doc/exceptions-ref.xml index 4af5bd9b..0ae42e8b 100644 --- a/doc/exceptions-ref.xml +++ b/doc/exceptions-ref.xml @@ -6,7 +6,7 @@ ]>
diff --git a/doc/faq.xml b/doc/faq.xml index 96c828b9..482d6811 100644 --- a/doc/faq.xml +++ b/doc/faq.xml @@ -6,7 +6,7 @@ ]>
Frequently Asked Questions diff --git a/doc/glossary.xml b/doc/glossary.xml index 26ead3c4..ad1c8442 100644 --- a/doc/glossary.xml +++ b/doc/glossary.xml @@ -6,7 +6,7 @@ ]> Glossary diff --git a/doc/implementation_notes.xml b/doc/implementation_notes.xml index 03377461..c097b070 100644 --- a/doc/implementation_notes.xml +++ b/doc/implementation_notes.xml @@ -6,7 +6,7 @@ ]>
Implementation Notes diff --git a/doc/index.html b/doc/index.html index 94d75801..4d1b5db3 100644 --- a/doc/index.html +++ b/doc/index.html @@ -1,6 +1,6 @@ diff --git a/doc/mutex-ref.xml b/doc/mutex-ref.xml index 34d8b17e..5faa2630 100644 --- a/doc/mutex-ref.xml +++ b/doc/mutex-ref.xml @@ -6,7 +6,7 @@ ]>
diff --git a/doc/once-ref.xml b/doc/once-ref.xml index 12480f57..d6520178 100644 --- a/doc/once-ref.xml +++ b/doc/once-ref.xml @@ -6,7 +6,7 @@ ]>
diff --git a/doc/overview.xml b/doc/overview.xml index 545bf1a5..1e639597 100644 --- a/doc/overview.xml +++ b/doc/overview.xml @@ -6,7 +6,7 @@ ]>
Overview diff --git a/doc/rationale.xml b/doc/rationale.xml index 8a77d99b..aeaf8511 100644 --- a/doc/rationale.xml +++ b/doc/rationale.xml @@ -6,7 +6,7 @@ ]>
Rationale diff --git a/doc/read_write_mutex-ref.xml b/doc/read_write_mutex-ref.xml index 46336961..747960fb 100644 --- a/doc/read_write_mutex-ref.xml +++ b/doc/read_write_mutex-ref.xml @@ -6,7 +6,7 @@ ]>
diff --git a/doc/recursive_mutex-ref.xml b/doc/recursive_mutex-ref.xml index 9a9d36d2..de95494a 100644 --- a/doc/recursive_mutex-ref.xml +++ b/doc/recursive_mutex-ref.xml @@ -6,7 +6,7 @@ ]>
diff --git a/doc/reference.xml b/doc/reference.xml index a9167d02..d6ea4d75 100644 --- a/doc/reference.xml +++ b/doc/reference.xml @@ -6,7 +6,7 @@ ]>
Release Notes diff --git a/doc/thread-ref.xml b/doc/thread-ref.xml index ad33271b..270302ca 100644 --- a/doc/thread-ref.xml +++ b/doc/thread-ref.xml @@ -6,7 +6,7 @@ ]>
diff --git a/doc/thread.xml b/doc/thread.xml index 209f48f9..f5c30883 100644 --- a/doc/thread.xml +++ b/doc/thread.xml @@ -6,7 +6,7 @@ ]> Subject to the Boost Software License, Version 1.0. - (See accompanying file LICENSE-1.0 or http://www.boost.org/LICENSE-1.0) + (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) Portable C++ multi-threading diff --git a/doc/tss-ref.xml b/doc/tss-ref.xml index 54399d4c..faacad1f 100644 --- a/doc/tss-ref.xml +++ b/doc/tss-ref.xml @@ -6,7 +6,7 @@ ]>
diff --git a/doc/xtime-ref.xml b/doc/xtime-ref.xml index de7fd999..566a28d1 100644 --- a/doc/xtime-ref.xml +++ b/doc/xtime-ref.xml @@ -6,7 +6,7 @@ ]>
diff --git a/index.html b/index.html index 1bc26cab..f54e21e2 100644 --- a/index.html +++ b/index.html @@ -1,6 +1,6 @@ From 6c22bdb3bda899b4bd31c4cdad79e69a038bb3c1 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 3 Mar 2008 08:44:42 +0000 Subject: [PATCH 095/214] Test and fix for issue #1665 [SVN r43461] --- include/boost/thread/pthread/tss.hpp | 2 +- include/boost/thread/win32/tss.hpp | 2 +- test/test_tss.cpp | 42 ++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/include/boost/thread/pthread/tss.hpp b/include/boost/thread/pthread/tss.hpp index 36c385c5..626f1c30 100644 --- a/include/boost/thread/pthread/tss.hpp +++ b/include/boost/thread/pthread/tss.hpp @@ -52,7 +52,7 @@ namespace boost void operator()(void* data) { - cleanup_function(data); + cleanup_function(static_cast(data)); } }; diff --git a/include/boost/thread/win32/tss.hpp b/include/boost/thread/win32/tss.hpp index fcf792bd..91870f14 100644 --- a/include/boost/thread/win32/tss.hpp +++ b/include/boost/thread/win32/tss.hpp @@ -51,7 +51,7 @@ namespace boost void operator()(void* data) { - cleanup_function(data); + cleanup_function(static_cast(data)); } }; diff --git a/test/test_tss.cpp b/test/test_tss.cpp index 462c5523..8c46d3db 100644 --- a/test/test_tss.cpp +++ b/test/test_tss.cpp @@ -187,12 +187,54 @@ void test_tss() timed_test(&do_test_tss, 2); } +bool tss_cleanup_called=false; + +struct Dummy +{}; + +void tss_custom_cleanup(Dummy* d) +{ + delete d; + tss_cleanup_called=true; +} + +boost::thread_specific_ptr tss_with_cleanup(tss_custom_cleanup); + +void tss_thread_with_custom_cleanup() +{ + tss_with_cleanup.reset(new Dummy); +} + +void do_test_tss_with_custom_cleanup() +{ + boost::thread t(tss_thread_with_custom_cleanup); + try + { + t.join(); + } + catch(...) + { + t.interrupt(); + t.join(); + throw; + } + + BOOST_CHECK(tss_cleanup_called); +} + + +void test_tss_with_custom_cleanup() +{ + timed_test(&do_test_tss_with_custom_cleanup, 2); +} + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = BOOST_TEST_SUITE("Boost.Threads: tss test suite"); test->add(BOOST_TEST_CASE(test_tss)); + test->add(BOOST_TEST_CASE(test_tss_with_custom_cleanup)); return test; } From 810306b8f32924b69736eb3861ab5fc894de2888 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 3 Mar 2008 10:52:44 +0000 Subject: [PATCH 096/214] thread constructor now accepts up to three additional arguments to pass to thread function [SVN r43464] --- include/boost/thread/pthread/thread.hpp | 30 ++++- include/boost/thread/win32/thread.hpp | 30 ++++- test/Jamfile.v2 | 1 + test/test_thread_launching.cpp | 164 ++++++++++++++++++++++++ 4 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 test/test_thread_launching.cpp diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index 24437ba6..f539d568 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -21,6 +21,7 @@ #include #include #include "thread_data.hpp" +#include namespace boost { @@ -133,6 +134,12 @@ namespace boost explicit thread(detail::thread_data_ptr data); detail::thread_data_ptr get_thread_info() const; + + template + static inline detail::thread_data_ptr make_thread_info(F f) + { + return detail::thread_data_ptr(new thread_data(f)); + } public: thread(); @@ -140,13 +147,32 @@ namespace boost template explicit thread(F f): - thread_info(new thread_data(f)) + thread_info(make_thread_info(f)) { start_thread(); } template thread(detail::thread_move_t f): - thread_info(new thread_data(f)) + thread_info(make_thread_info(f)) + { + start_thread(); + } + + template + thread(F f,A1 a1): + thread_info(make_thread_info(boost::bind(f,a1))) + { + start_thread(); + } + template + thread(F f,A1 a1,A2 a2): + thread_info(make_thread_info(boost::bind(f,a1,a2))) + { + start_thread(); + } + template + thread(F f,A1 a1,A2 a2,A3 a3): + thread_info(make_thread_info(boost::bind(f,a1,a2,a3))) { start_thread(); } diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 9ff339ec..61c2908a 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace boost { @@ -189,19 +190,44 @@ namespace boost explicit thread(detail::thread_data_ptr data); detail::thread_data_ptr get_thread_info() const; + + template + static inline detail::thread_data_ptr make_thread_info(F f) + { + return detail::heap_new >(f); + } public: thread(); ~thread(); template explicit thread(F f): - thread_info(detail::heap_new >(f)) + thread_info(make_thread_info(f)) { start_thread(); } template thread(detail::thread_move_t f): - thread_info(detail::heap_new >(f)) + thread_info(make_thread_info(f)) + { + start_thread(); + } + + template + thread(F f,A1 a1): + thread_info(make_thread_info(boost::bind(f,a1))) + { + start_thread(); + } + template + thread(F f,A1 a1,A2 a2): + thread_info(make_thread_info(boost::bind(f,a1,a2))) + { + start_thread(); + } + template + thread(F f,A1 a1,A2 a2,A3 a3): + thread_info(make_thread_info(boost::bind(f,a1,a2,a3))) { start_thread(); } diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index d9e4bcb9..0145cf5b 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -38,6 +38,7 @@ rule thread-run ( sources ) [ thread-run test_thread_id.cpp ] [ thread-run test_hardware_concurrency.cpp ] [ thread-run test_thread_move.cpp ] + [ thread-run test_thread_launching.cpp ] [ thread-run test_move_function.cpp ] [ thread-run test_mutex.cpp ] [ thread-run test_condition_notify_one.cpp ] diff --git a/test/test_thread_launching.cpp b/test/test_thread_launching.cpp new file mode 100644 index 00000000..28903dbc --- /dev/null +++ b/test/test_thread_launching.cpp @@ -0,0 +1,164 @@ +// Copyright (C) 2007 Anthony Williams +// +// 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) +#include +#include +#include +#include +#include +#include + +bool normal_function_called=false; + +void normal_function() +{ + normal_function_called=true; +} + +void test_thread_function_no_arguments() +{ + boost::thread function(normal_function); + function.join(); + BOOST_CHECK(normal_function_called); +} + +struct callable_no_args +{ + static bool called; + + void operator()() const + { + called=true; + } +}; + +bool callable_no_args::called=false; + +void test_thread_callable_object_no_arguments() +{ + boost::thread callable((callable_no_args())); + callable.join(); + BOOST_CHECK(callable_no_args::called); +} + +struct callable_noncopyable_no_args: + boost::noncopyable +{ + static bool called; + + void operator()() const + { + called=true; + } +}; + +bool callable_noncopyable_no_args::called=false; + +void test_thread_callable_object_ref_no_arguments() +{ + callable_noncopyable_no_args func; + + boost::thread callable(boost::ref(func)); + callable.join(); + BOOST_CHECK(callable_noncopyable_no_args::called); +} + +struct callable_one_arg +{ + static bool called; + static int called_arg; + + void operator()(int arg) const + { + called=true; + called_arg=arg; + } +}; + +bool callable_one_arg::called=false; +int callable_one_arg::called_arg=0; + +void test_thread_callable_object_one_argument() +{ + boost::thread callable(callable_one_arg(),42); + callable.join(); + BOOST_CHECK(callable_one_arg::called); + BOOST_CHECK_EQUAL(callable_one_arg::called_arg,42); +} + +struct callable_multiple_arg +{ + static bool called_two; + static int called_two_arg1; + static double called_two_arg2; + static bool called_three; + static std::string called_three_arg1; + static std::vector called_three_arg2; + static int called_three_arg3; + + void operator()(int arg1,double arg2) const + { + called_two=true; + called_two_arg1=arg1; + called_two_arg2=arg2; + } + void operator()(std::string const& arg1,std::vector const& arg2,int arg3) const + { + called_three=true; + called_three_arg1=arg1; + called_three_arg2=arg2; + called_three_arg3=arg3; + } +}; + +bool callable_multiple_arg::called_two=false; +bool callable_multiple_arg::called_three=false; +int callable_multiple_arg::called_two_arg1; +double callable_multiple_arg::called_two_arg2; +std::string callable_multiple_arg::called_three_arg1; +std::vector callable_multiple_arg::called_three_arg2; +int callable_multiple_arg::called_three_arg3; + +void test_thread_callable_object_multiple_arguments() +{ + std::vector x; + for(unsigned i=0;i<7;++i) + { + x.push_back(i*i); + } + + boost::thread callable3(callable_multiple_arg(),"hello",x,1.2); + callable3.join(); + BOOST_CHECK(callable_multiple_arg::called_three); + BOOST_CHECK_EQUAL(callable_multiple_arg::called_three_arg1,"hello"); + BOOST_CHECK_EQUAL(callable_multiple_arg::called_three_arg2.size(),x.size()); + for(unsigned j=0;jadd(BOOST_TEST_CASE(test_thread_function_no_arguments)); + test->add(BOOST_TEST_CASE(test_thread_callable_object_no_arguments)); + test->add(BOOST_TEST_CASE(test_thread_callable_object_ref_no_arguments)); + test->add(BOOST_TEST_CASE(test_thread_callable_object_one_argument)); + test->add(BOOST_TEST_CASE(test_thread_callable_object_multiple_arguments)); + return test; +} From a76c33f8cc355d40f36b273dda719bc4073e1b56 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 6 Mar 2008 07:59:16 +0000 Subject: [PATCH 097/214] made the callable_no_args function object a named object rather than a temporary, in order to avoid gratuitous breakage on some compilers [SVN r43528] --- test/test_thread_launching.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_thread_launching.cpp b/test/test_thread_launching.cpp index 28903dbc..0e03d69d 100644 --- a/test/test_thread_launching.cpp +++ b/test/test_thread_launching.cpp @@ -37,7 +37,8 @@ bool callable_no_args::called=false; void test_thread_callable_object_no_arguments() { - boost::thread callable((callable_no_args())); + callable_no_args func; + boost::thread callable(func); callable.join(); BOOST_CHECK(callable_no_args::called); } From b5bbb7fb1c8d40d9ac042d1654e4d6470a85a473 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 17 Mar 2008 08:36:09 +0000 Subject: [PATCH 098/214] Test and fix for bug #1693 to ensure thread_specific_ptr works as desired [SVN r43666] --- include/boost/thread/pthread/tss.hpp | 2 +- include/boost/thread/win32/tss.hpp | 2 +- test/test_tss.cpp | 36 ++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/include/boost/thread/pthread/tss.hpp b/include/boost/thread/pthread/tss.hpp index 626f1c30..8d3bfd36 100644 --- a/include/boost/thread/pthread/tss.hpp +++ b/include/boost/thread/pthread/tss.hpp @@ -86,7 +86,7 @@ namespace boost T* release() { T* const temp=get(); - detail::set_tss_data(this,0,0,false); + detail::set_tss_data(this,boost::shared_ptr(),0,false); return temp; } void reset(T* new_value=0) diff --git a/include/boost/thread/win32/tss.hpp b/include/boost/thread/win32/tss.hpp index 91870f14..c6ce5c12 100644 --- a/include/boost/thread/win32/tss.hpp +++ b/include/boost/thread/win32/tss.hpp @@ -85,7 +85,7 @@ namespace boost T* release() { T* const temp=get(); - detail::set_tss_data(this,0,0,false); + detail::set_tss_data(this,boost::shared_ptr(),0,false); return temp; } void reset(T* new_value=0) diff --git a/test/test_tss.cpp b/test/test_tss.cpp index 8c46d3db..51dcb7ff 100644 --- a/test/test_tss.cpp +++ b/test/test_tss.cpp @@ -228,6 +228,41 @@ void test_tss_with_custom_cleanup() timed_test(&do_test_tss_with_custom_cleanup, 2); } +Dummy* tss_object=new Dummy; + +void tss_thread_with_custom_cleanup_and_release() +{ + tss_with_cleanup.reset(tss_object); + tss_with_cleanup.release(); +} + +void do_test_tss_does_no_cleanup_after_release() +{ + tss_cleanup_called=false; + boost::thread t(tss_thread_with_custom_cleanup_and_release); + try + { + t.join(); + } + catch(...) + { + t.interrupt(); + t.join(); + throw; + } + + BOOST_CHECK(!tss_cleanup_called); + if(!tss_cleanup_called) + { + delete tss_object; + } +} + +void test_tss_does_no_cleanup_after_release() +{ + timed_test(&do_test_tss_does_no_cleanup_after_release, 2); +} + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = @@ -235,6 +270,7 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) test->add(BOOST_TEST_CASE(test_tss)); test->add(BOOST_TEST_CASE(test_tss_with_custom_cleanup)); + test->add(BOOST_TEST_CASE(test_tss_does_no_cleanup_after_release)); return test; } From cfb08be1a8568ac24be48833b71bed8111597489 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 17 Mar 2008 10:29:27 +0000 Subject: [PATCH 099/214] New documentation for new thread library [SVN r43671] --- doc/Jamfile.v2 | 54 +- doc/acknowledgements.qbk | 16 + doc/acknowledgements.xml | 73 -- doc/barrier-ref.xml | 82 -- doc/barrier.qbk | 65 + doc/bibliography.xml | 234 ---- doc/build.xml | 137 -- doc/changes.qbk | 50 + doc/concepts.xml | 2305 ---------------------------------- doc/condition-ref.xml | 196 --- doc/condition_variables.qbk | 494 ++++++++ doc/configuration.xml | 96 -- doc/design.xml | 159 --- doc/entities.xml | 31 - doc/exceptions-ref.xml | 62 - doc/faq.xml | 235 ---- doc/glossary.xml | 304 ----- doc/implementation_notes.xml | 38 - doc/mutex-ref.xml | 309 ----- doc/mutex_concepts.qbk | 887 +++++++++++++ doc/mutexes.qbk | 128 ++ doc/once-ref.xml | 88 -- doc/once.qbk | 45 + doc/overview.qbk | 15 + doc/overview.xml | 206 --- doc/rationale.xml | 438 ------- doc/read_write_mutex-ref.xml | 492 -------- doc/recursive_mutex-ref.xml | 306 ----- doc/reference.xml | 30 - doc/release_notes.xml | 204 --- doc/shared_mutex_ref.qbk | 35 + doc/thread-ref.xml | 270 ---- doc/thread.qbk | 150 +++ doc/thread.xml | 48 - doc/thread_ref.qbk | 933 ++++++++++++++ doc/tss-ref.xml | 206 --- doc/tss.qbk | 175 +++ doc/xtime-ref.xml | 82 -- 38 files changed, 3042 insertions(+), 6636 deletions(-) create mode 100644 doc/acknowledgements.qbk delete mode 100644 doc/acknowledgements.xml delete mode 100644 doc/barrier-ref.xml create mode 100644 doc/barrier.qbk delete mode 100644 doc/bibliography.xml delete mode 100644 doc/build.xml create mode 100644 doc/changes.qbk delete mode 100644 doc/concepts.xml delete mode 100644 doc/condition-ref.xml create mode 100644 doc/condition_variables.qbk delete mode 100644 doc/configuration.xml delete mode 100644 doc/design.xml delete mode 100644 doc/entities.xml delete mode 100644 doc/exceptions-ref.xml delete mode 100644 doc/faq.xml delete mode 100644 doc/glossary.xml delete mode 100644 doc/implementation_notes.xml delete mode 100644 doc/mutex-ref.xml create mode 100644 doc/mutex_concepts.qbk create mode 100644 doc/mutexes.qbk delete mode 100644 doc/once-ref.xml create mode 100644 doc/once.qbk create mode 100644 doc/overview.qbk delete mode 100644 doc/overview.xml delete mode 100644 doc/rationale.xml delete mode 100644 doc/read_write_mutex-ref.xml delete mode 100644 doc/recursive_mutex-ref.xml delete mode 100644 doc/reference.xml delete mode 100644 doc/release_notes.xml create mode 100644 doc/shared_mutex_ref.qbk delete mode 100644 doc/thread-ref.xml create mode 100644 doc/thread.qbk delete mode 100644 doc/thread.xml create mode 100644 doc/thread_ref.qbk delete mode 100644 doc/tss-ref.xml create mode 100644 doc/tss.qbk delete mode 100644 doc/xtime-ref.xml diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index 42c88f1d..dc68fc39 100644 --- a/doc/Jamfile.v2 +++ b/doc/Jamfile.v2 @@ -1,11 +1,55 @@ -# Copyright (C) 2001-2003 -# William E. Kempf +# (C) Copyright 2008 Anthony Williams # # 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) -import toolset ; -toolset.using doxygen ; +path-constant boost-images : ../../../doc/src/images ; -boostbook thread : thread.xml ; +xml thread : thread.qbk ; + +boostbook standalone + : + thread + : + # HTML options first: + # Use graphics not text for navigation: + navig.graphics=1 + # How far down we chunk nested sections, basically all of them: + chunk.section.depth=3 + # Don't put the first section on the same page as the TOC: + chunk.first.sections=1 + # How far down sections get TOC's + toc.section.depth=10 + # Max depth in each TOC: + toc.max.depth=3 + # How far down we go with TOC's + generate.section.toc.level=10 + # Path for links to Boost: + boost.root=../../../.. + # Path for libraries index: + boost.libraries=../../../../libs/libraries.htm + # Use the main Boost stylesheet: + html.stylesheet=../../../../doc/html/boostbook.css + + # PDF Options: + # TOC Generation: this is needed for FOP-0.9 and later: + #fop1.extensions=1 + # Or enable this if you're using XEP: + xep.extensions=1 + # TOC generation: this is needed for FOP 0.2, but must not be set to zero for FOP-0.9! + fop.extensions=0 + # No indent on body text: + body.start.indent=0pt + # Margin size: + page.margin.inner=0.5in + # Margin size: + page.margin.outer=0.5in + # Yes, we want graphics for admonishments: + admon.graphics=1 + # Set this one for PDF generation *only*: + # default pnd graphics are awful in PDF form, + # better use SVG's instead: + pdf:admon.graphics.extension=".svg" + pdf:admon.graphics.path=$(boost-images)/ + ; diff --git a/doc/acknowledgements.qbk b/doc/acknowledgements.qbk new file mode 100644 index 00000000..ec008cf3 --- /dev/null +++ b/doc/acknowledgements.qbk @@ -0,0 +1,16 @@ +[section:acknowledgements Acknowledgments] + +The original implementation of __boost_thread__ was written by William Kempf, with contributions from numerous others. This new +version initially grew out of an attempt to rewrite __boost_thread__ to William Kempf's design with fresh code that could be +released under the Boost Software License. However, as the C++ Standards committee have been actively discussing standardizing a +thread library for C++, this library has evolved to reflect the proposals, whilst retaining as much backwards-compatibility as +possible. + +Particular thanks must be given to Roland Schwarz, who contributed a lot of time and code to the original __boost_thread__ library, +and who has been actively involved with the rewrite. The scheme for dividing the platform-specific implementations into separate +directories was devised by Roland, and his input has contributed greatly to improving the quality of the current implementation. + +Thanks also must go to Peter Dimov, Howard Hinnant, Alexander Terekhov, Chris Thomasson and others for their comments on the +implementation details of the code. + +[endsect] diff --git a/doc/acknowledgements.xml b/doc/acknowledgements.xml deleted file mode 100644 index 60ad9198..00000000 --- a/doc/acknowledgements.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - %thread.entities; -]> - -
- Acknowledgements - William E. Kempf was the architect, designer, and implementor of - &Boost.Thread;. - Mac OS Carbon implementation written by Mac Murrett. - Dave Moore provided initial submissions and further comments on the - barrier - , - thread_pool - , - read_write_mutex - , - read_write_try_mutex - and - read_write_timed_mutex - classes. - Important contributions were also made by Jeremy Siek (lots of input - on the design and on the implementation), Alexander Terekhov (lots of input - on the Win32 implementation, especially in regards to boost::condition, as - well as a lot of explanation of POSIX behavior), Greg Colvin (lots of input - on the design), Paul Mclachlan, Thomas Matelich and Iain Hanson (for help - in trying to get the build to work on other platforms), and Kevin S. Van - Horn (for several updates/corrections to the documentation). - Mike Glassford finished changes to &Boost.Thread; that were begun - by William Kempf and moved them into the main CVS branch. - He also addressed a number of issues that were brought up on the Boost - developer's mailing list and provided some additions and changes to the - read_write_mutex and related classes. - The documentation was written by William E. Kempf. Beman Dawes - provided additional documentation material and editing. - Mike Glassford finished William Kempf's conversion of the documentation to - BoostBook format and added a number of new sections. - Discussions on the boost.org mailing list were essential in the - development of &Boost.Thread; - . As of August 1, 2001, participants included Alan Griffiths, Albrecht - Fritzsche, Aleksey Gurtovoy, Alexander Terekhov, Andrew Green, Andy Sawyer, - Asger Alstrup Nielsen, Beman Dawes, Bill Klein, Bill Rutiser, Bill Wade, - Branko èibej, Brent Verner, Craig Henderson, Csaba Szepesvari, - Dale Peakall, Damian Dixon, Dan Nuffer, Darryl Green, Daryle Walker, David - Abrahams, David Allan Finch, Dejan Jelovic, Dietmar Kuehl, Douglas Gregor, - Duncan Harris, Ed Brey, Eric Swanson, Eugene Karpachov, Fabrice Truillot, - Frank Gerlach, Gary Powell, Gernot Neppert, Geurt Vos, Ghazi Ramadan, Greg - Colvin, Gregory Seidman, HYS, Iain Hanson, Ian Bruntlett, J Panzer, Jeff - Garland, Jeff Paquette, Jens Maurer, Jeremy Siek, Jesse Jones, Joe Gottman, - John (EBo) David, John Bandela, John Maddock, John Max Skaller, John - Panzer, Jon Jagger , Karl Nelson, Kevlin Henney, KG Chandrasekhar, Levente - Farkas, Lie-Quan Lee, Lois Goldthwaite, Luis Pedro Coelho, Marc Girod, Mark - A. Borgerding, Mark Rodgers, Marshall Clow, Matthew Austern, Matthew Hurd, - Michael D. Crawford, Michael H. Cox , Mike Haller, Miki Jovanovic, Nathan - Myers, Paul Moore, Pavel Cisler, Peter Dimov, Petr Kocmid, Philip Nash, - Rainer Deyke, Reid Sweatman, Ross Smith, Scott McCaskill, Shalom Reich, - Steve Cleary, Steven Kirk, Thomas Holenstein, Thomas Matelich, Trevor - Perrin, Valentin Bonnard, Vesa Karvonen, Wayne Miller, and William - Kempf. - - As of February 2006 Anthony Williams and Roland Schwarz took over maintainance - and further development of the library after it has been in an orphaned state - for a rather long period of time. - - Apologies for anyone inadvertently missed. -
- diff --git a/doc/barrier-ref.xml b/doc/barrier-ref.xml deleted file mode 100644 index 10b5519b..00000000 --- a/doc/barrier-ref.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - %thread.entities; -]> - -
- - - - boost::noncopyable - Exposition only - - - - An object of class barrier is a synchronization - primitive used to cause a set of threads to wait until they each perform a - certain function or each reach a particular point in their execution. - - - - When a barrier is created, it is initialized with a thread count N. - The first N-1 calls to wait() will all cause their threads to be blocked. - The Nth call to wait() will allow all of the waiting threads, including - the Nth thread, to be placed in a ready state. The Nth call will also "reset" - the barrier such that, if an additional N+1th call is made to wait(), - it will be as though this were the first call to wait(); in other - words, the N+1th to 2N-1th calls to wait() will cause their - threads to be blocked, and the 2Nth call to wait() will allow all of - the waiting threads, including the 2Nth thread, to be placed in a ready state - and reset the barrier. This functionality allows the same set of N threads to re-use - a barrier object to synchronize their execution at multiple points during their - execution. - See for definitions of thread - states blocked - and ready. - Note that "waiting" is a synonym for blocked. - - - - - size_t - - - Constructs a barrier object that - will cause count threads to block on a call to wait(). - - - - - Destroys *this. If threads are still executing - their wait() operations, the behavior for these threads is undefined. - - - - - - bool - - Wait until N threads call wait(), where - N equals the count provided to the constructor for the - barrier object. - Note that if the barrier is - destroyed before wait() can return, the behavior is - undefined. - - Exactly one of the N threads will receive a return value - of true, the others will receive a value of false. - Precisely which thread receives the return value of true will - be implementation-defined. Applications can use this value to designate one - thread as a leader that will take a certain action, and the other threads - emerging from the barrier can wait for that action to take place. - - - - -
diff --git a/doc/barrier.qbk b/doc/barrier.qbk new file mode 100644 index 00000000..c5df571d --- /dev/null +++ b/doc/barrier.qbk @@ -0,0 +1,65 @@ +[section:barriers Barriers] + +A barrier is a simple concept. Also known as a ['rendezvous], it is a synchronization point between multiple threads. The barrier is +configured for a particular number of threads (`n`), and as threads reach the barrier they must wait until all `n` threads have +arrived. Once the `n`-th thread has reached the barrier, all the waiting threads can proceed, and the barrier is reset. + +[section:barrier Class `barrier`] + + #include + + class barrier + { + public: + barrier(unsigned int count); + ~barrier(); + + bool wait(); + }; + +Instances of __barrier__ are not copyable or movable. + +[heading Constructor] + + barrier(unsigned int count); + +[variablelist + +[[Effects:] [Construct a barrier for `count` threads.]] + +[[Throws:] [__thread_resource_error__ if an error occurs.]] + +] + +[heading Destructor] + + ~barrier(); + +[variablelist + +[[Precondition:] [No threads are waiting on `*this`.]] + +[[Effects:] [Destroys `*this`.]] + +[[Throws:] [Nothing.]] + +] + +[heading Member function `wait`] + + bool wait(); + +[variablelist + +[[Effects:] [Block until `count` threads have called `wait` on `*this`. When the `count`-th thread calls `wait`, all waiting threads +are unblocked, and the barrier is reset. ]] + +[[Returns:] [`true` for exactly one thread from each batch of waiting threads, `false` otherwise.]] + +[[Throws:] [__thread_resource_error__ if an error occurs.]] + +] + +[endsect] + +[endsect] diff --git a/doc/bibliography.xml b/doc/bibliography.xml deleted file mode 100644 index 458d6392..00000000 --- a/doc/bibliography.xml +++ /dev/null @@ -1,234 +0,0 @@ - - - %thread.entities; -]> - - - Bibliography - - AndrewsSchnieder83 - - ACM Computing Surveys - Vol. 15 - No. 1 - March, 1983 - - - - - Gregory - R. - Andrews - - - Fred - B. - Schneider - - - - <ulink - url="http://www.acm.org/pubs/citations/journals/surveys/1983-15-1/p3-andrews/" - >Concepts and Notations for Concurrent Programming</ulink> - - - Good general background reading. Includes descriptions of Path - Expressions, Message Passing, and Remote Procedure Call in addition to the - basics - - - Boost - The Boost world wide web site. - http://www.boost.org - &Boost.Thread; is one of many Boost libraries. The Boost web - site includes a great deal of documentation and general information which - applies to all Boost libraries. Current copies of the libraries including - documentation and test programs may be downloaded from the web - site. - - - Hansen73 - - ACM Computing Surveys - Vol. 5 - No. 4 - December, 1973 - - - 0-201-63392-2 - Per Brinch - Hansen - - - <ulink - url="http://www.acm.org/pubs/articles/journals/surveys/1973-5-4/p223-hansen/" - >Concurrent Programming Concepts</ulink> - - - "This paper describes the evolution of language features for - multiprogramming from event queues and semaphores to critical regions and - monitors." Includes analysis of why events are considered error-prone. Also - noteworthy because of an introductory quotation from Christopher Alexander; - Brinch Hansen was years ahead of others in recognizing pattern concepts - applied to software, too. - - - Butenhof97 - - <ulink url="http://cseng.aw.com/book/0,3828,0201633922,00.html" - >Programming with POSIX Threads </ulink> - - - David - R. - Butenhof - - Addison-Wesley - 1997 - ISNB: 0-201-63392-2 - This is a very readable explanation of threads and how to use - them. Many of the insights given apply to all multithreaded programming, not - just POSIX Threads - - - Hoare74 - - Communications of the ACM - Vol. 17 - No. 10 - October, 1974 - - - - <ulink url=" http://www.acm.org/classics/feb96/" - >Monitors: An Operating System Structuring Concept</ulink> - - - C.A.R. - Hoare - - 549-557 - - Hoare and Brinch Hansen's work on Monitors is the basis for reliable - multithreading patterns. This is one of the most often referenced papers in - all of computer science, and with good reason. - - - ISO98 - - <ulink url="http://www.ansi.org">Programming Language C++</ulink> - - ISO/IEC - 14882:1998(E) - This is the official C++ Standards document. Available from the ANSI - (American National Standards Institute) Electronic Standards Store. - - - McDowellHelmbold89 - - Communications of the ACM - Vol. 21 - No. 2 - December, 1989 - - - - Charles - E. - McDowell - - - David - P. - Helmbold - - - <ulink - url="http://www.acm.org/pubs/citations/journals/surveys/1989-21-4/p593-mcdowell/" - >Debugging Concurrent Programs</ulink> - - - Identifies many of the unique failure modes and debugging difficulties - associated with concurrent programs. - - - SchmidtPyarali - - <ulink url="http://www.cs.wustl.edu/~schmidt/win32-cv-1.html8" - >Strategies for Implementing POSIX Condition Variables on Win32</ulink> - - - - Douglas - C. - Schmidt - - - Irfan - Pyarali - - - Department of Computer Science, Washington University, St. Louis, - Missouri - Rationale for understanding &Boost.Thread; condition - variables. Note that Alexander Terekhov found some bugs in the - implementation given in this article, so pthreads-win32 and &Boost.Thread; - are even more complicated yet. - - - SchmidtStalRohnertBuschmann - - <ulink - url="http://www.wiley.com/Corporate/Website/Objects/Products/0,9049,104671,00.html" - >Pattern-Oriented Architecture Volume 2</ulink> - - Patterns for Concurrent and Networked Objects - POSA2 - - - Douglas - C. - Schmidt - - - Michael - Stal - - - Hans - Rohnert - - - Frank - Buschmann - - - Wiley - 2000 - This is a very good explanation of how to apply several patterns - useful for concurrent programming. Among the patterns documented is the - Monitor Pattern mentioned frequently in the &Boost.Thread; - documentation. - - - Stroustrup - - <ulink url="http://cseng.aw.com/book/0,3828,0201700735,00.html" - >The C++ Programming Language</ulink> - - Special Edition - Addison-Wesley - 2000 - ISBN: 0-201-70073-5 - The first book a C++ programmer should own. Note that the 3rd edition - (and subsequent editions like the Special Edition) has been rewritten to - cover the ISO standard language and library. - - diff --git a/doc/build.xml b/doc/build.xml deleted file mode 100644 index 7f426771..00000000 --- a/doc/build.xml +++ /dev/null @@ -1,137 +0,0 @@ - - - %thread.entities; -]> - -
- Build - - How you build the &Boost.Thread; libraries, and how you build your own applications - that use those libraries, are some of the most frequently asked questions. Build - processes are difficult to deal with in a portable manner. That's one reason - why &Boost.Thread; makes use of &Boost.Build;. - In general you should refer to the documentation for &Boost.Build;. - This document will only supply you with some simple usage examples for how to - use bjam to build and test &Boost.Thread;. In addition, this document - will try to explain the build requirements so that users may create their own - build processes (for instance, create an IDE specific project), both for building - and testing &Boost.Thread;, as well as for building their own projects using - &Boost.Thread;. - -
- Building the &Boost.Thread; Libraries - - Building the &Boost.Thread; Library depends on how you intend to use it. You have several options: - - - Using as a precompiled library, possibly - with auto-linking, or for use from within an IDE. - - - Use from a &Boost.Build; project. - - - Using in source form. - - - -
- Precompiled - - Using the &Boost.Thread; library in precompiled form is the way to go if you want to - install the library to a standard place, from where your linker is able to resolve code - in binary form. You also will want this option if compile time is a concern. Multiple - variants are available, for different toolsets and build variants (debug/release). - The library files are named {lead}boost_thread{build-specific-tags}.{extension}, - where the build-specific-tags indicate the toolset used to build the library, whether it's - a debug or release build, what version of &Boost; was used, etc.; and the lead and extension - are the appropriate extensions for a dynamic link library or static library for the platform - for which &Boost.Thread; is being built. - For instance, a debug build of the dynamic library built for Win32 with VC++ 7.1 using Boost 1.34 would - be named boost_thread-vc71-mt-gd-1_34.dll. - More information on this should be available from the &Boost.Build; documentation. - - - Building should be possible with the default configuration. If you are running into problems, - it might be wise to adjust your local settings of &Boost.Build; though. Typically you will - need to get your user-config.jam file to reflect your environment, i.e. used toolsets. Please - refer to the &Boost.Build; documentation to learn how to do this. - - - To create the libraries you need to open a command shell and change to the - boost_root directory. From there you give the command - bjam --toolset=mytoolset stage --with-thread - Replace mytoolset with the name of your toolset, e.g. msvc-7.1 . - This will compile and put the libraries into the stage directory which is just below the - boost_root directory. &Boost.Build; by default will generate static and - dynamic variants for debug and release. - - - Invoking the above command without the --with-thread switch &Boost.Build; will build all of - the Boost distribution, including &Boost.Thread;. - - - The next step is to copy your libraries to a place where your linker is able to pick them up. - It is also quite possible to leave them in the stage directory and instruct your IDE to take them - from there. - - - In your IDE you then need to add boost_root/boost to the paths where the compiler - expects to find files to be included. For toolsets that support auto-linking - it is not necessary to explicitly specify the name of the library to link against, it is sufficient - to specify the path of the stage directory. Typically this is true on Windows. For gcc you need - to specify the exact library name (including all the tags). Please don't forget that threading - support must be turned on to be able to use the library. You should be able now to build your - project from the IDE. - -
-
- &Boost.Build; Project - - If you have decided to use &Boost.Build; as a build environment for your application, you simply - need to add a single line to your Jamroot file: - use-project /boost : {path-to-boost-root} ; - where {path-to-boost-root} needs to be replaced with the location of - your copy of the boost tree. - Later when you specify a component that needs to link against &Boost.Thread; you specify this - as e.g.: - exe myapp : {myappsources} /boost//thread ; - and you are done. - -
-
- Source Form - - Of course it is also possible to use the &Boost.Thread; library in source form. - First you need to specify the boost_root/boost directory as - a path where your compiler expects to find files to include. It is not easy - to isolate the &Boost.Thread; include files from the rest of the boost - library though. You would also need to isolate every include file that the thread - library depends on. Next you need to copy the files from - boost_root/libs/thread/src to your project and instruct your - build system to compile them together with your project. Please look into the - Jamfile in boost_root/libs/thread/build - to find out which compiler options and defines you will need to get a clean compile. - Using the boost library in this way is the least recommended, and should only be - considered if avoiding dependency on &Boost.Build; is a requirement. Even if so - it might be a better option to use the library in it's precompiled form. - Precompiled downloads are available from the boost consulting web site, or as - part of most linux distributions. - -
-
-
- Testing the &Boost.Thread; Libraries - - To test the &Boost.Thread; libraries using &Boost.Build;, simply change to the - directory boost_root/libs/thread/test and execute the command: - bjam --toolset=mytoolset test - -
-
diff --git a/doc/changes.qbk b/doc/changes.qbk new file mode 100644 index 00000000..030dbc3a --- /dev/null +++ b/doc/changes.qbk @@ -0,0 +1,50 @@ +[section:changes Changes since boost 1.34] + +Almost every line of code in __boost_thread__ has been changed since the 1.34 release of boost. However, most of the interface +changes have been extensions, so the new code is largely backwards-compatible with the old code. The new features and breaking +changes are described below. + +[heading New Features] + +* Instances of __thread__ and of the various lock types are now movable. + +* Threads can be interrupted at __interruption_points__. + +* Condition variables can now be used with any type that implements the __lockable_concept__, through the use of +`boost::condition_variable_any` (`boost::condition` is a `typedef` to `boost::condition_variable_any`, provided for backwards +compatibility). `boost::condition_variable` is provided as an optimization, and will only work with +`boost::unique_lock` (`boost::mutex::scoped_lock`). + +* Thread IDs are separated from __thread__, so a thread can obtain it's own ID (using `boost::this_thread::get_id()`), and IDs can +be used as keys in associative containers, as they have the full set of comparison operators. + +* Timeouts are now implemented using the Boost DateTime library, through a typedef `boost::system_time` for absolute timeouts, and +with support for relative timeouts in many cases. `boost::xtime` is supported for backwards compatibility only. + +* Locks are implemented as publicly accessible templates `boost::lock_guard`, `boost::unique_lock`, `boost::shared_lock`, and +`boost::upgrade_lock`, which are templated on the type of the mutex. The __lockable_concept__ has been extended to include publicly +available __lock_ref__ and __unlock_ref__ member functions, which are used by the lock types. + +[heading Breaking Changes] + +The list below should cover all changes to the public interface which break backwards compatibility. + +* __try_mutex__ has been removed, and the functionality subsumed into __mutex__. __try_mutex__ is left as a `typedef`, +but is no longer a separate class. + +* __recursive_try_mutex__ has been removed, and the functionality subsumed into +__recursive_mutex__. __recursive_try_mutex__ is left as a `typedef`, but is no longer a separate class. + +* `boost::detail::thread::lock_ops` has been removed. Code that relies on the `lock_ops` implementation detail will no longer work, +as this has been removed, as it is no longer necessary now that mutex types now have public __lock_ref__ and __unlock_ref__ member +functions. + +* `scoped_lock` constructors with a second parameter of type `bool` are no longer provided. With previous boost releases, +``boost::mutex::scoped_lock some_lock(some_mutex,false);`` could be used to create a lock object that was associated with a mutex, +but did not lock it on construction. This facility has now been replaced with the constructor that takes a +`boost::defer_lock_type` as the second parameter: ``boost::mutex::scoped_lock some_lock(some_mutex,boost::defer_lock);`` + +* The broken `boost::read_write_mutex` has been replaced with __shared_mutex__. + + +[endsect] diff --git a/doc/concepts.xml b/doc/concepts.xml deleted file mode 100644 index 30386f70..00000000 --- a/doc/concepts.xml +++ /dev/null @@ -1,2305 +0,0 @@ - - - %thread.entities; -]> - -
- Concepts - - &Boost.Thread; currently supports two types of mutex concepts: - ordinary Mutexes, - which allow only one thread at a time to access a resource, and - Read/Write Mutexes, - which allow only one thread at a time to access a resource when it is - being modified (the "Write" part of Read/Write), but allows multiple threads - to access a resource when it is only being referenced (the "Read" part of - Read/Write). - Unfortunately it turned out that the current implementation of Read/Write Mutex has - some serious problems. So it was decided not to put this implementation into - release grade code. Also discussions on the mailing list led to the - conclusion that the current concepts need to be rethought. In particular - the schedulings - Inter-Class Scheduling Policies are deemed unnecessary. - There seems to be common belief that a fair scheme suffices. - The following documentation has been retained however, to give - readers of this document the opportunity to study the original design. - - -
- Mutexes - - Certain changes to the mutexes and lock concepts are - currently under discussion. In particular, the combination of - the multiple lock concepts into a single lock concept - is likely, and the combination of the multiple mutex - concepts into a single mutex concept is also possible. - - A mutex (short for mutual-exclusion) object is used to serialize - access to a resource shared between multiple threads. The - Mutex concept, with - TryMutex and - TimedMutex refinements, - formalize the requirements. A model that implements Mutex and its - refinements has two states: locked and - unlocked. Before using a shared resource, a - thread locks a &Boost.Thread; mutex object - (an object whose type is a model of - Mutex or one of it's - refinements), ensuring - thread-safe access to - the shared resource. When use of the shared resource is complete, the thread - unlocks the mutex object, allowing another thread to acquire the lock and - use the shared resource. - Traditional C thread APIs, like POSIX threads or the Windows thread - APIs, expose functions to lock and unlock a mutex object. This is dangerous - since it's easy to forget to unlock a locked mutex. When the flow of control - is complex, with multiple return points, the likelihood of forgetting to - unlock a mutex object becomes even greater. When exceptions are thrown, - it becomes nearly impossible to ensure that the mutex object is unlocked - properly when using these traditional API's. The result is - deadlock. - Many C++ threading libraries use a pattern known as Scoped - Locking &cite.SchmidtStalRohnertBuschmann; to free the programmer from - the need to explicitly lock and unlock mutex objects. With this pattern, a - Lock concept is employed where - the lock object's constructor locks the associated mutex object and the - destructor automatically does the unlocking. The - &Boost.Thread; library takes this pattern to - the extreme in that Lock concepts are the only way to lock and unlock a - mutex object: lock and unlock functions are not exposed by any - &Boost.Thread; mutex objects. This helps to - ensure safe usage patterns, especially when code throws exceptions. - -
- Locking Strategies - - Every mutex object follows one of several locking strategies. These - strategies define the semantics for the locking operation when the calling - thread already owns a lock on the mutex object. - -
- Recursive Locking Strategy - - With a recursive locking strategy, when a thread attempts to acquire - a lock on the mutex object for which it already owns a lock, the operation - is successful. Note the distinction between a thread, which may have - multiple locks outstanding on a recursive mutex object, and a lock object, - which even for a recursive mutex object cannot have any of its lock - functions called multiple times without first calling unlock. - - Internally a lock count is maintained and the owning thread must - unlock the mutex object the same number of times that it locked it before - the mutex object's state returns to unlocked. Since mutex objects in - &Boost.Thread; expose locking - functionality only through lock concepts, a thread will always unlock a - mutex object the same number of times that it locked it. This helps to - eliminate a whole set of errors typically found in traditional C style - thread APIs. - - Classes boost::recursive_mutex, - boost::recursive_try_mutex and - boost::recursive_timed_mutex use this locking - strategy. -
- -
- Checked Locking Strategy - - With a checked locking strategy, when a thread attempts to acquire a - lock on the mutex object for which the thread already owns a lock, the - operation will fail with some sort of error indication. Further, attempts - by a thread to unlock a mutex object that was not locked by the thread - will also return some sort of error indication. In - &Boost.Thread;, an exception of type - boost::lock_error - would be thrown in these cases. - - &Boost.Thread; does not currently - provide any mutex objects that use this strategy. -
- -
- Unchecked Locking Strategy - - With an unchecked locking strategy, when a thread attempts to acquire - a lock on a mutex object for which the thread already owns a lock the - operation will - deadlock. In general - this locking strategy is less safe than a checked or recursive strategy, - but it's also a faster strategy and so is employed by many libraries. - - &Boost.Thread; does not currently - provide any mutex objects that use this strategy. -
- -
- Unspecified Locking Strategy - - With an unspecified locking strategy, when a thread attempts to - acquire a lock on a mutex object for which the thread already owns a lock - the operation results in - undefined behavior. - - - In general a mutex object with an unspecified locking strategy is - unsafe, and it requires programmer discipline to use the mutex object - properly. However, this strategy allows an implementation to be as fast as - possible with no restrictions on its implementation. This is especially - true for portable implementations that wrap the native threading support - of a platform. For this reason, the classes - boost::mutex, - boost::try_mutex and - boost::timed_mutex use this locking strategy - despite the lack of safety. -
-
- -
- Scheduling Policies - - Every mutex object follows one of several scheduling policies. These - policies define the semantics when the mutex object is unlocked and there is - more than one thread waiting to acquire a lock. In other words, the policy - defines which waiting thread shall acquire the lock. - -
- FIFO Scheduling Policy - - With a FIFO ("First In First Out") scheduling policy, threads waiting - for the lock will acquire it in a first-come-first-served order. - This can help prevent a high priority thread from starving lower priority - threads that are also waiting on the mutex object's lock. -
- -
- Priority Driven Policy - - With a Priority Driven scheduling policy, the thread with the - highest priority acquires the lock. Note that this means that low-priority - threads may never acquire the lock if the mutex object has high contention - and there is always at least one high-priority thread waiting. This is - known as thread starvation. When multiple threads of the same priority are - waiting on the mutex object's lock one of the other scheduling priorities - will determine which thread shall acquire the lock. -
- -
- Unspecified Policy - - The mutex object does not specify a scheduling policy. In order to - ensure portability, all &Boost.Thread; - mutex objects use an unspecified scheduling policy. -
-
- -
- Mutex Concepts - -
- Mutex Concept - - A Mutex object has two states: locked and unlocked. Mutex object - state can only be determined by a lock object meeting the - appropriate lock concept requirements - and constructed for the Mutex object. - - A Mutex is - - NonCopyable. - For a Mutex type M - and an object m of that type, - the following expressions must be well-formed - and have the indicated effects. - - - Mutex Expressions - - - - - Expression - Effects - - - - - - M m; - Constructs a mutex object m. - Postcondition: m is unlocked. - - - (&m)->~M(); - Precondition: m is unlocked. Destroys a mutex object - m. - - - M::scoped_lock - A model of - ScopedLock - - - - -
-
- -
- TryMutex Concept - - A TryMutex is a refinement of - Mutex. - For a TryMutex type M - and an object m of that type, - the following expressions must be well-formed - and have the indicated effects. - - - TryMutex Expressions - - - - - Expression - Effects - - - - - - M::scoped_try_lock - A model of - ScopedTryLock - - - - -
-
- -
- TimedMutex Concept - - A TimedMutex is a refinement of - TryMutex. - For a TimedMutex type M - and an object m of that type, - the following expressions must be well-formed - and have the indicated effects. - - - TimedMutex Expressions - - - - - Expression - Effects - - - - - - M::scoped_timed_lock - A model of - ScopedTimedLock - - - - -
-
-
- -
- Mutex Models - - &Boost.Thread; currently supplies six models of - Mutex - and its refinements. - - - Mutex Models - - - - - Concept - Refines - Models - - - - - - Mutex - - - boost::mutex - boost::recursive_mutex - - - - TryMutex - Mutex - - boost::try_mutex - boost::recursive_try_mutex - - - - TimedMutex - TryMutex - - boost::timed_mutex - boost::recursive_timed_mutex - - - - -
-
- -
- Lock Concepts - - A lock object provides a safe means for locking and unlocking a mutex - object (an object whose type is a model of Mutex or one of its refinements). In - other words they are an implementation of the Scoped - Locking &cite.SchmidtStalRohnertBuschmann; pattern. The ScopedLock, - ScopedTryLock, and - ScopedTimedLock - concepts formalize the requirements. - Lock objects are constructed with a reference to a mutex object and - typically acquire ownership of the mutex object by setting its state to - locked. They also ensure ownership is relinquished in the destructor. Lock - objects also expose functions to query the lock status and to manually lock - and unlock the mutex object. - Lock objects are meant to be short lived, expected to be used at block - scope only. The lock objects are not thread-safe. Lock objects must - maintain state to indicate whether or not they've been locked and this state - is not protected by any synchronization concepts. For this reason a lock - object should never be shared between multiple threads. - -
- Lock Concept - - For a Lock type L - and an object lk - and const object clk of that type, - the following expressions must be well-formed - and have the indicated effects. - - - Lock Expressions - - - - - Expression - Effects - - - - - - (&lk)->~L(); - if (locked()) unlock(); - - - (&clk)->operator const void*() - Returns type void*, non-zero if the associated mutex - object has been locked by clk, otherwise 0. - - - clk.locked() - Returns a bool, (&clk)->operator - const void*() != 0 - - - lk.lock() - - Throws boost::lock_error - if locked(). - - If the associated mutex object is - already locked by some other thread, places the current thread in the - Blocked state until - the associated mutex is unlocked, after which the current thread - is placed in the Ready state, - eventually to be returned to the Running state. If - the associated mutex object is already locked by the same thread - the behavior is dependent on the locking - strategy of the associated mutex object. - - Postcondition: locked() == true - - - - lk.unlock() - - Throws boost::lock_error - if !locked(). - - Unlocks the associated mutex. - - Postcondition: !locked() - - - -
-
- -
- ScopedLock Concept - - A ScopedLock is a refinement of Lock. - For a ScopedLock type L - and an object lk of that type, - and an object m of a type meeting the - Mutex requirements, - and an object b of type bool, - the following expressions must be well-formed - and have the indicated effects. - - - ScopedLock Expressions - - - - - Expression - Effects - - - - - - L lk(m); - Constructs an object lk, and associates mutex - object m with it, then calls - lock() - - - L lk(m,b); - Constructs an object lk, and associates mutex - object m with it, then if b, calls - lock() - - - -
-
- -
- TryLock Concept - - A TryLock is a refinement of Lock. - For a TryLock type L - and an object lk of that type, - the following expressions must be well-formed - and have the indicated effects. - - - TryLock Expressions - - - - - Expression - Effects - - - - - - lk.try_lock() - - Throws boost::lock_error - if locked(). - - Makes a - non-blocking attempt to lock the associated mutex object, - returning true if the lock attempt is successful, - otherwise false. If the associated mutex object is - already locked by the same thread the behavior is dependent on the - locking - strategy of the associated mutex object. - - - - -
-
- -
- ScopedTryLock Concept - - A ScopedTryLock is a refinement of TryLock. - For a ScopedTryLock type L - and an object lk of that type, - and an object m of a type meeting the - TryMutex requirements, - and an object b of type bool, - the following expressions must be well-formed - and have the indicated effects. - - - ScopedTryLock Expressions - - - - - Expression - Effects - - - - - - L lk(m); - Constructs an object lk, and associates mutex - object m with it, then calls - try_lock() - - - L lk(m,b); - Constructs an object lk, and associates mutex - object m with it, then if b, calls - lock() - - - -
-
- -
- TimedLock Concept - - A TimedLock is a refinement of TryLock. - For a TimedLock type L - and an object lk of that type, - and an object t of type boost::xtime, - the following expressions must be well-formed - and have the indicated effects. - - - TimedLock Expressions - - - - Expression - Effects - - - - - - lk.timed_lock(t) - - Throws boost::lock_error - if locked(). - - Makes a blocking attempt - to lock the associated mutex object, and returns true - if successful within the specified time t, otherwise - false. If the associated mutex object is already - locked by the same thread the behavior is dependent on the locking - strategy of the associated mutex object. - - - - -
-
- -
- ScopedTimedLock Concept - - A ScopedTimedLock is a refinement of TimedLock. - For a ScopedTimedLock type L - and an object lk of that type, - and an object m of a type meeting the - TimedMutex requirements, - and an object b of type bool, - and an object t of type boost::xtime, - the following expressions must be well-formed - and have the indicated effects. - - - ScopedTimedLock Expressions - - - - Expression - Effects - - - - - - L lk(m,t); - Constructs an object lk, and associates mutex - object m with it, then calls - timed_lock(t) - - - L lk(m,b); - Constructs an object lk, and associates mutex - object m with it, then if b, calls - lock() - - - -
-
-
- -
- Lock Models - - &Boost.Thread; currently supplies twelve models of - Lock - and its refinements. - - - Lock Models - - - - - Concept - Refines - Models - - - - - - Lock - - - - - ScopedLock - Lock - - boost::mutex::scoped_lock - boost::recursive_mutex::scoped_lock - - boost::try_mutex::scoped_lock - boost::recursive_try_mutex::scoped_lock - - boost::timed_mutex::scoped_lock - boost::recursive_timed_mutex::scoped_lock - - - - TryLock - Lock - - - - ScopedTryLock - TryLock - - boost::try_mutex::scoped_try_lock - boost::recursive_try_mutex::scoped_try_lock - - boost::timed_mutex::scoped_try_lock - boost::recursive_timed_mutex::scoped_try_lock - - - - TimedLock - TryLock - - - - ScopedTimedLock - TimedLock - - boost::timed_mutex::scoped_timed_lock - boost::recursive_timed_mutex::scoped_timed_lock - - - - -
-
-
- -
- Read/Write Mutexes - Unfortunately it turned out that the current implementation has - some serious problems. So it was decided not to put this implementation into - release grade code. Also discussions on the mailing list led to the - conclusion that the current concepts need to be rethought. In particular - the schedulings - Inter-Class Scheduling Policies are deemed unnecessary. - There seems to be common belief that a fair scheme suffices. - The following documentation has been retained however, to give - readers of this document the opportunity to study the original design. - - - A read/write mutex (short for reader/writer mutual-exclusion) object - is used to serialize access to a resource shared between multiple - threads, where multiple "readers" can share simultaneous access, but - "writers" require exclusive access. The - ReadWriteMutex concept, with - TryReadWriteMutex and - TimedReadWriteMutex - refinements formalize the requirements. A model that implements - ReadWriteMutex and its refinements has three states: - read-locked, - write-locked, and - unlocked. - Before reading from a shared resource, a thread - read-locks - a &Boost.Thread; read/write mutex object - (an object whose type is a model of - ReadWriteMutex - or one of it's refinements), ensuring - thread-safe - access for reading from the shared resource. Before writing - to a shared resource, a thread - write-locks a &Boost.Thread; - read/write mutex object - (an object whose type is a model of - ReadWriteMutex - or one of it's refinements), ensuring - thread-safe - access for altering the shared resource. When use of the shared - resource is complete, the thread unlocks the mutex object, - allowing another thread to acquire the lock and use the shared - resource. - - Traditional C thread APIs that provide read/write mutex - primitives (like POSIX threads) expose functions to lock and unlock a - mutex object. This is dangerous since it's easy to forget to unlock a - locked mutex. When the flow of control is complex, with multiple - return points, the likelihood of forgetting to unlock a mutex object - becomes even greater. When exceptions are thrown, it becomes nearly - impossible to ensure that the mutex object is unlocked - properly when using these traditional API's. The result is - deadlock. - - Many C++ threading libraries use a pattern known as Scoped - Locking &cite.SchmidtStalRohnertBuschmann; to free the - programmer from the need to explicitly lock and unlock - read/write mutex objects. With this pattern, a - Read/Write Lock - concept is employed where the lock object's constructor locks - the associated read/write mutex object - and the destructor automatically does the unlocking. The - &Boost.Thread; library takes this pattern to - the extreme in that - Read/Write Lock - concepts are the only way to lock and unlock a read/write mutex - object: lock and unlock functions are not exposed by any - &Boost.Thread; read/write mutex objects. This helps to - ensure safe usage patterns, especially when code throws exceptions. - -
- Locking Strategies - - Every read/write mutex object follows one of several locking - strategies. These strategies define the semantics for the locking - operation when the calling thread already owns a lock on the - read/write mutex object. - -
- Recursive Locking Strategy - - With a recursive locking strategy, when a thread attempts - to acquire a lock on a read/write mutex object - for which it already owns a lock, the operation is successful, - except in the case where a thread holding a read-lock - attempts to obtain a write lock, in which case a - boost::lock_error exception will - be thrown. Note the distinction between a thread, which may have - multiple locks outstanding on a recursive read/write mutex object, - and a lock object, which even for a recursive read/write mutex - object cannot have any of its lock functions called multiple - times without first calling unlock. - - - - - - Lock Type Held - Lock Type Requested - Action - - - - - - read-lock - read-lock - Grant the read-lock immediately - - - read-lock - write-lock - If this thread is the only holder of the read-lock, - grants the write lock immediately. Otherwise throws a - boost::lock_error exception. - - - write-locked - read-lock - Grants the (additional) read-lock immediately. - - - write-locked - write-lock - Grant the write-lock immediately - - - - - - Internally a lock count is maintained and the owning - thread must unlock the mutex object the same number of times - that it locked it before the mutex object's state returns - to unlocked. Since mutex objects in &Boost.Thread; expose - locking functionality only through lock concepts, a thread - will always unlock a mutex object the same number of times - that it locked it. This helps to eliminate a whole set of - errors typically found in traditional C style thread APIs. - - - &Boost.Thread; does not currently provide any read/write mutex objects - that use this strategy. A successful implementation of this locking strategy - may require - thread identification. - -
- -
- Checked Locking Strategy - - With a checked locking strategy, when a thread attempts - to acquire a lock on the mutex object for which the thread - already owns a lock, the operation will fail with some sort of - error indication, except in the case of multiple read-lock - acquisition which is a normal operation for ANY ReadWriteMutex. - Further, attempts by a thread to unlock a mutex that was not - locked by the thread will also return some sort of error - indication. In &Boost.Thread;, an exception of type - boost::lock_error would be thrown in - these cases. - - - - - - Lock Type Held - Lock Type Requested - Action - - - - - - read-lock - read-lock - Grant the read-lock immediately - - - read-lock - write-lock - Throw boost::lock_error - - - write-locked - read-lock - Throw boost::lock_error - - - write-locked - write-lock - Throw boost::lock_error - - - - - - &Boost.Thread; does not currently provide any read/write mutex objects - that use this strategy. A successful implementation of this locking strategy - may require - thread identification. - -
- -
- Unchecked Locking Strategy - - With an unchecked locking strategy, when a thread - attempts to acquire a lock on the read/write mutex object - for which the thread already owns a lock, the operation - will deadlock. - In general this locking strategy is less safe than a checked - or recursive strategy, but it can be a faster strategy and so - is employed by many libraries. - - - - - - Lock Type Held - Lock Type Requested - Action - - - - - - read-lock - read-lock - Grant the read-lock immediately - - - read-lock - write-lock - Deadlock - - - write-locked - read-lock - Deadlock - - - write-locked - write-lock - Deadlock - - - - - - &Boost.Thread; does not currently provide any mutex - objects that use this strategy. For ReadWriteMutexes on - platforms that contain natively recursive synchronization - primitives, implementing a guaranteed-deadlock can actually - involve extra work, and would likely require - thread identification. - -
- -
- Unspecified Locking Strategy - - With an unspecified locking strategy, when a thread - attempts to acquire a lock on a read/write mutex object for - which the thread already owns a lock, the operation results - in undefined behavior. - When a read/write mutex object has an unspecified locking - strategy the programmer must assume that the read/write mutex - object instead uses an unchecked strategy as the worse case, - although some platforms may exhibit a mix of unchecked and - recursive behavior. - - - - - - Lock Type Held - Lock Type Requested - Action - - - - - - read-lock - read-lock - Grant the read-lock immediately - - - read-lock - write-lock - - Undefined, but generally deadlock - - - - write-locked - read-lock - Undefined, but generally deadlock - - - write-locked - write-lock - Undefined, but generally deadlock - - - - - - In general a read/write mutex object with an unspecified - locking strategy is unsafe, and it requires programmer discipline - to use the read/write mutex object properly. However, this strategy - allows an implementation to be as fast as possible with no restrictions - on its implementation. This is especially true for portable implementations - that wrap the native threading support of a platform. For this reason, the - classes - read_write_mutex, - try_read_write_mutex, and - timed_read_write_mutex - use this locking strategy despite the lack of safety. -
- -
- Thread Identification - - ReadWriteMutexes can support specific Locking Strategies - (recursive and checked) which help to detect and protect against - self-deadlock. Self-deadlock can occur when a holder of a locked - ReadWriteMutex attempts to obtain another lock. Given an - implemention I which is susceptible to - self-deadlock but otherwise correct and efficient, a recursive or - checked implementation Ir or - Ic can use the same basic implementation, - but make special checks against self-deadlock by tracking the - identities of thread(s) currently holding locks. This approach - makes deadlock detection othrogonal to the basic ReadWriteMutex - implementaion. - - Alternatively, a different basic implementation for - ReadWriteMutex concepts, - I' (I-Prime) may exist which uses recursive - or checked versions of synchronization primitives to produce - a recursive or checked ReadWriteMutex while still providing - flexibility in terms of Scheduling Policies. - - Please refer to the &Boost.Thread; - read/write mutex concept - documentation for a discussion of locking strategies. - The read/write mutex supports only the - unspecified - locking strategy. ReadWriteMutexes are parameterized on a - Mutex type which they use to control write-locking - and access to internal state. -
- -
- Lock Promotion - - ReadWriteMutexes can support lock promotion, where a - mutex which is in the read-locked state transitions to a - write-locked state without releasing the lock. Lock - promotion can be tricky to implement; for instance, - extra care must be taken to ensure that only one thread holding a - read-lock can block awaiting promotion at any given time. If - more than one read-lock holder is allowed to enter a blocked - state while waiting to be promoted, deadlock will result since - both threads will be waiting for the other to release their read-lock. - - - Currently, &Boost.Thread; supports lock promotion - through promote(), try_promote(), - and timed_promote() operations. -
- -
- Lock Demotion - - ReadWriteMutexes can support lock demotion, where a - mutex which is in the write-locked state transitions to a - read-locked state without releasing the lock. - Since by definition only one thread at a time may hold - a write-lock, the problem with deadlock that can occur - during lock promotion is not a problem for lock - demotion. - - Currently, &Boost.Thread; supports lock demotion - through demote(), try_demote(), - and timed_demote() operations. -
-
- -
- Scheduling Policies - - Every read/write mutex object follows one of several scheduling - policies. These policies define the semantics when the mutex object - is unlocked and there is more than one thread waiting to acquire a - lock. In other words, the policy defines which waiting thread shall - acquire the lock. For a read/write mutex, it is particularly important - to define the behavior when threads are requesting both read and - write access simultaneously. This will be referred to as "inter-class - scheduling" because it describes the scheduling between two - classes of threads (those waiting for a read lock and those - waiting for a write lock). - - For some types of inter-class scheduling, an "intra-class" - scheduling policy can also be defined that will describe the order - in which waiting threads of the same class (i.e., those - waiting for the same type of lock) will acquire the thread. - - -
- Inter-Class Scheduling Policies - -
- ReaderPriority - - With ReaderPriority scheduling, any pending request for - a read-lock will have priority over a pending request for a - write-lock, irrespective of the current lock state of the - read/write mutex, and irrespective of the relative order - that the pending requests arrive. - - - - - - Current mutex state - Request Type - Action - - - - - - unlocked - read-lock - Grant the read-lock immediately - - - read-locked - read-lock - Grant the additional read-lock immediately. - - - write-locked - read-lock - Wait to acquire the lock until the thread - holding the write-lock releases its lock (or until - the specified time, if any). A - read-lock will be granted to all pending readers - before any other thread can acquire a write-lock. - TODO: try-lock, timed-lock. - - - - unlocked - write-lock - Grant the write-lock immediately, if and - only if there are no pending read-lock requests. - TODO: try-lock, timed-lock. - - - - read-locked - write-lock - Wait to acquire the lock until all - threads holding read-locks release their locks - AND no requests - for read-locks exist. If other write-lock - requests exist, the lock is granted in accordance - with the intra-class scheduling policy. - TODO: try-lock, timed-lock. - - - - write-locked - write-lock - Wait to acquire the lock until the thread - holding the write-lock releases its lock - AND no requests - for read-locks exist. If other write-lock - requests exist, the lock is granted in accordance - with the intra-class scheduling policy. - TODO: try-lock, timed-lock. - - - - read-locked - promote - TODO - - - write-locked - demote - TODO - - - - -
- -
- WriterPriority - - With WriterPriority scheduling, any pending request - for a write-lock will have priority over a pending request - for a read-lock, irrespective of the current lock state - of the read/write mutex, and irrespective of the relative - order that the pending requests arrive. - - - - - - Current mutex state - Request Type - Action - - - - - - unlocked - read-lock - Grant the read-lock immediately. - - - read-locked - read-lock - Grant the additional read-lock immediately, - IF no outstanding - requests for a write-lock exist; otherwise TODO. - TODO: try-lock, timed-lock. - - - - write-locked - read-lock - Wait to acquire the lock until the - thread holding the write-lock - releases its lock. The read lock will be granted - once no other outstanding write-lock requests - exist. - TODO: try-lock, timed-lock. - - - - unlocked - write-lock - Grant the write-lock immediately. - - - read-locked - write-lock - Wait to acquire the lock until all - threads holding read-locks release their locks. - If other write-lock requests exist, the lock - is granted in accordance with the intra-class - scheduling policy. This request will be granted - before any new read-lock requests are granted. - TODO: try-lock, timed-lock. - - - - write-locked - write-lock - Wait to acquire the lock until the thread - holding the write-lock releases its lock. If - other write-lock requests exist, the lock is - granted in accordance with the intra-class - scheduling policy. This request will be granted - before any new read-lock requests are granted. - TODO: try-lock, timed-lock. - - - - read-locked - promote - TODO - - - write-locked - demote - TODO - - - - -
- -
- AlternatingPriority/ManyReads - - With AlternatingPriority/ManyReads scheduling, reader - or writer starvation is avoided by alternatively granting read - or write access when pending requests exist for both types of - locks. Outstanding read-lock requests are treated as a group - when it is the "readers' turn" - - - - - - Current mutex state - Request Type - Action - - - - - - unlocked - read-lock - Grant the read-lock immediately. - - - read-locked - read-lock - Grant the additional read-lock immediately, - IF no outstanding - requests for a write-lock exist. If outstanding - write-lock requests exist, this lock will not - be granted until at least one of the - write-locks is granted and released. If other - read-lock requests exist, all read-locks will be - granted as a group. - TODO: try-lock, timed-lock. - - - - write-locked - read-lock - Wait to acquire the lock until the thread - holding the write-lock releases its lock. If other - outstanding write-lock requests exist, they will - have to wait until all current read-lock requests - are serviced. - TODO: try-lock, timed-lock. - - - - unlocked - write-lock - Grant the write-lock immediately. - - - read-locked - write-lock - - Wait to acquire the lock until all threads - holding read-locks release their locks. - - If other write-lock requests exist, this - lock will be granted to one of them in accordance - with the intra-class scheduling policy. - - TODO: try-lock, timed-lock. - - - - write-locked - write-lock - Wait to acquire the lock until the thread - holding the write-lock releases its lock. If - other outstanding read-lock requests exist, this - lock will not be granted until all of the - currently waiting read-locks are granted and - released. If other write-lock requests exist, - this lock will be granted in accordance with the - intra-class scheduling policy. - TODO: try-lock, timed-lock. - - - - read-locked - promote - TODO - - - write-locked - demote - TODO - - - - -
- -
- AlternatingPriority/SingleRead - - With AlternatingPriority/SingleRead scheduling, reader - or writer starvation is avoided by alternatively granting read - or write access when pending requests exist for both types of - locks. Outstanding read-lock requests are services one at a - time when it is the "readers' turn" - - - - - - Current mutex state - Request Type - Action - - - - - - unlocked - read-lock - Grant the read-lock immediately. - - - read-locked - read-lock - Grant the additional read-lock immediately, - IF no outstanding - requests for a write-lock exist. If outstanding - write-lock requests exist, this lock will not - be granted until at least one of the write-locks - is granted and released. - TODO: try-lock, timed-lock. - - - - write-locked - read-lock - - Wait to acquire the lock until the thread - holding the write-lock releases its lock. - If other outstanding write-lock requests - exist, exactly one read-lock request will be - granted before the next write-lock is granted. - - TODO: try-lock, timed-lock. - - - - unlocked - write-lock - Grant the write-lock immediately. - - - read-locked - write-lock - - Wait to acquire the lock until all - threads holding read-locks release their - locks. - - If other write-lock requests exist, - this lock will be granted to one of them - in accordance with the intra-class - scheduling policy. - - TODO: try-lock, timed-lock. - - - write-locked - write-lock - Wait to acquire the lock until the - thread holding the write-lock releases its - lock. If other outstanding read-lock requests - exist, this lock can not be granted until - exactly one read-lock request is granted and - released. If other write-lock requests exist, - this lock will be granted in accordance with - the intra-class scheduling policy. - TODO: try-lock, timed-lock. - - - - read-locked - promote - TODO - - - write-locked - demote - TODO - - - - -
-
- -
- Intra-Class Scheduling Policies - - Please refer to - - for a discussion of mutex scheduling policies, which are identical to - read/write mutex intra-class scheduling policies. - - For threads waiting to obtain write-locks, the read/write mutex - supports only the - Unspecified - intra-class scheduling policy. That is, given a set of threads - waiting for write-locks, the order, relative to one another, in - which they receive the write-lock is unspecified. - - For threads waiting to obtain read-locks, the read/write mutex - supports only the - Unspecified - intra-class scheduling policy. That is, given a set of threads - waiting for read-locks, the order, relative to one another, in - which they receive the read-lock is unspecified. -
-
- -
- Mutex Concepts - -
- ReadWriteMutex Concept - - A ReadWriteMutex object has three states: read-locked, - write-locked, and unlocked. ReadWriteMutex object state can - only be determined by a lock object meeting the appropriate lock concept - requirements and constructed for the ReadWriteMutex object. - - A ReadWriteMutex is - NonCopyable. - - - For a ReadWriteMutex type M, - and an object m of that type, - the following expressions must be well-formed - and have the indicated effects. - - - ReadWriteMutex Expressions - - - - - Expression - Effects - - - - - - M m; - Constructs a read/write mutex object m. - Post-condition: m is unlocked. - - - (&m)->~M(); - Precondition: m is unlocked. - Destroys a read/write mutex object m. - - - - M::scoped_read_write_lock - A type meeting the - ScopedReadWriteLock - requirements. - - - M::scoped_read_lock - A type meeting the - ScopedLock - requirements. - - - M::scoped_write_lock - A type meeting the - ScopedLock - requirements. - - - -
-
- -
- TryReadWriteMutex Concept - - A TryReadWriteMutex is a refinement of - ReadWriteMutex. - For a TryReadWriteMutex type M - and an object m of that type, - the following expressions must be well-formed - and have the indicated effects. - - - TryReadWriteMutex Expressions - - - - - Expression - Effects - - - - - - M::scoped_try_read_write_lock - A type meeting the - ScopedTryReadWriteLock - requirements. - - - M::scoped_try_read_lock - A type meeting the - ScopedTryLock - requirements. - - - M::scoped_try_write_lock - A type meeting the - ScopedTryLock - requirements. - - - -
-
- -
- TimedReadWriteMutex Concept - - A TimedReadWriteMutex is a refinement of - TryReadWriteMutex. - For a TimedReadWriteMutex type M - and an object m of that type - the following expressions must be well-formed - and have the indicated effects. - - - TimedReadWriteMutex Expressions - - - - - Expression - Effects - - - - - - M::scoped_timed_read_write_lock - A type meeting the - ScopedTimedReadWriteLock - requirements. - - - M::scoped_timed_read_lock - A type meeting the - ScopedTimedLock - requirements. - - - M::scoped_timed_write_lock - A type meeting the - ScopedTimedLock - requirements. - - - -
-
-
- -
- Mutex Models - - &Boost.Thread; currently supplies three models of - ReadWriteMutex - and its refinements. - - - Mutex Models - - - - - Concept - Refines - Models - - - - - - ReadWriteMutex - - boost::read_write_mutex - - - TryReadWriteMutex - ReadWriteMutex - boost::try_read_write_mutex - - - TimedReadWriteMutex - TryReadWriteMutex - boost::timed_read_write_mutex - - - -
-
- -
- Lock Concepts - - A read/write lock object provides a safe means for locking - and unlocking a read/write mutex object (an object whose type is - a model of - ReadWriteMutex - or one of its refinements). In other words they are an - implementation of the Scoped Locking - &cite.SchmidtStalRohnertBuschmann; pattern. The - ScopedReadWriteLock, - ScopedTryReadWriteLock, and - ScopedTimedReadWriteLock - concepts formalize the requirements. - - Read/write lock objects are constructed with a reference to a - read/write mutex object and typically acquire ownership of the - read/write mutex object by setting its state to locked. They also - ensure ownership is relinquished in the destructor. Lock objects - also expose functions to query the lock status and to manually lock - and unlock the read/write mutex object. - - Read/write lock objects are meant to be short lived, expected - to be used at block scope only. The read/write lock objects are not - thread-safe. - Read/write lock objects must maintain state to indicate whether or - not they've been locked and this state is not protected by any - synchronization concepts. For this reason a read/write lock object - should never be shared between multiple threads. - -
- ReadWriteLock Concept - - For a read/write lock type L - and an object lk - and const object clk of that type, - the following expressions must be well-formed - and have the indicated effects. - - - ReadWriteLock Expressions - - - - - Expression - Effects - - - - - - (&lk)->~L(); - if (locked()) unlock(); - - - (&clk)->operator const void*() - Returns type void*, non-zero if the associated read/write - mutex object has been either read-locked or write-locked by - clk, otherwise 0. - - - clk.locked() - Returns a bool, (&clk)->operator - const void*() != 0 - - - clk.state() - Returns an enumeration constant of type read_write_lock_state: - read_write_lock_state::read_locked if the associated read/write mutex object has been - read-locked by clk, read_write_lock_state::write_locked if it - has been write-locked by clk, and read_write_lock_state::unlocked - if has not been locked by clk. - - - clk.read_locked() - Returns a bool, (&clk)->state() == read_write_lock_state::read_locked. - - - clk.write_locked() - Returns a bool, (&clk)->state() == read_write_lock_state::write_locked. - - - lk.read_lock() - - Throws boost::lock_error - if locked(). - - If the associated read/write mutex - object is already read-locked by some other - thread, the effect depends on the - inter-class scheduling policy - of the associated read/write mutex: - either immediately obtains an additional - read-lock on the associated read/write - mutex, or places the current thread in the - Blocked - state until the associated read/write mutex - is unlocked, after which the current thread - is placed in the - Ready - state, eventually to be returned to the - Running - state. - - If the associated read/write mutex - object is already write-locked by some other - thread, places the current thread in the - Blocked - state until the associated read/write mutex - is unlocked, after which the current thread - is placed in the - Ready - state, eventually to be returned to the - Running - state. - - If the associated read/write mutex - object is already locked by the same thread - the behavior is dependent on the - locking strategy - of the associated read/write mutex object. - - - Postcondition: state() == read_write_lock_state::read_locked - - - - lk.write_lock() - - - Throws boost::lock_error - if locked(). - - If the associated read/write mutex - object is already locked by some other - thread, places the current thread in the - Blocked - state until the associated read/write mutex - is unlocked, after which the current thread - is placed in the - Ready - state, eventually to be returned to the - Running - state. - - If the associated read/write mutex - object is already locked by the same thread - the behavior is dependent on the - locking strategy - of the associated read/write mutex object. - - - Postcondition: state() == read_write_lock_state::write_locked - - - - lk.demote() - - Throws boost::lock_error - if state() != read_write_lock_state::write_locked. - - Converts the lock held on the associated read/write mutex - object from a write-lock to a read-lock without releasing - the lock. - - Postcondition: state() == read_write_lock_state::read_locked - - - - lk.promote() - - Throws boost::lock_error - if state() != read_write_lock_state::read_locked - or if the lock cannot be promoted because another lock - on the same mutex is already waiting to be promoted. - - Makes a blocking attempt to convert the lock held on the associated - read/write mutex object from a read-lock to a write-lock without releasing - the lock. - - - - lk.unlock() - - Throws boost::lock_error - if !locked(). - - Unlocks the associated read/write mutex. - - Postcondition: !locked() - - - - -
-
- -
- ScopedReadWriteLock Concept - - A ScopedReadWriteLock is a refinement of - ReadWriteLock. - For a ScopedReadWriteLock type L - and an object lk of that type, - and an object m of a type meeting the - ReadWriteMutex requirements, - and an object s of type read_write_lock_state, - the following expressions must be well-formed - and have the indicated effects. - - - ScopedReadWriteLock Expressions - - - - - Expression - Effects - - - - - - L lk(m,s); - Constructs an object lk and associates read/write mutex - object m with it, then: if s == read_write_lock_state::read_locked, calls - read_lock(); if s==read_write_lock_state::write_locked, - calls write_lock(). - - - -
-
- -
- TryReadWriteLock Expressions - - A TryReadWriteLock is a refinement of - ReadWriteLock. - For a TryReadWriteLock type L - and an object lk of that type, - the following expressions must be well-formed - and have the indicated effects. - - - TryReadWriteLock Expressions - - - - - Expression - Effects - - - - - - lk.try_read_lock() - - Throws boost::lock_error - if locked(). - - Makes a non-blocking attempt to read-lock the associated read/write - mutex object, returning true if the attempt is successful, - otherwise false. If the associated read/write mutex object is - already locked by the same thread the behavior is dependent on the - locking - strategy of the associated read/write mutex object. - - - - lk.try_write_lock() - - Throws boost::lock_error - if locked(). - - Makes a non-blocking attempt to write-lock the associated read/write - mutex object, returning true if the attempt is successful, - otherwise false. If the associated read/write mutex object is - already locked by the same thread the behavior is dependent on the - locking - strategy of the associated read/write mutex object. - - - - lk.try_demote() - - Throws boost::lock_error - if state() != read_write_lock_state::write_locked. - - Makes a non-blocking attempt to convert the lock held on the associated - read/write mutex object from a write-lock to a read-lock without releasing - the lock, returning true if the attempt is successful, - otherwise false. - - - - lk.try_promote() - - Throws boost::lock_error - if state() != read_write_lock_state::read_locked. - - Makes a non-blocking attempt to convert the lock held on the associated - read/write mutex object from a read-lock to a write-lock without releasing - the lock, returning true if the attempt is successful, - otherwise false. - - - - -
-
- -
- ScopedTryReadWriteLock Expressions - - A ScopedTryReadWriteLock is a refinement of - TryReadWriteLock. - For a ScopedTryReadWriteLock type L - and an object lk of that type, - and an object m of a type meeting the - TryReadWriteMutex requirements, - and an object s of type read_write_lock_state, - and an object b of type blocking_mode, - the following expressions must be well-formed - and have the indicated effects. - - - ScopedTryReadWriteLock Expressions - - - - - Expression - Effects - - - - - - L lk(m,s,b); - Constructs an object lk and associates read/write mutex - object m with it, then: if s == read_write_lock_state::read_locked, calls - read_lock() if b, otherwise try_read_lock(); - if s==read_write_lock_state::write_locked, calls write_lock() if b, - otherwise try_write_lock. - - - -
-
- -
- TimedReadWriteLock Concept - - A TimedReadWriteLock is a refinement of - TryReadWriteLock. - For a TimedReadWriteLock type L - and an object lk of that type, - and an object t of type boost::xtime, - the following expressions must be well-formed - and have the indicated effects. - - - TimedReadWriteLock Expressions - - - - - Expression - Effects - - - - - - lk.timed_read_lock(t) - - Throws boost::lock_error - if locked(). - - Makes a blocking attempt to read-lock the associated read/write mutex object, - and returns true if successful within the specified time t, - otherwise false. If the associated read/write mutex object is already - locked by the same thread the behavior is dependent on the locking - strategy of the associated read/write mutex object. - - - - lk.timed_write_lock(t) - - Throws boost::lock_error - if locked(). - - Makes a blocking attempt to write-lock the associated read/write mutex object, - and returns true if successful within the specified time t, - otherwise false. If the associated read/write mutex object is already - locked by the same thread the behavior is dependent on the locking - strategy of the associated read/write mutex object. - - - - lk.timed_demote(t) - - Throws boost::lock_error - if state() != read_write_lock_state::write_locked. - - Makes a blocking attempt to convert the lock held on the associated - read/write mutex object from a write-lock to a read-lock without releasing - the lock, returning true if the attempt is successful - in the specified time t, otherwise false. - - - - lk.timed_promote(t) - - Throws boost::lock_error - if state() != read_write_lock_state::read_locked. - - Makes a blocking attempt to convert the lock held on the associated - read/write mutex object from a read-lock to a write-lock without releasing - the lock, returning true if the attempt is successful - in the specified time t, otherwise false. - - - - -
-
- -
- ScopedTimedReadWriteLock Concept - - A ScopedTimedReadWriteLock is a refinement of - TimedReadWriteLock. - For a ScopedTimedReadWriteLock type L - and an object lk of that type, - and an object m of a type meeting the - TimedReadWriteMutex requirements, - and an object s of type read_write_lock_state, - and an object t of type boost::xtime, - and an object b of type blocking_mode, - the following expressions must be well-formed and have the - indicated effects. - - - ScopedTimedReadWriteLock Expressions - - - - - Expression - Effects - - - - - - L lk(m,s,b); - Constructs an object lk and associates read/write mutex - object m with it, then: if s == read_write_lock_state::read_locked, calls - read_lock() if b, otherwise try_read_lock(); - if s==read_write_lock_state::write_locked, calls write_lock() if b, - otherwise try_write_lock. - - - L lk(m,s,t); - Constructs an object lk and associates read/write mutex - object m with it, then: if s == read_write_lock_state::read_locked, calls - timed_read_lock(t); if s==read_write_lock_state::write_locked, - calls timed_write_lock(t). - - - -
-
-
- -
- Lock Models - - &Boost.Thread; currently supplies six models of - ReadWriteLock - and its refinements. - - - Lock Models - - - - - Concept - Refines - Models - - - - - - ReadWriteLock - - - - - ScopedReadWriteLock - ReadWriteLock - - boost::read_write_mutex::scoped_read_write_lock - boost::try_read_write_mutex::scoped_read_write_lock - boost::timed_read_write_mutex::scoped_read_write_lock - - - - TryReadWriteLock - ReadWriteLock - - - - ScopedTryReadWriteLock - TryReadWriteLock - - boost::try_read_write_mutex::scoped_try_read_write_lock - boost::timed_read_write_mutex::scoped_try_read_write_lock - - - - TimedReadWriteLock - TryReadWriteLock - - - - ScopedTimedReadWriteLock - TimedReadWriteLock - - boost::timed_read_write_mutex::scoped_timed_read_write_lock - - - - -
-
-
-
diff --git a/doc/condition-ref.xml b/doc/condition-ref.xml deleted file mode 100644 index 51b3b1d0..00000000 --- a/doc/condition-ref.xml +++ /dev/null @@ -1,196 +0,0 @@ - - - %thread.entities; -]> - -
- - - - boost::noncopyable - Exposition only - - - - An object of class condition is a - synchronization primitive used to cause a thread to wait until a - particular shared-data condition (or time) is met. - - - - A condition object is always used in - conjunction with a mutex - object (an object whose type is a model of a Mutex or one of its - refinements). The mutex object must be locked prior to waiting on the - condition, which is verified by passing a lock object (an object whose - type is a model of Lock or - one of its refinements) to the condition object's - wait functions. Upon blocking on the condition - object, the thread unlocks the mutex object. When the thread returns - from a call to one of the condition object's wait - functions the mutex object is again locked. The tricky unlock/lock - sequence is performed automatically by the - condition object's wait functions. - The condition type is often used to - implement the Monitor Object and other important patterns (see - &cite.SchmidtStalRohnertBuschmann; and &cite.Hoare74;). Monitors are one - of the most important patterns for creating reliable multithreaded - programs. - See for definitions of thread states - blocked and ready. Note that "waiting" is a synonym for blocked. - - - - Constructs a condition - object. - - - - Destroys *this. - - - - - void - If there is a thread waiting on *this, - change that thread's state to ready. Otherwise there is no - effect. - If more than one thread is waiting on *this, - it is unspecified which is made ready. After returning to a ready - state the notified thread must still acquire the mutex again (which - occurs within the call to one of the condition - object's wait functions.) - - - - void - Change the state of all threads waiting on - *this to ready. If there are no waiting threads, - notify_all() has no effect. - - - - - - - - void - - - ScopedLock& - - - ScopedLock meets the ScopedLock - requirements. - Releases the lock on the mutex object - associated with lock, blocks the current thread of execution - until readied by a call to this->notify_one() - or this->notify_all(), and then reacquires the - lock. - lock_error if - !lock.locked() - - - - - - void - - - ScopedLock& - - - - Pred - - - ScopedLock meets the ScopedLock - requirements and the return from pred() is - convertible to bool. - As if: while (!pred()) - wait(lock) - lock_error if - !lock.locked() - - - - - - bool - - - ScopedLock& - - - - const boost::xtime& - - - ScopedLock meets the ScopedLock - requirements. - Releases the lock on the mutex object - associated with lock, blocks the current thread of execution - until readied by a call to this->notify_one() - or this->notify_all(), or until time xt - is reached, and then reacquires the lock. - false if time xt is reached, - otherwise true. - lock_error if - !lock.locked() - - - - - - bool - - - ScopedLock& - - - - const boost::xtime& - - - - Pred - - - ScopedLock meets the ScopedLock - requirements and the return from pred() is - convertible to bool. - As if: while (!pred()) { if (!timed_wait(lock, - xt)) return false; } return true; - false if xt is reached, - otherwise true. - lock_error if - !lock.locked() - - - - -
diff --git a/doc/condition_variables.qbk b/doc/condition_variables.qbk new file mode 100644 index 00000000..e793a777 --- /dev/null +++ b/doc/condition_variables.qbk @@ -0,0 +1,494 @@ +[section:condvar_ref Condition Variables] + +[heading Synopsis] + +The classes `condition_variable` and `condition_variable_any` provide a +mechanism for one thread to wait for notification from another thread that a +particular condition has become true. The general usage pattern is that one +thread locks a mutex and then calls `wait` on an instance of +`condition_variable` or `condition_variable_any`. When the thread is woken from +the wait, then it checks to see if the appropriate condition is now true, and +continues if so. If the condition is not true, then the thread then calls `wait` +again to resume waiting. In the simplest case, this condition is just a boolean +variable: + + boost::condition_variable cond; + boost::mutex mut; + bool data_ready; + + void process_data(); + + void wait_for_data_to_process() + { + boost::unique_lock lock(mut); + while(!data_ready) + { + cond.wait(lock); + } + process_data(); + } + +Notice that the `lock` is passed to `wait`: `wait` will atomically add the +thread to the set of threads waiting on the condition variable, and unlock the +mutex. When the thread is woken, the mutex will be locked again before the call +to `wait` returns. This allows other threads to acquire the mutex in order to +update the shared data, and ensures that the data associated with the condition +is correctly synchronized. + +In the mean time, another thread sets the condition to `true`, and then calls +either `notify_one` or `notify_all` on the condition variable to wake one +waiting thread or all the waiting threads respectively. + + void retrieve_data(); + void prepare_data(); + + void prepare_data_for_processing() + { + retrieve_data(); + prepare_data(); + { + boost::lock_guard lock(mut); + data_ready=true; + } + cond.notify_one(); + } + +Note that the same mutex is locked before the shared data is updated, but that +the mutex does not have to be locked across the call to `notify_one`. + +This example uses an object of type `condition_variable`, but would work just as +well with an object of type `condition_variable_any`: `condition_variable_any` +is more general, and will work with any kind of lock or mutex, whereas +`condition_variable` requires that the lock passed to `wait` is an instance of +`boost::unique_lock`. This enables `condition_variable` to make +optimizations in some cases, based on the knowledge of the mutex type; +`condition_variable_any` typically has a more complex implementation than +`condition_variable`. + +[section:condition_variable Class `condition_variable`] + + namespace boost + { + class condition_variable + { + public: + condition_variable(); + ~condition_variable(); + + void wait(boost::unique_lock& lock); + + template + void wait(boost::unique_lock& lock,predicate_type predicate); + + bool timed_wait(boost::unique_lock& lock,boost::system_time const& abs_time); + + template + bool timed_wait(boost::unique_lock& lock,duration_type const& rel_time); + + template + bool timed_wait(boost::unique_lock& lock,boost::system_time const& abs_time,predicate_type predicate); + + template + bool timed_wait(boost::unique_lock& lock,duration_type const& rel_time,predicate_type predicate); + + // backwards compatibility + + bool timed_wait(boost::unique_lock& lock,boost::xtime const& abs_time); + + template + bool timed_wait(boost::unique_lock& lock,boost::xtime const& abs_time,predicate_type predicate); + }; + } + +[section:constructor `condition_variable()`] + +[variablelist + +[[Effects:] [Constructs an object of class `condition_variable`.]] + +[[Throws:] [__thread_resource_error__ if an error occurs.]] + +] + +[endsect] + +[section:destructor `~condition_variable()`] + +[variablelist + +[[Precondition:] [All threads waiting on `*this` have been notified by a call to +`notify_one` or `notify_all` (though the respective calls to `wait` or +`timed_wait` need not have returned).]] + +[[Effects:] [Destroys the object.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:notify_one `void notify_one()`] + +[variablelist + +[[Effects:] [If any threads are currently __blocked__ waiting on `*this` in a call +to `wait` or `timed_wait`, unblocks one of those threads.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:notify_all `void notify_all()`] + +[variablelist + +[[Effects:] [If any threads are currently __blocked__ waiting on `*this` in a call +to `wait` or `timed_wait`, unblocks all of those threads.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:wait `void wait(boost::unique_lock& lock)`] + +[variablelist + +[[Precondition:] [`lock` is locked by the current thread, and either no other +thread is currently waiting on `*this`, or the execution of the `mutex()` member +function on the `lock` objects supplied in the calls to `wait` or `timed_wait` +in all the threads currently waiting on `*this` would return the same value as +`lock->mutex()` for this call to `wait`.]] + +[[Effects:] [Atomically call `lock.unlock()` and blocks the current thread. The +thread will unblock when notified by a call to `this->notify_one()` or +`this->notify_all()`, or spuriously. When the thread is unblocked (for whatever +reason), the lock is reacquired by invoking `lock.lock()` before the call to +`wait` returns. The lock is also reacquired by invoking `lock.lock()` if the +function exits with an exception.]] + +[[Postcondition:] [`lock` is locked by the current thread.]] + +[[Throws:] [__thread_resource_error__ if an error +occurs. __thread_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __thread__ object associated with the current thread of execution.]] + +] + +[endsect] + +[section:wait_predicate `template void wait(boost::unique_lock& lock, predicate_type pred)`] + +[variablelist + +[[Effects:] [As-if `` +while(!pred()) +{ + wait(lock); +} +``]] + +] + +[endsect] + +[section:timed_wait `bool timed_wait(boost::unique_lock& lock,boost::system_time const& abs_time)`] + +[variablelist + +[[Precondition:] [`lock` is locked by the current thread, and either no other +thread is currently waiting on `*this`, or the execution of the `mutex()` member +function on the `lock` objects supplied in the calls to `wait` or `timed_wait` +in all the threads currently waiting on `*this` would return the same value as +`lock->mutex()` for this call to `wait`.]] + +[[Effects:] [Atomically call `lock.unlock()` and blocks the current thread. The +thread will unblock when notified by a call to `this->notify_one()` or +`this->notify_all()`, when the time as reported by `boost::get_system_time()` +would be equal to or later than the specified `abs_time`, or spuriously. When +the thread is unblocked (for whatever reason), the lock is reacquired by +invoking `lock.lock()` before the call to `wait` returns. The lock is also +reacquired by invoking `lock.lock()` if the function exits with an exception.]] + +[[Returns:] [`false` if the call is returning because the time specified by +`abs_time` was reached, `true` otherwise.]] + +[[Postcondition:] [`lock` is locked by the current thread.]] + +[[Throws:] [__thread_resource_error__ if an error +occurs. __thread_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __thread__ object associated with the current thread of execution.]] + +] + +[endsect] + +[section:timed_wait_rel `template bool timed_wait(boost::unique_lock& lock,duration_type const& rel_time)`] + +[variablelist + +[[Precondition:] [`lock` is locked by the current thread, and either no other +thread is currently waiting on `*this`, or the execution of the `mutex()` member +function on the `lock` objects supplied in the calls to `wait` or `timed_wait` +in all the threads currently waiting on `*this` would return the same value as +`lock->mutex()` for this call to `wait`.]] + +[[Effects:] [Atomically call `lock.unlock()` and blocks the current thread. The +thread will unblock when notified by a call to `this->notify_one()` or +`this->notify_all()`, after the period of time indicated by the `rel_time` +argument has elapsed, or spuriously. When the thread is unblocked (for whatever +reason), the lock is reacquired by invoking `lock.lock()` before the call to +`wait` returns. The lock is also reacquired by invoking `lock.lock()` if the +function exits with an exception.]] + +[[Returns:] [`false` if the call is returning because the time period specified +by `rel_time` has elapsed, `true` otherwise.]] + +[[Postcondition:] [`lock` is locked by the current thread.]] + +[[Throws:] [__thread_resource_error__ if an error +occurs. __thread_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __thread__ object associated with the current thread of execution.]] + +] + +[note The duration overload of timed_wait is difficult to use correctly. The overload taking a predicate should be preferred in most cases.] + +[endsect] + +[section:timed_wait_predicate `template bool timed_wait(boost::unique_lock& lock, boost::system_time const& abs_time, predicate_type pred)`] + +[variablelist + +[[Effects:] [As-if `` +while(!pred()) +{ + if(!timed_wait(lock,abs_time)) + { + return pred(); + } +} +return true; +``]] + +] + +[endsect] + + +[endsect] + +[section:condition_variable_any Class `condition_variable_any`] + + namespace boost + { + class condition_variable_any + { + public: + condition_variable_any(); + ~condition_variable_any(); + + template + void wait(lock_type& lock); + + template + void wait(lock_type& lock,predicate_type predicate); + + template + bool timed_wait(lock_type& lock,boost::system_time const& abs_time); + + template + bool timed_wait(lock_type& lock,duration_type const& rel_time); + + template + bool timed_wait(lock_type& lock,boost::system_time const& abs_time,predicate_type predicate); + + template + bool timed_wait(lock_type& lock,duration_type const& rel_time,predicate_type predicate); + + // backwards compatibility + + template + bool timed_wait(lock_type>& lock,boost::xtime const& abs_time); + + template + bool timed_wait(lock_type& lock,boost::xtime const& abs_time,predicate_type predicate); + }; + } + +[section:constructor `condition_variable_any()`] + +[variablelist + +[[Effects:] [Constructs an object of class `condition_variable_any`.]] + +[[Throws:] [__thread_resource_error__ if an error occurs.]] + +] + +[endsect] + +[section:destructor `~condition_variable_any()`] + +[variablelist + +[[Precondition:] [All threads waiting on `*this` have been notified by a call to +`notify_one` or `notify_all` (though the respective calls to `wait` or +`timed_wait` need not have returned).]] + +[[Effects:] [Destroys the object.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:notify_one `void notify_one()`] + +[variablelist + +[[Effects:] [If any threads are currently __blocked__ waiting on `*this` in a call +to `wait` or `timed_wait`, unblocks one of those threads.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:notify_all `void notify_all()`] + +[variablelist + +[[Effects:] [If any threads are currently __blocked__ waiting on `*this` in a call +to `wait` or `timed_wait`, unblocks all of those threads.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:wait `template void wait(lock_type& lock)`] + +[variablelist + +[[Effects:] [Atomically call `lock.unlock()` and blocks the current thread. The +thread will unblock when notified by a call to `this->notify_one()` or +`this->notify_all()`, or spuriously. When the thread is unblocked (for whatever +reason), the lock is reacquired by invoking `lock.lock()` before the call to +`wait` returns. The lock is also reacquired by invoking `lock.lock()` if the +function exits with an exception.]] + +[[Postcondition:] [`lock` is locked by the current thread.]] + +[[Throws:] [__thread_resource_error__ if an error +occurs. __thread_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __thread__ object associated with the current thread of execution.]] + +] + +[endsect] + +[section:wait_predicate `template void wait(lock_type& lock, predicate_type pred)`] + +[variablelist + +[[Effects:] [As-if `` +while(!pred()) +{ + wait(lock); +} +``]] + +] + +[endsect] + +[section:timed_wait `template bool timed_wait(lock_type& lock,boost::system_time const& abs_time)`] + +[variablelist + +[[Effects:] [Atomically call `lock.unlock()` and blocks the current thread. The +thread will unblock when notified by a call to `this->notify_one()` or +`this->notify_all()`, when the time as reported by `boost::get_system_time()` +would be equal to or later than the specified `abs_time`, or spuriously. When +the thread is unblocked (for whatever reason), the lock is reacquired by +invoking `lock.lock()` before the call to `wait` returns. The lock is also +reacquired by invoking `lock.lock()` if the function exits with an exception.]] + +[[Returns:] [`false` if the call is returning because the time specified by +`abs_time` was reached, `true` otherwise.]] + +[[Postcondition:] [`lock` is locked by the current thread.]] + +[[Throws:] [__thread_resource_error__ if an error +occurs. __thread_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __thread__ object associated with the current thread of execution.]] + +] + +[endsect] + +[section:timed_wait_rel `template bool timed_wait(lock_type& lock,duration_type const& rel_time)`] + +[variablelist + +[[Effects:] [Atomically call `lock.unlock()` and blocks the current thread. The +thread will unblock when notified by a call to `this->notify_one()` or +`this->notify_all()`, after the period of time indicated by the `rel_time` +argument has elapsed, or spuriously. When the thread is unblocked (for whatever +reason), the lock is reacquired by invoking `lock.lock()` before the call to +`wait` returns. The lock is also reacquired by invoking `lock.lock()` if the +function exits with an exception.]] + +[[Returns:] [`false` if the call is returning because the time period specified +by `rel_time` has elapsed, `true` otherwise.]] + +[[Postcondition:] [`lock` is locked by the current thread.]] + +[[Throws:] [__thread_resource_error__ if an error +occurs. __thread_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __thread__ object associated with the current thread of execution.]] + +] + +[note The duration overload of timed_wait is difficult to use correctly. The overload taking a predicate should be preferred in most cases.] + +[endsect] + +[section:timed_wait_predicate `template bool timed_wait(lock_type& lock, boost::system_time const& abs_time, predicate_type pred)`] + +[variablelist + +[[Effects:] [As-if `` +while(!pred()) +{ + if(!timed_wait(lock,abs_time)) + { + return pred(); + } +} +return true; +``]] + +] + +[endsect] + +[endsect] + +[section:condition Typedef `condition`] + + typedef condition_variable_any condition; + +The typedef `condition` is provided for backwards compatibility with previous boost releases. + +[endsect] + +[endsect] diff --git a/doc/configuration.xml b/doc/configuration.xml deleted file mode 100644 index bb42d665..00000000 --- a/doc/configuration.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - %thread.entities; -]> - -
- Configuration - &Boost.Thread; uses several configuration macros in <boost/config.hpp>, - as well as configuration macros meant to be supplied by the application. These - macros are documented here. - -
- Library Defined Public Macros - - These macros are defined by &Boost.Thread; but are expected to be used - by application code. - - - - - - Macro - Meaning - - - - - BOOST_HAS_THREADS - - Indicates that threading support is available. This means both that there - is a platform specific implementation for &Boost.Thread; and that - threading support has been enabled in a platform specific manner. For instance, - on the Win32 platform there's an implementation for &Boost.Thread; - but unless the program is compiled against one of the multithreading runtimes - (often determined by the compiler predefining the macro _MT) the BOOST_HAS_THREADS - macro remains undefined. - - - - - -
-
- Library Defined Implementation Macros - - These macros are defined by &Boost.Thread; and are implementation details - of interest only to implementors. - - - - - - Macro - Meaning - - - - - BOOST_HAS_WINTHREADS - - Indicates that the platform has the Microsoft Win32 threading libraries, - and that they should be used to implement &Boost.Thread;. - - - - BOOST_HAS_PTHREADS - - Indicates that the platform has the POSIX pthreads libraries, and that - they should be used to implement &Boost.Thread;. - - - - BOOST_HAS_FTIME - - Indicates that the implementation should use GetSystemTimeAsFileTime() - and the FILETIME type to calculate the current time. This is an implementation - detail used by boost::detail::getcurtime(). - - - - BOOST_HAS_GETTTIMEOFDAY - - Indicates that the implementation should use gettimeofday() to calculate - the current time. This is an implementation detail used by boost::detail::getcurtime(). - - - - - -
-
diff --git a/doc/design.xml b/doc/design.xml deleted file mode 100644 index c9b58480..00000000 --- a/doc/design.xml +++ /dev/null @@ -1,159 +0,0 @@ - - - %thread.entities; -]> - -
- Design - With client/server and three-tier architectures becoming common place - in today's world, it's becoming increasingly important for programs to be - able to handle parallel processing. Modern day operating systems usually - provide some support for this through native thread APIs. Unfortunately, - writing portable code that makes use of parallel processing in C++ is made - very difficult by a lack of a standard interface for these native APIs. - Further, these APIs are almost universally C APIs and fail to take - advantage of C++'s strengths, or to address concepts unique to C++, such as - exceptions. - The &Boost.Thread; library is an attempt to define a portable interface - for writing parallel processes in C++. -
- Goals - The &Boost.Thread; library has several goals that should help to set - it apart from other solutions. These goals are listed in order of precedence - with full descriptions below. - - - Portability - - &Boost.Thread; was designed to be highly portable. The goal is - for the interface to be easily implemented on any platform that - supports threads, and possibly even on platforms without native thread - support. - - - - Safety - - &Boost.Thread; was designed to be as safe as possible. Writing - thread-safe - code is very difficult and successful libraries must strive to - insulate the programmer from dangerous constructs as much as - possible. This is accomplished in several ways: - - - C++ language features are used to make correct usage easy - (if possible) and error-prone usage impossible or at least more - difficult. For example, see the Mutex and Lock designs, and note - how they interact. - - - Certain traditional concurrent programming features are - considered so error-prone that they are not provided at all. For - example, see . - - - Dangerous features, or features which may be misused, are - identified as such in the documentation to make users aware of - potential pitfalls. - - - - - - Flexibility - - &Boost.Thread; was designed to be flexible. This goal is often - at odds with safety. When functionality might be - compromised by the desire to keep the interface safe, &Boost.Thread; - has been designed to provide the functionality, but to make it's use - prohibitive for general use. In other words, the interfaces have been - designed such that it's usually obvious when something is unsafe, and - the documentation is written to explain why. - - - - Efficiency - - &Boost.Thread; was designed to be as efficient as - possible. When building a library on top of another library there is - always a danger that the result will be so much slower than the - "native" API that programmers are inclined to ignore the higher level - API. &Boost.Thread; was designed to minimize the chances of this - occurring. The interfaces have been crafted to allow an implementation - the greatest chance of being as efficient as possible. This goal is - often at odds with the goal for safety. Every - effort was made to ensure efficient implementations, but when in - conflict safety has always taken - precedence. - - - -
-
- Iterative Phases - Another goal of &Boost.Thread; was to take a dynamic, iterative - approach in its development. The computing industry is still exploring the - concepts of parallel programming. Most thread libraries supply only simple - primitive concepts for thread synchronization. These concepts are very - simple, but it is very difficult to use them safely or to provide formal - proofs for constructs built on top of them. There has been a lot of research - into other concepts, such as in "Communicating Sequential Processes." - &Boost.Thread; was designed in iterative steps, with each step providing - the building blocks necessary for the next step and giving the researcher - the tools necessary to explore new concepts in a portable manner. - Given the goal of following a dynamic, iterative approach - &Boost.Thread; shall go through several growth cycles. Each phase in its - development shall be roughly documented here. -
-
- Phase 1, Synchronization Primitives - Boost is all about providing high quality libraries with - implementations for many platforms. Unfortunately, there's a big problem - faced by developers wishing to supply such high quality libraries, namely - thread-safety. The C++ standard doesn't address threads at all, but real - world programs often make use of native threading support. A portable - library that doesn't address the issue of thread-safety is therefore not - much help to a programmer who wants to use the library in his multithreaded - application. So there's a very great need for portable primitives that will - allow the library developer to create thread-safe - implementations. This need far out weighs the need for portable methods to - create and manage threads. - Because of this need, the first phase of &Boost.Thread; focuses - solely on providing portable primitive concepts for thread - synchronization. Types provided in this phase include the - boost::mutex, - boost::try_mutex, - boost::timed_mutex, - boost::recursive_mutex, - boost::recursive_try_mutex, - boost::recursive_timed_mutex, and - boost::lock_error. These are considered the "core" - synchronization primitives, though there are others that will be added in - later phases. -
-
- Phase 2, Thread Management and Thread Specific Storage - This phase addresses the creation and management of threads and - provides a mechanism for thread specific storage (data associated with a - thread instance). Thread management is a tricky issue in C++, so this - phase addresses only the basic needs of multithreaded program. Later - phases are likely to add additional functionality in this area. This - phase of &Boost.Thread; adds the boost::thread and - boost::thread_specific_ptr types. With these - additions the &Boost.Thread; library can be considered minimal but - complete. -
-
- The Next Phase - The next phase will address more advanced synchronization concepts, - such as read/write mutexes and barriers. -
-
diff --git a/doc/entities.xml b/doc/entities.xml deleted file mode 100644 index fbfa6d21..00000000 --- a/doc/entities.xml +++ /dev/null @@ -1,31 +0,0 @@ - -Boost"> -Boost.Thread"> -Boost.Build"> -"> -"> -"> -"> -"> -"> -"> -"> -"> -"> diff --git a/doc/exceptions-ref.xml b/doc/exceptions-ref.xml deleted file mode 100644 index 0ae42e8b..00000000 --- a/doc/exceptions-ref.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - %thread.entities; -]> - -
- - - - The lock_error class defines an exception type thrown - to indicate a locking related error has been detected. - - - - Examples of errors indicated by a lock_error exception - include a lock operation which can be determined to result in a - deadlock, or unlock operations attempted by a thread that does - not own the lock. - - - - std::logical_error - - - - Constructs a lock_error object. - - - - - - - The thread_resource_error class - defines an exception type that is thrown by constructors in the - &Boost.Thread; library when thread-related resources can not be - acquired. - - - - thread_resource_error is used - only when thread-related resources cannot be acquired; memory - allocation failures are indicated by - std::bad_alloc. - - - - std::runtime_error - - - - Constructs a thread_resource_error - object. - - - -
diff --git a/doc/faq.xml b/doc/faq.xml deleted file mode 100644 index 482d6811..00000000 --- a/doc/faq.xml +++ /dev/null @@ -1,235 +0,0 @@ - - - %thread.entities; -]> - -
- Frequently Asked Questions - - - - Are lock objects thread safe? - - - No! Lock objects are not meant to - be shared between threads. They are meant to be short-lived objects - created on automatic storage within a code block. Any other usage is - just likely to lead to errors and won't really be of actual benefit anyway. - Share Mutexes, not - Locks. For more information see the rationale behind the - design for lock objects. - - - - - Why was &Boost.Thread; modeled after (specific library - name)? - - - It wasn't. &Boost.Thread; was designed from scratch. Extensive - design discussions involved numerous people representing a wide range of - experience across many platforms. To ensure portability, the initial - implements were done in parallel using POSIX Threads and the Win32 - threading API. But the &Boost.Thread; design is very much in the spirit - of C++, and thus doesn't model such C based APIs. - - - - - Why wasn't &Boost.Thread; modeled after (specific library - name)? - - - Existing C++ libraries either seemed dangerous (often failing to - take advantage of prior art to reduce errors) or had excessive - dependencies on library components unrelated to threading. Existing C - libraries couldn't meet our C++ requirements, and were also missing - certain features. For instance, the WIN32 thread API lacks condition - variables, even though these are critical for the important Monitor - pattern &cite.SchmidtStalRohnertBuschmann;. - - - - - Why do Mutexes - have noncopyable semantics? - - - To ensure that deadlocks don't occur. The - only logical form of copy would be to use some sort of shallow copy - semantics in which multiple mutex objects could refer to the same mutex - state. This means that if ObjA has a mutex object as part of its state - and ObjB is copy constructed from it, then when ObjB::foo() locks the - mutex it has effectively locked ObjA as well. This behavior can result - in deadlock. Other copy semantics result in similar problems (if you - think you can prove this to be wrong then supply us with an alternative - and we'll reconsider). - - - - - How can you prevent deadlock from occurring when - a thread must lock multiple mutexes? - - - Always lock them in the same order. One easy way of doing this is - to use each mutex's address to determine the order in which they are - locked. A future &Boost.Thread; concept may wrap this pattern up in a - reusable class. - - - - - Don't noncopyable Mutex semantics mean that a - class with a mutex member will be noncopyable as well? - - - No, but what it does mean is that the compiler can't generate a - copy constructor and assignment operator, so they will have to be coded - explicitly. This is a good thing, - however, since the compiler generated operations would not be thread-safe. The following - is a simple example of a class with copyable semantics and internal - synchronization through a mutex member. - -class counter -{ -public: - // Doesn't need synchronization since there can be no references to *this - // until after it's constructed! - explicit counter(int initial_value) - : m_value(initial_value) - { - } - // We only need to synchronize other for the same reason we don't have to - // synchronize on construction! - counter(const counter& other) - { - boost::mutex::scoped_lock scoped_lock(other.m_mutex); - m_value = other.m_value; - } - // For assignment we need to synchronize both objects! - const counter& operator=(const counter& other) - { - if (this == &other) - return *this; - boost::mutex::scoped_lock lock1(&m_mutex < &other.m_mutex ? m_mutex : other.m_mutex); - boost::mutex::scoped_lock lock2(&m_mutex > &other.m_mutex ? m_mutex : other.m_mutex); - m_value = other.m_value; - return *this; - } - int value() const - { - boost::mutex::scoped_lock scoped_lock(m_mutex); - return m_value; - } - int increment() - { - boost::mutex::scoped_lock scoped_lock(m_mutex); - return ++m_value; - } -private: - mutable boost::mutex m_mutex; - int m_value; -}; - - - - - - How can you lock a Mutex member in a const member - function, in order to implement the Monitor Pattern? - - - The Monitor Pattern &cite.SchmidtStalRohnertBuschmann; mutex - should simply be declared as mutable. See the example code above. The - internal state of mutex types could have been made mutable, with all - lock calls made via const functions, but this does a poor job of - documenting the actual semantics (and in fact would be incorrect since - the logical state of a locked mutex clearly differs from the logical - state of an unlocked mutex). Declaring a mutex member as mutable clearly - documents the intended semantics. - - - - - Why supply boost::condition variables rather than - event variables? - - - Condition variables result in user code much less prone to race conditions than - event variables. See - for analysis. Also see &cite.Hoare74; and &cite.SchmidtStalRohnertBuschmann;. - - - - - - Why isn't thread cancellation or termination provided? - - - There's a valid need for thread termination, so at some point - &Boost.Thread; probably will include it, but only after we can find a - truly safe (and portable) mechanism for this concept. - - - - - Is it safe for threads to share automatic storage duration (stack) - objects via pointers or references? - - - Only if you can guarantee that the lifetime of the stack object - will not end while other threads might still access the object. Thus the - safest practice is to avoid sharing stack objects, particularly in - designs where threads are created and destroyed dynamically. Restrict - sharing of stack objects to simple designs with very clear and - unchanging function and thread lifetimes. (Suggested by Darryl - Green). - - - - - Why has class semaphore disappeared? - - - Semaphore was removed as too error prone. The same effect can be - achieved with greater safety by the combination of a mutex and a - condition variable. - - - - - Why doesn't the thread's ctor take at least a void* to pass any - information along with the function? All other threading libs support - that and it makes Boost.Threads inferiour. - - - There is no need, because Boost.Threads are superiour! First - thing is that its ctor doesn't take a function but a functor. That - means that you can pass an object with an overloaded operator() and - include additional data as members in that object. Beware though that - this object is copied, use boost::ref to prevent that. Secondly, even - a boost::function<void (void)> can carry parameters, you only have to - use boost::bind() to create it from any function and bind its - parameters. - That is also why Boost.Threads are superiour, because they - don't require you to pass a type-unsafe void pointer. Rather, you can - use the flexible Boost.Functions to create a thread entry out of - anything that can be called. - - - -
diff --git a/doc/glossary.xml b/doc/glossary.xml deleted file mode 100644 index ad1c8442..00000000 --- a/doc/glossary.xml +++ /dev/null @@ -1,304 +0,0 @@ - - - %thread.entities; -]> - - - Glossary - Definitions are given in terms of the C++ Standard - &cite.ISO98;. References to the standard are in the form [1.2.3/4], which - represents the section number, with the paragraph number following the - "/". - Because the definitions are written in something akin to "standardese", - they can be difficult to understand. The intent isn't to confuse, but rather - to clarify the additional requirements &Boost.Thread; places on a C++ - implementation as defined by the C++ Standard. - - Thread - - Thread is short for "thread of execution". A thread of execution is - an execution environment [1.9/7] within the execution environment of a C++ - program [1.9]. The main() function [3.6.1] of the program is the initial - function of the initial thread. A program in a multithreading environment - always has an initial thread even if the program explicitly creates no - additional threads. - Unless otherwise specified, each thread shares all aspects of its - execution environment with other threads in the program. Shared aspects of - the execution environment include, but are not limited to, the - following: - - Static storage duration (static, extern) objects - [3.7.1]. - Dynamic storage duration (heap) objects [3.7.3]. Thus - each memory allocation will return a unique addresses, regardless of the - thread making the allocation request. - Automatic storage duration (stack) objects [3.7.2] - accessed via pointer or reference from another thread. - Resources provided by the operating system. For example, - files. - The program itself. In other words, each thread is - executing some function of the same program, not a totally different - program. - - Each thread has its own: - - Registers and current execution sequence (program - counter) [1.9/5]. - Automatic storage duration (stack) objects - [3.7.2]. - - - - - Thread-safe - - A program is thread-safe if it has no race conditions, does - not deadlock, and has - no priority - failures. - Note that thread-safety does not necessarily imply efficiency, and - than while some thread-safety violations can be determined statically at - compile time, many thread-safety errors can only only be detected at - runtime. - - - - Thread State - - During the lifetime of a thread, it shall be in one of the following - states: - - Thread States - - - - State - Description - - - - - Ready - Ready to run, but waiting for a processor. - - - Running - Currently executing on a processor. Zero or more threads - may be running at any time, with a maximum equal to the number of - processors. - - - Blocked - Waiting for some resource other than a processor which is - not currently available, or for the completion of calls to library - functions [1.9/6]. The term "waiting" is synonymous with - "blocked" - - - Terminated - Finished execution but not yet detached or joined. - - - -
- Thread state transitions shall occur only as specified: - - Thread States Transitions - - - - From - To - Cause - - - - - [none] - Ready - Thread is created by a call to a library function. - In the case of the initial thread, creation is implicit and - occurs during the startup of the main() function [3.6.1]. - - - Ready - Running - Processor becomes available. - - - Running - Ready - Thread preempted. - - - Running - Blocked - Thread calls a library function which waits for a resource or - for the completion of I/O. - - - Running - Terminated - Thread returns from its initial function, calls a thread - termination library function, or is canceled by some other thread - calling a thread termination library function. - - - Blocked - Ready - The resource being waited for becomes available, or the - blocking library function completes. - - - Terminated - [none] - Thread is detached or joined by some other thread calling the - appropriate library function, or by program termination - [3.6.3]. - - - -
- [Note: if a suspend() function is added to the threading library, - additional transitions to the blocked state will have to be added to the - above table.] -
-
- - Race Condition - - A race condition is what occurs when multiple threads read from and write - to the same memory without proper synchronization, resulting in an incorrect - value being read or written. The result of a race condition may be a bit - pattern which isn't even a valid value for the data type. A race condition - results in undefined behavior [1.3.12]. - Race conditions can be prevented by serializing memory access using - the tools provided by &Boost.Thread;. - - - - Deadlock - - Deadlock is an execution state where for some set of threads, each - thread in the set is blocked waiting for some action by one of the other - threads in the set. Since each is waiting on the others, none will ever - become ready again. - - - - Starvation - - The condition in which a thread is not making sufficient progress in - its work during a given time interval. - - - - Priority Failure - - A priority failure (such as priority inversion or infinite overtaking) - occurs when threads are executed in such a sequence that required work is not - performed in time to be useful. - - - - Undefined Behavior - - The result of certain operations in &Boost.Thread; is undefined; - this means that those operations can invoke almost any behavior when - they are executed. - - An operation whose behavior is undefined can work "correctly" - in some implementations (i.e., do what the programmer thought it - would do), while in other implementations it may exhibit almost - any "incorrect" behavior--such as returning an invalid value, - throwing an exception, generating an access violation, or terminating - the process. - - Executing a statement whose behavior is undefined is a - programming error. - - - - Memory Visibility - - An address [1.7] shall always point to the same memory byte, - regardless of the thread or processor dereferencing the address. - An object [1.8, 1.9] is accessible from multiple threads if it is of - static storage duration (static, extern) [3.7.1], or if a pointer or - reference to it is explicitly or implicitly dereferenced in multiple - threads. - For an object accessible from multiple threads, the value of the - object accessed from one thread may be indeterminate or different from the - value accessed from another thread, except under the conditions specified in - the following table. For the same row of the table, the value of an object - accessible at the indicated sequence point in thread A will be determinate - and the same if accessed at or after the indicated sequence point in thread - B, provided the object is not otherwise modified. In the table, the - "sequence point at a call" is the sequence point after the evaluation of all - function arguments [1.9/17], while the "sequence point after a call" is the - sequence point after the copying of the returned value... [1.9/17]. - - Memory Visibility - - - - Thread A - Thread B - - - - - The sequence point at a call to a library thread-creation - function. - The first sequence point of the initial function in the new - thread created by the Thread A call. - - - The sequence point at a call to a library function which - locks a mutex, directly or by waiting for a condition - variable. - The sequence point after a call to a library function which - unlocks the same mutex. - - - The last sequence point before thread termination. - The sequence point after a call to a library function which - joins the terminated thread. - - - The sequence point at a call to a library function which - signals or broadcasts a condition variable. - The sequence point after the call to the library function - which was waiting on that same condition variable or signal. - - - -
- The architecture of the execution environment and the observable - behavior of the abstract machine [1.9] shall be the same on all - processors. - The latitude granted by the C++ standard for an implementation to - alter the definition of observable behavior of the abstract machine to - include additional library I/O functions [1.9/6] is extended to include - threading library functions. - When an exception is thrown and there is no matching exception handler - in the same thread, behavior is undefined. The preferred behavior is the - same as when there is no matching exception handler in a program - [15.3/9]. That is, terminate() is called, and it is implementation-defined - whether or not the stack is unwound. -
-
-
- Acknowledgements - This document was originally written by Beman Dawes, and then much - improved by the incorporation of comments from William Kempf, who now - maintains the contents. - The visibility rules are based on &cite.Butenhof97;. -
-
diff --git a/doc/implementation_notes.xml b/doc/implementation_notes.xml deleted file mode 100644 index c097b070..00000000 --- a/doc/implementation_notes.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - %thread.entities; -]> - -
- Implementation Notes -
- Win32 - - In the current Win32 implementation, creating a boost::thread object - during dll initialization will result in deadlock because the thread - class constructor causes the current thread to wait on the thread that - is being created until it signals that it has finished its initialization, - and, as stated in the - MSDN Library, "DllMain" article, "Remarks" section, - "Because DLL notifications are serialized, entry-point functions should not - attempt to communicate with other threads or processes. Deadlocks may occur as a result." - (Also see "Under the Hood", January 1996 - for a more detailed discussion of this issue). - - - The following non-exhaustive list details some of the situations that - should be avoided until this issue can be addressed: - - Creating a boost::thread object in DllMain() or in any function called by it. - Creating a boost::thread object in the constructor of a global static object or in any function called by one. - Creating a boost::thread object in MFC's CWinApp::InitInstance() function or in any function called by it. - Creating a boost::thread object in the function pointed to by MFC's _pRawDllMain function pointer or in any function called by it. - - -
-
diff --git a/doc/mutex-ref.xml b/doc/mutex-ref.xml deleted file mode 100644 index 5faa2630..00000000 --- a/doc/mutex-ref.xml +++ /dev/null @@ -1,309 +0,0 @@ - - - %thread.entities; -]> - -
- - - - The mutex class is a model of the - Mutex concept. - - - - The mutex class is a model of the - Mutex concept. - It should be used to synchronize access to shared resources using - Unspecified - locking mechanics. - - For classes that model related mutex concepts, see - try_mutex and timed_mutex. - - For Recursive - locking mechanics, see recursive_mutex, - recursive_try_mutex, and recursive_timed_mutex. - - - The mutex class supplies the following typedef, - which models - the specified locking strategy: - - - - - - Lock Name - Lock Concept - - - - - scoped_lock - ScopedLock - - - - - - - The mutex class uses an - Unspecified - locking strategy, so attempts to recursively lock a mutex - object or attempts to unlock one by threads that don't own a lock on it result in - undefined behavior. - This strategy allows implementations to be as efficient as possible - on any given platform. It is, however, recommended that - implementations include debugging support to detect misuse when - NDEBUG is not defined. - - Like all - mutex models - in &Boost.Thread;, mutex leaves the - scheduling policy - as Unspecified. - Programmers should make no assumptions about the order in which - waiting threads acquire a lock. - - - - boost::noncopyable - Exposition only - - - - implementation-defined - - - - Constructs a mutex object. - - - *this is in an unlocked state. - - - - - Destroys a mutex object. - - *this is in an unlocked state. - - Danger: Destruction of a - locked mutex is a serious programming error resulting in undefined - behavior such as a program crash. - - - - - - The try_mutex class is a model of the - TryMutex concept. - - - - The try_mutex class is a model of the - TryMutex concept. - It should be used to synchronize access to shared resources using - Unspecified - locking mechanics. - - For classes that model related mutex concepts, see - mutex and timed_mutex. - - For Recursive - locking mechanics, see recursive_mutex, - recursive_try_mutex, and recursive_timed_mutex. - - - The try_mutex class supplies the following typedefs, - which model - the specified locking strategies: - - - - - - Lock Name - Lock Concept - - - - - scoped_lock - ScopedLock - - - scoped_try_lock - ScopedTryLock - - - - - - - The try_mutex class uses an - Unspecified - locking strategy, so attempts to recursively lock a try_mutex - object or attempts to unlock one by threads that don't own a lock on it result in - undefined behavior. - This strategy allows implementations to be as efficient as possible - on any given platform. It is, however, recommended that - implementations include debugging support to detect misuse when - NDEBUG is not defined. - - Like all - mutex models - in &Boost.Thread;, try_mutex leaves the - scheduling policy - as Unspecified. - Programmers should make no assumptions about the order in which - waiting threads acquire a lock. - - - - boost::noncopyable - Exposition only - - - - implementation-defined - - - - implementation-defined - - - - Constructs a try_mutex object. - - - *this is in an unlocked state. - - - - - Destroys a try_mutex object. - - - *this is in an unlocked state. - - Danger: Destruction of a - locked mutex is a serious programming error resulting in undefined - behavior such as a program crash. - - - - - - The timed_mutex class is a model of the - TimedMutex concept. - - - - The timed_mutex class is a model of the - TimedMutex concept. - It should be used to synchronize access to shared resources using - Unspecified - locking mechanics. - - For classes that model related mutex concepts, see - mutex and try_mutex. - - For Recursive - locking mechanics, see recursive_mutex, - recursive_try_mutex, and recursive_timed_mutex. - - - The timed_mutex class supplies the following typedefs, - which model - the specified locking strategies: - - - - - - Lock Name - Lock Concept - - - - - scoped_lock - ScopedLock - - - scoped_try_lock - ScopedTryLock - - - scoped_timed_lock - ScopedTimedLock - - - - - - - The timed_mutex class uses an - Unspecified - locking strategy, so attempts to recursively lock a timed_mutex - object or attempts to unlock one by threads that don't own a lock on it result in - undefined behavior. - This strategy allows implementations to be as efficient as possible - on any given platform. It is, however, recommended that - implementations include debugging support to detect misuse when - NDEBUG is not defined. - - Like all - mutex models - in &Boost.Thread;, timed_mutex leaves the - scheduling policy - as Unspecified. - Programmers should make no assumptions about the order in which - waiting threads acquire a lock. - - - - boost::noncopyable - Exposition only - - - - implementation-defined - - - - implementation-defined - - - - implementation-defined - - - - Constructs a timed_mutex object. - - - *this is in an unlocked state. - - - - - Destroys a timed_mutex object. - - *this is in an unlocked state. - - Danger: Destruction of a - locked mutex is a serious programming error resulting in undefined - behavior such as a program crash. - - - -
diff --git a/doc/mutex_concepts.qbk b/doc/mutex_concepts.qbk new file mode 100644 index 00000000..f99acd18 --- /dev/null +++ b/doc/mutex_concepts.qbk @@ -0,0 +1,887 @@ +[section:mutex_concepts Mutex Concepts] + +A mutex object facilitates protection against data races and allows thread-safe synchronization of data between threads. A thread +obtains ownership of a mutex object by calling one of the lock functions and relinquishes ownership by calling the corresponding +unlock function. Mutexes may be either recursive or non-recursive, and may grant simultaneous ownership to one or many +threads. __boost_thread__ supplies recursive and non-recursive mutexes with exclusive ownership semantics, along with a shared +ownership (multiple-reader / single-writer) mutex. + +__boost_thread__ supports four basic concepts for lockable objects: __lockable_concept_type__, __timed_lockable_concept_type__, +__shared_lockable_concept_type__ and __upgrade_lockable_concept_type__. Each mutex type implements one or more of these concepts, as +do the various lock types. + +[section:lockable `Lockable` Concept] + +The __lockable_concept__ models exclusive ownership. A type that implements the __lockable_concept__ shall provide the following +member functions: + +* [lock_ref_link `void lock();`] +* [try_lock_ref_link `bool try_lock();`] +* [unlock_ref_link `void unlock();`] + +Lock ownership acquired through a call to __lock_ref__ or __try_lock_ref__ must be released through a call to __unlock_ref__. + +[section:lock `void lock()`] + +[variablelist + +[[Effects:] [The current thread blocks until ownership can be obtained for the current thread.]] + +[[Postcondition:] [The current thread owns `*this`.]] + +[[Throws:] [__thread_resource_error__ if an error occurs.]] + +] +[endsect] + +[section:try_lock `bool try_lock()`] + +[variablelist + +[[Effects:] [Attempt to obtain ownership for the current thread without blocking.]] + +[[Returns:] [`true` if ownership was obtained for the current thread, `false` otherwise.]] + +[[Postcondition:] [If the call returns `true`, the current thread owns the `*this`.]] + +[[Throws:] [__thread_resource_error__ if an error occurs.]] + +] +[endsect] + +[section:unlock `void unlock()`] + +[variablelist + +[[Precondition:] [The current thread owns `*this`.]] + +[[Effects:] [Releases ownership by the current thread.]] + +[[Postcondition:] [The current thread no longer owns `*this`.]] + +[[Throws:] [Nothing]] +] +[endsect] +[endsect] + +[section:timed_lockable `TimedLockable` Concept] + +The __timed_lockable_concept__ refines the __lockable_concept__ to add support for +timeouts when trying to acquire the lock. + +A type that implements the __timed_lockable_concept__ shall meet the requirements +of the __lockable_concept__. In addition, the following member functions must be +provided: + +* [timed_lock_ref_link `bool timed_lock(boost::system_time const& abs_time);`] +* [timed_lock_duration_ref_link `template bool timed_lock(DurationType const& rel_time);`] + +Lock ownership acquired through a call to __timed_lock_ref__ must be released through a call to __unlock_ref__. + +[section:timed_lock `bool timed_lock(boost::system_time const& abs_time)`] + +[variablelist + +[[Effects:] [Attempt to obtain ownership for the current thread. Blocks until ownership can be obtained, or the specified time is +reached. If the specified time has already passed, behaves as __try_lock_ref__.]] + +[[Returns:] [`true` if ownership was obtained for the current thread, `false` otherwise.]] + +[[Postcondition:] [If the call returns `true`, the current thread owns `*this`.]] + +[[Throws:] [__thread_resource_error__ if an error occurs.]] +] +[endsect] + +[section:timed_lock_duration `template bool +timed_lock(DurationType const& rel_time)`] + +[variablelist + +[[Effects:] [As-if [timed_lock_ref_link +`timed_lock(boost::get_system_time()+rel_time)`].]] + +] +[endsect] + +[endsect] + +[section:shared_lockable `SharedLockable` Concept] + +The __shared_lockable_concept__ is a refinement of the __timed_lockable_concept__ that +allows for ['shared ownership] as well as ['exclusive ownership]. This is the +standard multiple-reader / single-write model: at most one thread can have +exclusive ownership, and if any thread does have exclusive ownership, no other threads +can have shared or exclusive ownership. Alternatively, many threads may have +shared ownership. + +For a type to implement the __shared_lockable_concept__, as well as meeting the +requirements of the __timed_lockable_concept__, it must also provide the following +member functions: + +* [lock_shared_ref_link `void lock_shared();`] +* [try_lock_shared_ref_link `bool try_lock_shared();`] +* [unlock_shared_ref_link `bool unlock_shared();`] +* [timed_lock_shared_ref_link `bool timed_lock_shared(boost::system_time const& abs_time);`] + +Lock ownership acquired through a call to __lock_shared_ref__, __try_lock_shared_ref__ or __timed_lock_shared_ref__ must be released +through a call to __unlock_shared_ref__. + +[section:lock_shared `void lock_shared()`] + +[variablelist + +[[Effects:] [The current thread blocks until shared ownership can be obtained for the current thread.]] + +[[Postcondition:] [The current thread has shared ownership of `*this`.]] + +[[Throws:] [__thread_resource_error__ if an error occurs.]] + +] +[endsect] + +[section:try_lock_shared `bool try_lock_shared()`] + +[variablelist + +[[Effects:] [Attempt to obtain shared ownership for the current thread without blocking.]] + +[[Returns:] [`true` if shared ownership was obtained for the current thread, `false` otherwise.]] + +[[Postcondition:] [If the call returns `true`, the current thread has shared ownership of `*this`.]] + +[[Throws:] [__thread_resource_error__ if an error occurs.]] + +] +[endsect] + +[section:timed_lock_shared `bool timed_lock_shared(boost::system_time const& abs_time)`] + +[variablelist + +[[Effects:] [Attempt to obtain shared ownership for the current thread. Blocks until shared ownership can be obtained, or the +specified time is reached. If the specified time has already passed, behaves as __try_lock_shared_ref__.]] + +[[Returns:] [`true` if shared ownership was acquired for the current thread, `false` otherwise.]] + +[[Postcondition:] [If the call returns `true`, the current thread has shared +ownership of `*this`.]] + +[[Throws:] [__thread_resource_error__ if an error occurs.]] + +] +[endsect] + +[section:unlock_shared `void unlock_shared()`] + +[variablelist + +[[Precondition:] [The current thread has shared ownership of `*this`.]] + +[[Effects:] [Releases shared ownership of `*this` by the current thread.]] + +[[Postcondition:] [The current thread no longer has shared ownership of `*this`.]] + +[[Throws:] [Nothing]] + +] +[endsect] + + +[endsect] + +[section:upgrade_lockable `UpgradeLockable` Concept] + +The __upgrade_lockable_concept__ is a refinement of the __shared_lockable_concept__ that allows for ['upgradable ownership] as well +as ['shared ownership] and ['exclusive ownership]. This is an extension to the multiple-reader / single-write model provided by the +__shared_lockable_concept__: a single thread may have ['upgradable ownership] at the same time as others have ['shared +ownership]. The thread with ['upgradable ownership] may at any time attempt to upgrade that ownership to ['exclusive ownership]. If +no other threads have shared ownership, the upgrade is completed immediately, and the thread now has ['exclusive ownership], which +must be relinquished by a call to __unlock_ref__, just as if it had been acquired by a call to __lock_ref__. + +If a thread with ['upgradable ownership] tries to upgrade whilst other threads have ['shared ownership], the attempt will fail and +the thread will block until ['exclusive ownership] can be acquired. + +Ownership can also be ['downgraded] as well as ['upgraded]: exclusive ownership of an implementation of the +__upgrade_lockable_concept__ can be downgraded to upgradable ownership or shared ownership, and upgradable ownership can be +downgraded to plain shared ownership. + +For a type to implement the __upgrade_lockable_concept__, as well as meeting the +requirements of the __shared_lockable_concept__, it must also provide the following +member functions: + +* [lock_upgrade_ref_link `void lock_upgrade();`] +* [unlock_upgrade_ref_link `bool unlock_upgrade();`] +* [unlock_upgrade_and_lock_ref_link `void unlock_upgrade_and_lock();`] +* [unlock_and_lock_upgrade_ref_link `void unlock_and_lock_upgrade();`] +* [unlock_upgrade_and_lock_shared_ref_link `void unlock_upgrade_and_lock_shared();`] + +Lock ownership acquired through a call to __lock_upgrade_ref__ must be released through a call to __unlock_upgrade_ref__. If the +ownership type is changed through a call to one of the `unlock_xxx_and_lock_yyy()` functions, ownership must be released through a +call to the unlock function corresponding to the new level of ownership. + + +[section:lock_upgrade `void lock_upgrade()`] + +[variablelist + +[[Effects:] [The current thread blocks until upgrade ownership can be obtained for the current thread.]] + +[[Postcondition:] [The current thread has upgrade ownership of `*this`.]] + +[[Throws:] [__thread_resource_error__ if an error occurs.]] + +] +[endsect] + +[section:unlock_upgrade `void unlock_upgrade()`] + +[variablelist + +[[Precondition:] [The current thread has upgrade ownership of `*this`.]] + +[[Effects:] [Releases upgrade ownership of `*this` by the current thread.]] + +[[Postcondition:] [The current thread no longer has upgrade ownership of `*this`.]] + +[[Throws:] [Nothing]] + +] +[endsect] + +[section:unlock_upgrade_and_lock `void unlock_upgrade_and_lock()`] + +[variablelist + +[[Precondition:] [The current thread has upgrade ownership of `*this`.]] + +[[Effects:] [Atomically releases upgrade ownership of `*this` by the current thread and acquires exclusive ownership of `*this`. If +any other threads have shared ownership, blocks until exclusive ownership can be acquired.]] + +[[Postcondition:] [The current thread has exclusive ownership of `*this`.]] + +[[Throws:] [Nothing]] + +] +[endsect] + +[section:unlock_upgrade_and_lock_shared `void unlock_upgrade_and_lock_shared()`] + +[variablelist + +[[Precondition:] [The current thread has upgrade ownership of `*this`.]] + +[[Effects:] [Atomically releases upgrade ownership of `*this` by the current thread and acquires shared ownership of `*this` without +blocking.]] + +[[Postcondition:] [The current thread has shared ownership of `*this`.]] + +[[Throws:] [Nothing]] + +] +[endsect] + +[section:unlock_and_lock_upgrade `void unlock_and_lock_upgrade()`] + +[variablelist + +[[Precondition:] [The current thread has exclusive ownership of `*this`.]] + +[[Effects:] [Atomically releases exclusive ownership of `*this` by the current thread and acquires upgrade ownership of `*this` +without blocking.]] + +[[Postcondition:] [The current thread has upgrade ownership of `*this`.]] + +[[Throws:] [Nothing]] + +] +[endsect] + +[endsect] + +[endsect] + +[section:locks Lock Types] + +[section:lock_guard Class template `lock_guard`] + + template + class lock_guard + { + public: + explicit lock_guard(Lockable& m_); + lock_guard(Lockable& m_,boost::adopt_lock_t); + + ~lock_guard(); + }; + +__lock_guard__ is very simple: on construction it +acquires ownership of the implementation of the __lockable_concept__ supplied as +the constructor parameter. On destruction, the ownership is released. This +provides simple RAII-style locking of a __lockable_concept_type__ object, to facilitate exception-safe +locking and unlocking. In addition, the [link +thread.synchronization.locks.lock_guard.constructor_adopt `lock_guard(Lockable & +m,boost::adopt_lock_t)` constructor] allows the __lock_guard__ object to +take ownership of a lock already held by the current thread. + +[section:constructor `lock_guard(Lockable & m)`] + +[variablelist + +[[Effects:] [Stores a reference to `m`. Invokes [lock_ref_link `m.lock()`].]] + +[[Throws:] [Any exception thrown by the call to [lock_ref_link `m.lock()`].]] + +] + +[endsect] + +[section:constructor_adopt `lock_guard(Lockable & m,boost::adopt_lock_t)`] + +[variablelist + +[[Precondition:] [The current thread owns a lock on `m` equivalent to one +obtained by a call to [lock_ref_link `m.lock()`].]] + +[[Effects:] [Stores a reference to `m`. Takes ownership of the lock state of +`m`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:destructor `~lock_guard()`] + +[variablelist + +[[Effects:] [Invokes [unlock_ref_link `m.unlock()`] on the __lockable_concept_type__ +object passed to the constructor.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[endsect] + +[section:unique_lock Class template `unique_lock`] + + template + class unique_lock + { + public: + explicit unique_lock(Lockable& m_); + unique_lock(Lockable& m_,adopt_lock_t); + unique_lock(Lockable& m_,defer_lock_t); + unique_lock(Lockable& m_,try_to_lock_t); + unique_lock(Lockable& m_,system_time const& target_time); + + ~unique_lock(); + + unique_lock(detail::thread_move_t > other); + unique_lock(detail::thread_move_t > other); + + operator detail::thread_move_t >(); + detail::thread_move_t > move(); + unique_lock& operator=(detail::thread_move_t > other); + unique_lock& operator=(detail::thread_move_t > other); + + void swap(unique_lock& other); + void swap(detail::thread_move_t > other); + + void lock(); + bool try_lock(); + + template + bool timed_lock(TimeDuration const& relative_time); + bool timed_lock(::boost::system_time const& absolute_time); + + void unlock(); + + bool owns_lock() const; + operator ``['unspecified-bool-type]``() const; + bool operator!() const; + + Lockable* mutex() const; + Lockable* release(); + }; + +__unique_lock__ is more complex than __lock_guard__: not only does it provide for RAII-style locking, it also allows for deferring +acquiring the lock until the __lock_ref__ member function is called explicitly, or trying to acquire the lock in a non-blocking +fashion, or with a timeout. Consequently, __unlock_ref__ is only called in the destructor if the lock object has locked the +__lockable_concept_type__ object, or otherwise adopted a lock on the __lockable_concept_type__ object. + +Specializations of __unique_lock__ model the __timed_lockable_concept__ if the supplied __lockable_concept_type__ type itself models +__timed_lockable_concept__ (e.g. `boost::unique_lock`), or the __lockable_concept__ otherwise +(e.g. `boost::unique_lock`). + +An instance of __unique_lock__ is said to ['own] the lock state of a __lockable_concept_type__ `m` if __mutex_func_ref__ returns a +pointer to `m` and __owns_lock_ref__ returns `true`. If an object that ['owns] the lock state of a __lockable_concept_type__ object +is destroyed, then the destructor will invoke [unlock_ref_link `mutex()->unlock()`]. + +The member functions of __unique_lock__ are not thread-safe. In particular, __unique_lock__ is intended to model the ownership of a +__lockable_concept_type__ object by a particular thread, and the member functions that release ownership of the lock state +(including the destructor) must be called by the same thread that acquired ownership of the lock state. + +[section:constructor `unique_lock(Lockable & m)`] + +[variablelist + +[[Effects:] [Stores a reference to `m`. Invokes [lock_ref_link `m.lock()`].]] + +[[Postcondition:] [__owns_lock_ref__ returns `true`. __mutex_func_ref__ returns `&m`.]] + +[[Throws:] [Any exception thrown by the call to [lock_ref_link `m.lock()`].]] + +] + +[endsect] + +[section:constructor_adopt `unique_lock(Lockable & m,boost::adopt_lock_t)`] + +[variablelist + +[[Precondition:] [The current thread owns an exclusive lock on `m`.]] + +[[Effects:] [Stores a reference to `m`. Takes ownership of the lock state of `m`.]] + +[[Postcondition:] [__owns_lock_ref__ returns `true`. __mutex_func_ref__ returns `&m`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:constructor_defer `unique_lock(Lockable & m,boost::defer_lock_t)`] + +[variablelist + +[[Effects:] [Stores a reference to `m`.]] + +[[Postcondition:] [__owns_lock_ref__ returns `false`. __mutex_func_ref__ returns `&m`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:constructor_try `unique_lock(Lockable & m,boost::try_to_lock_t)`] + +[variablelist + +[[Effects:] [Stores a reference to `m`. Invokes [try_lock_ref_link +`m.try_lock()`], and takes ownership of the lock state if the call returns +`true`.]] + +[[Postcondition:] [__mutex_func_ref__ returns `&m`. If the call to __try_lock_ref__ +returned `true`, then __owns_lock_ref__ returns `true`, otherwise __owns_lock_ref__ +returns `false`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:constructor_abs_time `unique_lock(Lockable & m,boost::system_time const& abs_time)`] + +[variablelist + +[[Effects:] [Stores a reference to `m`. Invokes [timed_lock_ref_link +`m.timed_lock(abs_time)`], and takes ownership of the lock state if the call +returns `true`.]] + +[[Postcondition:] [__mutex_func_ref__ returns `&m`. If the call to __timed_lock_ref__ +returned `true`, then __owns_lock_ref__ returns `true`, otherwise __owns_lock_ref__ +returns `false`.]] + +[[Throws:] [Any exceptions thrown by the call to [timed_lock_ref_link `m.timed_lock(abs_time)`].]] + +] + +[endsect] + +[section:destructor `~unique_lock()`] + +[variablelist + +[[Effects:] [Invokes __mutex_func_ref__`->`[unlock_ref_link `unlock()`] if +__owns_lock_ref__ returns `true`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:owns_lock `bool owns_lock() const`] + +[variablelist + +[[Returns:] [`true` if the `*this` owns the lock on the __lockable_concept_type__ +object associated with `*this`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:mutex `Lockable* mutex() const`] + +[variablelist + +[[Returns:] [A pointer to the __lockable_concept_type__ object associated with +`*this`, or `NULL` if there is no such object.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:bool_conversion `operator unspecified-bool-type() const`] + +[variablelist + +[[Returns:] [If __owns_lock_ref__ would return `true`, a value that evaluates to +`true` in boolean contexts, otherwise a value that evaluates to `false` in +boolean contexts.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:operator_not `bool operator!() const`] + +[variablelist + +[[Returns:] [`!` __owns_lock_ref__.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:release `Lockable* release()`] + +[variablelist + +[[Effects:] [The association between `*this` and the __lockable_concept_type__ object is removed, without affecting the lock state +of the __lockable_concept_type__ object. If __owns_lock_ref__ would have returned `true`, it is the responsibility of the calling +code to ensure that the __lockable_concept_type__ is correctly unlocked.]] + +[[Returns:] [A pointer to the __lockable_concept_type__ object associated with `*this` at the point of the call, or `NULL` if there +is no such object.]] + +[[Throws:] [Nothing.]] + +[[Postcondition:] [`*this` is no longer associated with any __lockable_concept_type__ object. __mutex_func_ref__ returns `NULL` and +__owns_lock_ref__ returns `false`.]] + +] + +[endsect] + +[endsect] + +[section:shared_lock Class template `shared_lock`] + + template + class shared_lock + { + public: + explicit shared_lock(Lockable& m_); + shared_lock(Lockable& m_,adopt_lock_t); + shared_lock(Lockable& m_,defer_lock_t); + shared_lock(Lockable& m_,try_to_lock_t); + shared_lock(Lockable& m_,system_time const& target_time); + shared_lock(detail::thread_move_t > other); + shared_lock(detail::thread_move_t > other); + shared_lock(detail::thread_move_t > other); + + ~shared_lock(); + + operator detail::thread_move_t >(); + detail::thread_move_t > move(); + + shared_lock& operator=(detail::thread_move_t > other); + shared_lock& operator=(detail::thread_move_t > other); + shared_lock& operator=(detail::thread_move_t > other); + void swap(shared_lock& other); + + void lock(); + bool try_lock(); + bool timed_lock(boost::system_time const& target_time); + void unlock(); + + operator ``['unspecified-bool-type]``() const; + bool operator!() const; + bool owns_lock() const; + }; + +Like __unique_lock__, __shared_lock__ models the __lockable_concept__, but rather than acquiring unique ownership of the supplied +__lockable_concept_type__ object, locking an instance of __shared_lock__ acquires shared ownership. + +Like __unique_lock__, not only does it provide for RAII-style locking, it also allows for deferring acquiring the lock until the +__lock_ref__ member function is called explicitly, or trying to acquire the lock in a non-blocking fashion, or with a +timeout. Consequently, __unlock_ref__ is only called in the destructor if the lock object has locked the __lockable_concept_type__ +object, or otherwise adopted a lock on the __lockable_concept_type__ object. + +An instance of __shared_lock__ is said to ['own] the lock state of a __lockable_concept_type__ `m` if __mutex_func_ref__ returns a +pointer to `m` and __owns_lock_ref__ returns `true`. If an object that ['owns] the lock state of a __lockable_concept_type__ object +is destroyed, then the destructor will invoke [unlock_shared_ref_link `mutex()->unlock_shared()`]. + +The member functions of __shared_lock__ are not thread-safe. In particular, __shared_lock__ is intended to model the shared +ownership of a __lockable_concept_type__ object by a particular thread, and the member functions that release ownership of the lock +state (including the destructor) must be called by the same thread that acquired ownership of the lock state. + +[section:constructor `shared_lock(Lockable & m)`] + +[variablelist + +[[Effects:] [Stores a reference to `m`. Invokes [lock_shared_ref_link `m.lock_shared()`].]] + +[[Postcondition:] [__owns_lock_shared_ref__ returns `true`. __mutex_func_ref__ returns `&m`.]] + +[[Throws:] [Any exception thrown by the call to [lock_shared_ref_link `m.lock_shared()`].]] + +] + +[endsect] + +[section:constructor_adopt `shared_lock(Lockable & m,boost::adopt_lock_t)`] + +[variablelist + +[[Precondition:] [The current thread owns an exclusive lock on `m`.]] + +[[Effects:] [Stores a reference to `m`. Takes ownership of the lock state of `m`.]] + +[[Postcondition:] [__owns_lock_shared_ref__ returns `true`. __mutex_func_ref__ returns `&m`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:constructor_defer `shared_lock(Lockable & m,boost::defer_lock_t)`] + +[variablelist + +[[Effects:] [Stores a reference to `m`.]] + +[[Postcondition:] [__owns_lock_shared_ref__ returns `false`. __mutex_func_ref__ returns `&m`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:constructor_try `shared_lock(Lockable & m,boost::try_to_lock_t)`] + +[variablelist + +[[Effects:] [Stores a reference to `m`. Invokes [try_lock_shared_ref_link +`m.try_lock_shared()`], and takes ownership of the lock state if the call returns +`true`.]] + +[[Postcondition:] [__mutex_func_ref__ returns `&m`. If the call to __try_lock_shared_ref__ +returned `true`, then __owns_lock_shared_ref__ returns `true`, otherwise __owns_lock_shared_ref__ +returns `false`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:constructor_abs_time `shared_lock(Lockable & m,boost::system_time const& abs_time)`] + +[variablelist + +[[Effects:] [Stores a reference to `m`. Invokes [timed_lock_shared_ref_link +`m.timed_lock(abs_time)`], and takes ownership of the lock state if the call +returns `true`.]] + +[[Postcondition:] [__mutex_func_ref__ returns `&m`. If the call to __timed_lock_shared_ref__ +returned `true`, then __owns_lock_shared_ref__ returns `true`, otherwise __owns_lock_shared_ref__ +returns `false`.]] + +[[Throws:] [Any exceptions thrown by the call to [timed_lock_shared_ref_link `m.timed_lock(abs_time)`].]] + +] + +[endsect] + +[section:destructor `~shared_lock()`] + +[variablelist + +[[Effects:] [Invokes __mutex_func_ref__`->`[unlock_shared_ref_link `unlock_shared()`] if +__owns_lock_shared_ref__ returns `true`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:owns_lock `bool owns_lock() const`] + +[variablelist + +[[Returns:] [`true` if the `*this` owns the lock on the __lockable_concept_type__ +object associated with `*this`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:mutex `Lockable* mutex() const`] + +[variablelist + +[[Returns:] [A pointer to the __lockable_concept_type__ object associated with +`*this`, or `NULL` if there is no such object.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:bool_conversion `operator unspecified-bool-type() const`] + +[variablelist + +[[Returns:] [If __owns_lock_shared_ref__ would return `true`, a value that evaluates to +`true` in boolean contexts, otherwise a value that evaluates to `false` in +boolean contexts.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:operator_not `bool operator!() const`] + +[variablelist + +[[Returns:] [`!` __owns_lock_shared_ref__.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:release `Lockable* release()`] + +[variablelist + +[[Effects:] [The association between `*this` and the __lockable_concept_type__ object is removed, without affecting the lock state +of the __lockable_concept_type__ object. If __owns_lock_shared_ref__ would have returned `true`, it is the responsibility of the calling +code to ensure that the __lockable_concept_type__ is correctly unlocked.]] + +[[Returns:] [A pointer to the __lockable_concept_type__ object associated with `*this` at the point of the call, or `NULL` if there +is no such object.]] + +[[Throws:] [Nothing.]] + +[[Postcondition:] [`*this` is no longer associated with any __lockable_concept_type__ object. __mutex_func_ref__ returns `NULL` and +__owns_lock_shared_ref__ returns `false`.]] + +] + +[endsect] + +[endsect] + +[section:upgrade_lock Class template `upgrade_lock`] + + template + class upgrade_lock + { + public: + explicit upgrade_lock(Lockable& m_); + + upgrade_lock(detail::thread_move_t > other); + upgrade_lock(detail::thread_move_t > other); + + ~upgrade_lock(); + + operator detail::thread_move_t >(); + detail::thread_move_t > move(); + + upgrade_lock& operator=(detail::thread_move_t > other); + upgrade_lock& operator=(detail::thread_move_t > other); + + void swap(upgrade_lock& other); + + void lock(); + void unlock(); + + operator ``['unspecified-bool-type]``() const; + bool operator!() const; + bool owns_lock() const; + }; + +Like __unique_lock__, __upgrade_lock__ models the __lockable_concept__, but rather than acquiring unique ownership of the supplied +__lockable_concept_type__ object, locking an instance of __upgrade_lock__ acquires upgrade ownership. + +Like __unique_lock__, not only does it provide for RAII-style locking, it also allows for deferring acquiring the lock until the +__lock_ref__ member function is called explicitly, or trying to acquire the lock in a non-blocking fashion, or with a +timeout. Consequently, __unlock_ref__ is only called in the destructor if the lock object has locked the __lockable_concept_type__ +object, or otherwise adopted a lock on the __lockable_concept_type__ object. + +An instance of __upgrade_lock__ is said to ['own] the lock state of a __lockable_concept_type__ `m` if __mutex_func_ref__ returns a +pointer to `m` and __owns_lock_ref__ returns `true`. If an object that ['owns] the lock state of a __lockable_concept_type__ object +is destroyed, then the destructor will invoke [unlock_upgrade_ref_link `mutex()->unlock_upgrade()`]. + +The member functions of __upgrade_lock__ are not thread-safe. In particular, __upgrade_lock__ is intended to model the upgrade +ownership of a __lockable_concept_type__ object by a particular thread, and the member functions that release ownership of the lock +state (including the destructor) must be called by the same thread that acquired ownership of the lock state. + +[endsect] + +[section:upgrade_to_unique_lock Class template `upgrade_to_unique_lock`] + + template + class upgrade_to_unique_lock + { + public: + explicit upgrade_to_unique_lock(upgrade_lock& m_); + + ~upgrade_to_unique_lock(); + + upgrade_to_unique_lock(detail::thread_move_t > other); + upgrade_to_unique_lock& operator=(detail::thread_move_t > other); + void swap(upgrade_to_unique_lock& other); + + operator ``['unspecified-bool-type]``() const; + bool operator!() const; + bool owns_lock() const; + }; + +__upgrade_to_unique_lock__ allows for a temporary upgrade of an __upgrade_lock__ to exclusive ownership. When constructed with a +reference to an instance of __upgrade_lock__, if that instance has upgrade ownership on some __lockable_concept_type__ object, that +ownership is upgraded to exclusive ownership. When the __upgrade_to_unique_lock__ instance is destroyed, the ownership of the +__lockable_concept_type__ is downgraded back to ['upgrade ownership]. + +[endsect] + +[endsect] diff --git a/doc/mutexes.qbk b/doc/mutexes.qbk new file mode 100644 index 00000000..020f767f --- /dev/null +++ b/doc/mutexes.qbk @@ -0,0 +1,128 @@ +[section:mutex_types Mutex Types] + +[section:mutex Class `mutex`] + + class mutex: + boost::noncopyable + { + public: + mutex(); + ~mutex(); + + void lock(); + bool try_lock(); + void unlock(); + + typedef unique_lock scoped_lock; + typedef scoped_lock scoped_try_lock; + }; + +__mutex__ implements the __lockable_concept__ to provide an exclusive-ownership mutex. At most one thread can own the lock on a given +instance of __mutex__ at any time. Multiple concurrent calls to __lock_ref__, __try_lock_ref__ and __unlock_ref__ shall be permitted. + +[endsect] + +[section:try_mutex Typedef `try_mutex`] + + typedef mutex try_mutex; + +__try_mutex__ is a `typedef` to __mutex__, provided for backwards compatibility with previous releases of boost. + +[endsect] + +[section:timed_mutex Class `timed_mutex`] + + class timed_mutex: + boost::noncopyable + { + public: + timed_mutex(); + ~timed_mutex(); + + void lock(); + void unlock(); + bool try_lock(); + bool timed_lock(system_time const & abs_time); + + template + bool timed_lock(TimeDuration const & relative_time); + + typedef unique_lock scoped_timed_lock; + typedef scoped_timed_lock scoped_try_lock; + typedef scoped_timed_lock scoped_lock; + }; + +__timed_mutex__ implements the __timed_lockable_concept__ to provide an exclusive-ownership mutex. At most one thread can own the +lock on a given instance of __timed_mutex__ at any time. Multiple concurrent calls to __lock_ref__, __try_lock_ref__, +__timed_lock_ref__, __timed_lock_duration_ref__ and __unlock_ref__ shall be permitted. + +[endsect] + +[section:recursive_mutex Class `recursive_mutex`] + + class recursive_mutex: + boost::noncopyable + { + public: + recursive_mutex(); + ~recursive_mutex(); + + void lock(); + bool try_lock(); + void unlock(); + + typedef unique_lock scoped_lock; + typedef scoped_lock scoped_try_lock; + }; + +__recursive_mutex__ implements the __lockable_concept__ to provide an exclusive-ownership recursive mutex. At most one thread can +own the lock on a given instance of __recursive_mutex__ at any time. Multiple concurrent calls to __lock_ref__, __try_lock_ref__ and +__unlock_ref__ shall be permitted. A thread that already has exclusive ownership of a given __recursive_mutex__ instance can call +__lock_ref__ or __try_lock_ref__ to acquire an additional level of ownership of the mutex. __unlock_ref__ must be called once for +each level of ownership acquired by a single thread before ownership can be acquired by another thread. + +[endsect] + +[section:recursive_try_mutex Typedef `recursive_try_mutex`] + + typedef recursive_mutex recursive_try_mutex; + +__recursive_try_mutex__ is a `typedef` to __recursive_mutex__, provided for backwards compatibility with previous releases of boost. + +[endsect] + +[section:recursive_timed_mutex Class `recursive_timed_mutex`] + + class recursive_timed_mutex: + boost::noncopyable + { + public: + recursive_timed_mutex(); + ~recursive_timed_mutex(); + + void lock(); + bool try_lock(); + void unlock(); + + bool timed_lock(system_time const & abs_time); + + template + bool timed_lock(TimeDuration const & relative_time); + + typedef unique_lock scoped_lock; + typedef scoped_lock scoped_try_lock; + typedef scoped_lock scoped_timed_lock; + }; + +__recursive_timed_mutex__ implements the __timed_lockable_concept__ to provide an exclusive-ownership recursive mutex. At most one +thread can own the lock on a given instance of __recursive_timed_mutex__ at any time. Multiple concurrent calls to __lock_ref__, +__try_lock_ref__, __timed_lock_ref__, __timed_lock_duration_ref__ and __unlock_ref__ shall be permitted. A thread that already has +exclusive ownership of a given __recursive_timed_mutex__ instance can call __lock_ref__, __timed_lock_ref__, +__timed_lock_duration_ref__ or __try_lock_ref__ to acquire an additional level of ownership of the mutex. __unlock_ref__ must be +called once for each level of ownership acquired by a single thread before ownership can be acquired by another thread. + +[endsect] + +[include shared_mutex_ref.qbk] + +[endsect] diff --git a/doc/once-ref.xml b/doc/once-ref.xml deleted file mode 100644 index d6520178..00000000 --- a/doc/once-ref.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - %thread.entities; -]> - -
- - The call_once function and - once_flag type (statically initialized to - BOOST_ONCE_INIT) can be used to run a - routine exactly once. This can be used to initialize data in a - thread-safe - manner. - - The implementation-defined macro - BOOST_ONCE_INIT is a constant value used to - initialize once_flag instances to indicate that the - logically associated routine has not been run yet. See - call_once for more details. - - - - - The call_once function and - once_flag type (statically initialized to - BOOST_ONCE_INIT) can be used to run a - routine exactly once. This can be used to initialize data in a - thread-safe - manner. - - The implementation-defined type once_flag - is used as a flag to insure a routine is called only once. - Instances of this type should be statically initialized to - BOOST_ONCE_INIT. See - call_once for more details. - - - implementation-defined - - - - The call_once function and - once_flag type (statically initialized to - BOOST_ONCE_INIT) can be used to run a - routine exactly once. This can be used to initialize data in a - thread-safe - manner. - - - Example usage is as follows: - -//Example usage: -boost::once_flag once = BOOST_ONCE_INIT; - -void init() -{ - //... -} - -void thread_proc() -{ - boost::call_once(once, &init); -} - - - - once_flag& - - - - Function func - - - As if (in an atomic fashion): - if (flag == BOOST_ONCE_INIT) func();. If func() throws an exception, it shall be as if this -thread never invoked call_once - - flag != BOOST_ONCE_INIT unless func() throws an exception. - - - -
diff --git a/doc/once.qbk b/doc/once.qbk new file mode 100644 index 00000000..86b0ef2b --- /dev/null +++ b/doc/once.qbk @@ -0,0 +1,45 @@ +[section:once One-time Initialization] + +`boost::call_once` provides a mechanism for ensuring that an initialization routine is run exactly once without data races or deadlocks. + +[section:once_flag Typedef `once_flag`] + + typedef platform-specific-type once_flag; + #define BOOST_ONCE_INIT platform-specific-initializer + +Objects of type `boost::once_flag` shall be initialized with `BOOST_ONCE_INIT`: + + boost::once_flag f=BOOST_ONCE_INIT; + +[endsect] + +[section:call_once Non-member function `call_once`] + + template + void call_once(once_flag& flag,Callable func); + +[variablelist + +[[Requires:] [`Callable` is `CopyConstructible`. Copying `func` shall have no side effects, and the effect of calling the copy shall +be equivalent to calling the original. ]] + +[[Effects:] [Calls to `call_once` on the same `once_flag` object are serialized. If there has been no prior effective `call_once` on +the same `once_flag` object, the argument `func` (or a copy thereof) is called as-if by invoking `func(args)`, and the invocation of +`call_once` is effective if and only if `func(args)` returns without exception. If an exception is thrown, the exception is +propagated to the caller. If there has been a prior effective `call_once` on the same `once_flag` object, the `call_once` returns +without invoking `func`. ]] + +[[Synchronization:] [The completion of an effective `call_once` invocation on a `once_flag` object, synchronizes with +all subsequent `call_once` invocations on the same `once_flag` object. ]] + +[[Throws:] [`thread_resource_error` when the effects cannot be achieved. or any exception propagated from `func`.]] + +] + + void call_once(void (*func)(),once_flag& flag); + +This second overload is provided for backwards compatibility. The effects of `call_once(func,flag)` shall be the same as those of +`call_once(flag,func)`. + +[endsect] +[endsect] diff --git a/doc/overview.qbk b/doc/overview.qbk new file mode 100644 index 00000000..63edba0f --- /dev/null +++ b/doc/overview.qbk @@ -0,0 +1,15 @@ +[section:overview Overview] + +__boost_thread__ enables the use of multiple threads of execution with shared data in portable C++ code. It provides classes and +functions for managing the threads themselves, along with others for synchronizing data between the threads or providing separate +copies of data specific to individual threads. + +The __boost_thread__ library was originally written and designed by William E. Kempf. This version is a major rewrite designed to +closely follow the proposals presented to the C++ Standards Committee, in particular +[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2497.html N2497], +[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2320.html N2320], +[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2184.html N2184], +[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2139.html N2139], and +[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2094.html N2094] + +[endsect] diff --git a/doc/overview.xml b/doc/overview.xml deleted file mode 100644 index 1e639597..00000000 --- a/doc/overview.xml +++ /dev/null @@ -1,206 +0,0 @@ - - - %thread.entities; -]> - -
- Overview -
- Introduction - &Boost.Thread; allows C++ programs to execute as multiple, - asynchronous, independent threads-of-execution. Each thread has its own - machine state including program instruction counter and registers. Programs - which execute as multiple threads are called multithreaded programs to - distinguish them from traditional single-threaded programs. The glossary gives a more complete description - of the multithreading execution environment. - Multithreading provides several advantages: - - - Programs which would otherwise block waiting for some external - event can continue to respond if the blocking operation is placed in a - separate thread. Multithreading is usually an absolute requirement for - these programs. - - - Well-designed multithreaded programs may execute faster than - single-threaded programs, particularly on multiprocessor hardware. - Note, however, that poorly-designed multithreaded programs are often - slower than single-threaded programs. - - - Some program designs may be easier to formulate using a - multithreaded approach. After all, the real world is - asynchronous! - - -
-
- Dangers -
- General considerations - Beyond the errors which can occur in single-threaded programs, - multithreaded programs are subject to additional errors: - - - Race - conditions - - - Deadlock - (sometimes called "deadly embrace") - - - Priority - failures (priority inversion, infinite overtaking, starvation, - etc.) - - - Every multithreaded program must be designed carefully to avoid these - errors. These aren't rare or exotic failures - they are virtually guaranteed - to occur unless multithreaded code is designed to avoid them. Priority - failures are somewhat less common, but are nonetheless serious. - The &Boost.Thread; design - attempts to minimize these errors, but they will still occur unless the - programmer proactively designs to avoid them. - Please also see - for additional, implementation-specific considerations. -
-
- Testing and debugging considerations - Multithreaded programs are non-deterministic. In other words, the - same program with the same input data may follow different execution - paths each time it is invoked. That can make testing and debugging a - nightmare: - - - Failures are often not repeatable. - - - Probe effect causes debuggers to produce very different results - from non-debug uses. - - - Debuggers require special support to show thread state. - - - Tests on a single processor system may give no indication of - serious errors which would appear on multiprocessor systems, and visa - versa. Thus test cases should include a varying number of - processors. - - - For programs which create a varying number of threads according - to workload, tests which don't span the full range of possibilities - may miss serious errors. - - -
-
- Getting a head start - Although it might appear that multithreaded programs are inherently - unreliable, many reliable multithreaded programs do exist. Multithreading - techniques are known which lead to reliable programs. - Design patterns for reliable multithreaded programs, including the - important monitor pattern, are presented in - Pattern-Oriented Software Architecture Volume 2 - Patterns for - Concurrent and Networked Objects - &cite.SchmidtStalRohnertBuschmann;. Many important multithreading programming - considerations (independent of threading library) are discussed in - Programming with POSIX Threads &cite.Butenhof97;. - Doing some reading before attempting multithreaded designs will - give you a head start toward reliable multithreaded programs. -
-
-
- C++ Standard Library usage in multithreaded programs -
- Runtime libraries - - Warning: Multithreaded programs such as - those using &Boost.Thread; must link to thread-safe versions of - all runtime libraries used by the program, including the runtime library - for the C++ Standard Library. Failure to do so will cause race conditions to occur - when multiple threads simultaneously execute runtime library functions for - new, delete, or other language features which - imply shared state. -
-
- Potentially non-thread-safe functions - Certain C++ Standard Library functions inherited from C are - particular problems because they hold internal state between - calls: - - - rand - - - strtok - - - asctime - - - ctime - - - gmtime - - - localtime - - - It is possible to write thread-safe implementations of these by - using thread specific storage (see - boost::thread_specific_ptr), and several C++ - compiler vendors do just that. The technique is well-know and is explained - in &cite.Butenhof97;. - But at least one vendor (HP-UX) does not provide thread-safe - implementations of the above functions in their otherwise thread-safe - runtime library. Instead they provide replacement functions with - different names and arguments. - Recommendation: For the most - portable, yet thread-safe code, use Boost replacements for the problem - functions. See the Boost Random Number Library - and Boost Tokenizer Library. -
-
-
- Common guarantees for all &Boost.Thread; components -
- Exceptions - &Boost.Thread; destructors never - throw exceptions. Unless otherwise specified, other - &Boost.Thread; functions that do not have - an exception-specification may throw implementation-defined - exceptions. - In particular, &Boost.Thread; - reports failure to allocate storage by throwing an exception of type - std::bad_alloc or a class derived from - std::bad_alloc, failure to obtain thread resources other than - memory by throwing an exception of type - boost::thread_resource_error, and certain lock - related failures by throwing an exception of type - boost::lock_error. - Rationale: Follows the C++ Standard - Library practice of allowing all functions except destructors or other - specified functions to throw exceptions on errors. -
-
- NonCopyable requirement - &Boost.Thread; classes documented as - meeting the NonCopyable requirement disallow copy construction and copy - assignment. For the sake of exposition, the synopsis of such classes show - private derivation from boost::noncopyable. Users - should not depend on this derivation, however, as implementations are free - to meet the NonCopyable requirement in other ways. -
-
-
diff --git a/doc/rationale.xml b/doc/rationale.xml deleted file mode 100644 index aeaf8511..00000000 --- a/doc/rationale.xml +++ /dev/null @@ -1,438 +0,0 @@ - - - %thread.entities; -]> - -
- Rationale - This page explains the rationale behind various design decisions in the - &Boost.Thread; library. Having the rationale documented here should explain - how we arrived at the current design as well as prevent future rehashing of - discussions and thought processes that have already occurred. It can also give - users a lot of insight into the design process required for this - library. -
- Rationale for the Creation of &Boost.Thread; - Processes often have a degree of "potential parallelism" and it can - often be more intuitive to design systems with this in mind. Further, these - parallel processes can result in more responsive programs. The benefits for - multithreaded programming are quite well known to most modern programmers, - yet the C++ language doesn't directly support this concept. - Many platforms support multithreaded programming despite the fact that - the language doesn't support it. They do this through external libraries, - which are, unfortunately, platform specific. POSIX has tried to address this - problem through the standardization of a "pthread" library. However, this is - a standard only on POSIX platforms, so its portability is limited. - Another problem with POSIX and other platform specific thread - libraries is that they are almost universally C based libraries. This leaves - several C++ specific issues unresolved, such as what happens when an - exception is thrown in a thread. Further, there are some C++ concepts, such - as destructors, that can make usage much easier than what's available in a C - library. - What's truly needed is C++ language support for threads. However, the - C++ standards committee needs existing practice or a good proposal as a - starting point for adding this to the standard. - The &Boost.Thread; library was developed to provide a C++ developer - with a portable interface for writing multithreaded programs on numerous - platforms. There's a hope that the library can be the basis for a more - detailed proposal for the C++ standards committee to consider for inclusion - in the next C++ standard. -
-
- Rationale for the Low Level Primitives Supported in &Boost.Thread; - The &Boost.Thread; library supplies a set of low level primitives for - writing multithreaded programs, such as mutexes and condition variables. In - fact, the first release of &Boost.Thread; supports only these low level - primitives. However, computer science research has shown that use of these - primitives is difficult since it's difficult to mathematically prove that a - usage pattern is correct, meaning it doesn't result in race conditions or - deadlocks. There are several algebras (such as CSP, CCS and Join calculus) - that have been developed to help write provably correct parallel - processes. In order to prove the correctness these processes must be coded - using higher level abstractions. So why does &Boost.Thread; support the - lower level concepts? - The reason is simple: the higher level concepts need to be implemented - using at least some of the lower level concepts. So having portable lower - level concepts makes it easier to develop the higher level concepts and will - allow researchers to experiment with various techniques. - Beyond this theoretical application of higher level concepts, however, - the fact remains that many multithreaded programs are written using only the - lower level concepts, so they are useful in and of themselves, even if it's - hard to prove that their usage is correct. Since many users will be familiar - with these lower level concepts but unfamiliar with any of the higher - level concepts, supporting the lower level concepts provides - greater accessibility. -
-
- Rationale for the Lock Design - Programmers who are used to multithreaded programming issues will - quickly note that the &Boost.Thread; design for mutex lock concepts is not - thread-safe (this is - clearly documented as well). At first this may seem like a serious design - flaw. Why have a multithreading primitive that's not thread-safe - itself? - A lock object is not a synchronization primitive. A lock object's sole - responsibility is to ensure that a mutex is both locked and unlocked in a - manner that won't result in the common error of locking a mutex and then - forgetting to unlock it. This means that instances of a lock object are only - going to be created, at least in theory, within block scope and won't be - shared between threads. Only the mutex objects will be created outside of - block scope and/or shared between threads. Though it's possible to create a - lock object outside of block scope and to share it between threads, to do so - would not be a typical usage (in fact, to do so would likely be an - error). Nor are there any cases when such usage would be required. - Lock objects must maintain some state information. In order to allow a - program to determine if a try_lock or timed_lock was successful the lock - object must retain state indicating the success or failure of the call made - in its constructor. If a lock object were to have such state and remain - thread-safe it would need to synchronize access to the state information - which would result in roughly doubling the time of most operations. Worse, - since checking the state can occur only by a call after construction, we'd - have a race condition if the lock object were shared between threads. - So, to avoid the overhead of synchronizing access to the state - information and to avoid the race condition, the &Boost.Thread; library - simply does nothing to make lock objects thread-safe. Instead, sharing a - lock object between threads results in undefined behavior. Since the only - proper usage of lock objects is within block scope this isn't a problem, and - so long as the lock object is properly used there's no danger of any - multithreading issues. -
-
- Rationale for NonCopyable Thread Type - Programmers who are used to C libraries for multithreaded programming - are likely to wonder why &Boost.Thread; uses a noncopyable design for - boost::thread. After all, the C thread types are - copyable, and you often have a need for copying them within user - code. However, careful comparison of C designs to C++ designs shows a flaw - in this logic. - All C types are copyable. It is, in fact, not possible to make a - noncopyable type in C. For this reason types that represent system resources - in C are often designed to behave very similarly to a pointer to dynamic - memory. There's an API for acquiring the resource and an API for releasing - the resource. For memory we have pointers as the type and alloc/free for - the acquisition and release APIs. For files we have FILE* as the type and - fopen/fclose for the acquisition and release APIs. You can freely copy - instances of the types but must manually manage the lifetime of the actual - resource through the acquisition and release APIs. - C++ designs recognize that the acquisition and release APIs are error - prone and try to eliminate possible errors by acquiring the resource in the - constructor and releasing it in the destructor. The best example of such a - design is the std::iostream set of classes which can represent the same - resource as the FILE* type in C. A file is opened in the std::fstream's - constructor and closed in its destructor. However, if an iostream were - copyable it could lead to a file being closed twice, an obvious error, so - the std::iostream types are noncopyable by design. This is the same design - used by boost::thread, which is a simple and easy to understand design - that's consistent with other C++ standard types. - During the design of boost::thread it was pointed out that it would be - possible to allow it to be a copyable type if some form of "reference - management" were used, such as ref-counting or ref-lists, and many argued - for a boost::thread_ref design instead. The reasoning was that copying - "thread" objects was a typical need in the C libraries, and so presumably - would be in the C++ libraries as well. It was also thought that - implementations could provide more efficient reference management than - wrappers (such as boost::shared_ptr) around a noncopyable thread - concept. Analysis of whether or not these arguments would hold true doesn't - appear to bear them out. To illustrate the analysis we'll first provide - pseudo-code illustrating the six typical usage patterns of a thread - object. -
- 1. Use case: Simple creation of a thread. - - void foo() - { - create_thread(&bar); - } - -
-
- 2. Use case: Creation of a thread that's later joined. - - void foo() - { - thread = create_thread(&bar); - join(thread); - } - -
-
- 3. Use case: Simple creation of several threads in a loop. - - void foo() - { - for (int i=0; i<NUM_THREADS; ++i) - create_thread(&bar); - } - -
-
- 4. Use case: Creation of several threads in a loop which are later joined. - - void foo() - { - for (int i=0; i<NUM_THREADS; ++i) - threads[i] = create_thread(&bar); - for (int i=0; i<NUM_THREADS; ++i) - threads[i].join(); - } - -
-
- 5. Use case: Creation of a thread whose ownership is passed to another object/method. - - void foo() - { - thread = create_thread(&bar); - manager.owns(thread); - } - -
-
- 6. Use case: Creation of a thread whose ownership is shared between multiple - objects. - - void foo() - { - thread = create_thread(&bar); - manager1.add(thread); - manager2.add(thread); - } - -
- Of these usage patterns there's only one that requires reference - management (number 6). Hopefully it's fairly obvious that this usage pattern - simply won't occur as often as the other usage patterns. So there really - isn't a "typical need" for a thread concept, though there is some - need. - Since the need isn't typical we must use different criteria for - deciding on either a thread_ref or thread design. Possible criteria include - ease of use and performance. So let's analyze both of these - carefully. - With ease of use we can look at existing experience. The standard C++ - objects that represent a system resource, such as std::iostream, are - noncopyable, so we know that C++ programmers must at least be experienced - with this design. Most C++ developers are also used to smart pointers such - as boost::shared_ptr, so we know they can at least adapt to a thread_ref - concept with little effort. So existing experience isn't going to lead us to - a choice. - The other thing we can look at is how difficult it is to use both - types for the six usage patterns above. If we find it overly difficult to - use a concept for any of the usage patterns there would be a good argument - for choosing the other design. So we'll code all six usage patterns using - both designs. -
- 1. Comparison: simple creation of a thread. - - void foo() - { - thread thrd(&bar); - } - void foo() - { - thread_ref thrd = create_thread(&bar); - } - -
-
- 2. Comparison: creation of a thread that's later joined. - - void foo() - { - thread thrd(&bar); - thrd.join(); - } - void foo() - { - thread_ref thrd = - create_thread(&bar);thrd->join(); - } - -
-
- 3. Comparison: simple creation of several threads in a loop. - - void foo() - { - for (int i=0; i<NUM_THREADS; ++i) - thread thrd(&bar); - } - void foo() - { - for (int i=0; i<NUM_THREADS; ++i) - thread_ref thrd = create_thread(&bar); - } - -
-
- 4. Comparison: creation of several threads in a loop which are later joined. - - void foo() - { - std::auto_ptr<thread> threads[NUM_THREADS]; - for (int i=0; i<NUM_THREADS; ++i) - threads[i] = std::auto_ptr<thread>(new thread(&bar)); - for (int i= 0; i<NUM_THREADS; - ++i)threads[i]->join(); - } - void foo() - { - thread_ref threads[NUM_THREADS]; - for (int i=0; i<NUM_THREADS; ++i) - threads[i] = create_thread(&bar); - for (int i= 0; i<NUM_THREADS; - ++i)threads[i]->join(); - } - -
-
- 5. Comparison: creation of a thread whose ownership is passed to another object/method. - - void foo() - { - thread thrd* = new thread(&bar); - manager.owns(thread); - } - void foo() - { - thread_ref thrd = create_thread(&bar); - manager.owns(thrd); - } - -
-
- 6. Comparison: creation of a thread whose ownership is shared - between multiple objects. - - void foo() - { - boost::shared_ptr<thread> thrd(new thread(&bar)); - manager1.add(thrd); - manager2.add(thrd); - } - void foo() - { - thread_ref thrd = create_thread(&bar); - manager1.add(thrd); - manager2.add(thrd); - } - -
- This shows the usage patterns being nearly identical in complexity for - both designs. The only actual added complexity occurs because of the use of - operator new in - (4), - (5), and - (6); - and the use of std::auto_ptr and boost::shared_ptr in - (4) and - (6) - respectively. However, that's not really - much added complexity, and C++ programmers are used to using these idioms - anyway. Some may dislike the presence of operator new in user code, but - this can be eliminated by proper design of higher level concepts, such as - the boost::thread_group class that simplifies example - (4) - down to: - - void foo() - { - thread_group threads; - for (int i=0; i<NUM_THREADS; ++i) - threads.create_thread(&bar); - threads.join_all(); - } - - So ease of use is really a wash and not much help in picking a - design. - So what about performance? Looking at the above code examples, - we can analyze the theoretical impact to performance that both designs - have. For (1) - we can see that platforms that don't have a ref-counted native - thread type (POSIX, for instance) will be impacted by a thread_ref - design. Even if the native thread type is ref-counted there may be an impact - if more state information has to be maintained for concepts foreign to the - native API, such as clean up stacks for Win32 implementations. - For (2) - and (3) - the performance impact will be identical to - (1). - For (4) - things get a little more interesting and we find that theoretically at least - the thread_ref may perform faster since the thread design requires dynamic - memory allocation/deallocation. However, in practice there may be dynamic - allocation for the thread_ref design as well, it will just be hidden from - the user. As long as the implementation has to do dynamic allocations the - thread_ref loses again because of the reference management. For - (5) we see - the same impact as we do for - (4). - For (6) - we still have a possible impact to - the thread design because of dynamic allocation but thread_ref no longer - suffers because of its reference management, and in fact, theoretically at - least, the thread_ref may do a better job of managing the references. All of - this indicates that thread wins for - (1), - (2) and - (3); with - (4) - and (5) the - winner depending on the implementation and the platform but with the thread design - probably having a better chance; and with - (6) - it will again depend on the - implementation and platform but this time we favor thread_ref - slightly. Given all of this it's a narrow margin, but the thread design - prevails. - Given this analysis, and the fact that noncopyable objects for system - resources are the normal designs that C++ programmers are used to dealing - with, the &Boost.Thread; library has gone with a noncopyable design. -
-
- Rationale for not providing <emphasis>Event Variables</emphasis> - Event variables are simply far too - error-prone. boost::condition variables are a much safer - alternative. [Note that Graphical User Interface events are - a different concept, and are not what is being discussed here.] - Event variables were one of the first synchronization primitives. They - are still used today, for example, in the native Windows multithreading - API. Yet both respected computer science researchers and experienced - multithreading practitioners believe event variables are so inherently - error-prone that they should never be used, and thus should not be part of a - multithreading library. - Per Brinch Hansen &cite.Hansen73; analyzed event variables in some - detail, pointing out [emphasis his] that "event operations force - the programmer to be aware of the relative speeds of the sending and - receiving processes". His summary: -
- We must therefore conclude that event variables of the previous type - are impractical for system design. The effect of an interaction - between two processes must be independent of the speed at which it is - carried out. -
- Experienced programmers using the Windows platform today report that - event variables are a continuing source of errors, even after previous bad - experiences caused them to be very careful in their use of event - variables. Overt problems can be avoided, for example, by teaming the event - variable with a mutex, but that may just convert a race condition into another - problem, such as excessive resource use. One of the most distressing aspects - of the experience reports is the claim that many defects are latent. That - is, the programs appear to work correctly, but contain hidden timing - dependencies which will cause them to fail when environmental factors or - usage patterns change, altering relative thread timings. - The decision to exclude event variables from &Boost.Thread; has been - surprising to some Windows programmers. They have written programs which - work using event variables, and wonder what the problem is. It seems similar - to the "goto considered harmful" controversy of 30 years ago. It isn't that - events, like gotos, can't be made to work, but rather that virtually all - programs using alternatives will be easier to write, debug, read, maintain, - and will be less likely to contain latent defects. - [Rationale provided by Beman Dawes] -
-
diff --git a/doc/read_write_mutex-ref.xml b/doc/read_write_mutex-ref.xml deleted file mode 100644 index 747960fb..00000000 --- a/doc/read_write_mutex-ref.xml +++ /dev/null @@ -1,492 +0,0 @@ - - - %thread.entities; -]> - -
- - - - - - - - - - Specifies the - inter-class sheduling policy - to use when a set of threads try to obtain different types of - locks simultaneously. - - - - The only clock type supported by &Boost.Thread; is - TIME_UTC. The epoch for TIME_UTC - is 1970-01-01 00:00:00. - - - - - - - The read_write_mutex class is a model of the - ReadWriteMutex concept. - Unfortunately it turned out that the current implementation of Read/Write Mutex has - some serious problems. So it was decided not to put this implementation into - release grade code. Also discussions on the mailing list led to the - conclusion that the current concepts need to be rethought. In particular - the schedulings - Inter-Class Scheduling Policies are deemed unnecessary. - There seems to be common belief that a fair scheme suffices. - The following documentation has been retained however, to give - readers of this document the opportunity to study the original design. - - - - - The read_write_mutex class is a model of the - ReadWriteMutex concept. - It should be used to synchronize access to shared resources using - Unspecified - locking mechanics. - - For classes that model related mutex concepts, see - try_read_write_mutex and timed_read_write_mutex. - - The read_write_mutex class supplies the following typedefs, - which model - the specified locking strategies: - - - - - - Lock Name - Lock Concept - - - - - scoped_read_write_lock - ScopedReadWriteLock - - - scoped_read_lock - ScopedLock - - - scoped_write_lock - ScopedLock - - - - - - - The read_write_mutex class uses an - Unspecified - locking strategy, so attempts to recursively lock a read_write_mutex - object or attempts to unlock one by threads that don't own a lock on it result in - undefined behavior. - This strategy allows implementations to be as efficient as possible - on any given platform. It is, however, recommended that - implementations include debugging support to detect misuse when - NDEBUG is not defined. - - Like all - read/write mutex models - in &Boost.Thread;, read_write_mutex has two types of - scheduling policies, an - inter-class sheduling policy - between threads trying to obtain different types of locks and an - intra-class sheduling policy - between threads trying to obtain the same type of lock. - The read_write_mutex class allows the - programmer to choose what - inter-class sheduling policy - will be used; however, like all read/write mutex models, - read_write_mutex leaves the - intra-class sheduling policy as - Unspecified. - - - Self-deadlock is virtually guaranteed if a thread tries to - lock the same read_write_mutex multiple times - unless all locks are read-locks (but see below) - - - - boost::noncopyable - Exposition only - - - - boost::noncopyable - Exposition only - - - - implementation-defined - - - - implementation-defined - - - - implementation-defined - - - - - boost::read_write_scheduling_policy - - - Constructs a read_write_mutex object. - - - *this is in an unlocked state. - - - - - Destroys a read_write_mutex object. - - *this is in an unlocked state. - - Danger: Destruction of a - locked mutex is a serious programming error resulting in undefined - behavior such as a program crash. - - - - - - The try_read_write_mutex class is a model of the - TryReadWriteMutex concept. - Unfortunately it turned out that the current implementation of Read/Write Mutex has - some serious problems. So it was decided not to put this implementation into - release grade code. Also discussions on the mailing list led to the - conclusion that the current concepts need to be rethought. In particular - the schedulings - Inter-Class Scheduling Policies are deemed unnecessary. - There seems to be common belief that a fair scheme suffices. - The following documentation has been retained however, to give - readers of this document the opportunity to study the original design. - - - - - The try_read_write_mutex class is a model of the - TryReadWriteMutex concept. - It should be used to synchronize access to shared resources using - Unspecified - locking mechanics. - - For classes that model related mutex concepts, see - read_write_mutex and timed_read_write_mutex. - - The try_read_write_mutex class supplies the following typedefs, - which model - the specified locking strategies: - - - - - - Lock Name - Lock Concept - - - - - scoped_read_write_lock - ScopedReadWriteLock - - - scoped_try_read_write_lock - ScopedTryReadWriteLock - - - scoped_read_lock - ScopedLock - - - scoped_try_read_lock - ScopedTryLock - - - scoped_write_lock - ScopedLock - - - scoped_try_write_lock - ScopedTryLock - - - - - - - The try_read_write_mutex class uses an - Unspecified - locking strategy, so attempts to recursively lock a try_read_write_mutex - object or attempts to unlock one by threads that don't own a lock on it result in - undefined behavior. - This strategy allows implementations to be as efficient as possible - on any given platform. It is, however, recommended that - implementations include debugging support to detect misuse when - NDEBUG is not defined. - - Like all - read/write mutex models - in &Boost.Thread;, try_read_write_mutex has two types of - scheduling policies, an - inter-class sheduling policy - between threads trying to obtain different types of locks and an - intra-class sheduling policy - between threads trying to obtain the same type of lock. - The try_read_write_mutex class allows the - programmer to choose what - inter-class sheduling policy - will be used; however, like all read/write mutex models, - try_read_write_mutex leaves the - intra-class sheduling policy as - Unspecified. - - - Self-deadlock is virtually guaranteed if a thread tries to - lock the same try_read_write_mutex multiple times - unless all locks are read-locks (but see below) - - - - boost::noncopyable - Exposition only - - - - implementation-defined - - - - implementation-defined - - - - implementation-defined - - - - implementation-defined - - - - implementation-defined - - - - implementation-defined - - - - - boost::read_write_scheduling_policy - - - Constructs a try_read_write_mutex object. - - - *this is in an unlocked state. - - - - - Destroys a try_read_write_mutex object. - - *this is in an unlocked state. - - Danger: Destruction of a - locked mutex is a serious programming error resulting in undefined - behavior such as a program crash. - - - - - - The timed_read_write_mutex class is a model of the - TimedReadWriteMutex concept. - Unfortunately it turned out that the current implementation of Read/Write Mutex has - some serious problems. So it was decided not to put this implementation into - release grade code. Also discussions on the mailing list led to the - conclusion that the current concepts need to be rethought. In particular - the schedulings - Inter-Class Scheduling Policies are deemed unnecessary. - There seems to be common belief that a fair scheme suffices. - The following documentation has been retained however, to give - readers of this document the opportunity to study the original design. - - - - - The timed_read_write_mutex class is a model of the - TimedReadWriteMutex concept. - It should be used to synchronize access to shared resources using - Unspecified - locking mechanics. - - For classes that model related mutex concepts, see - read_write_mutex and try_read_write_mutex. - - The timed_read_write_mutex class supplies the following typedefs, - which model - the specified locking strategies: - - - - - - Lock Name - Lock Concept - - - - - scoped_read_write_lock - ScopedReadWriteLock - - - scoped_try_read_write_lock - ScopedTryReadWriteLock - - - scoped_timed_read_write_lock - ScopedTimedReadWriteLock - - - scoped_read_lock - ScopedLock - - - scoped_try_read_lock - ScopedTryLock - - - scoped_timed_read_lock - ScopedTimedLock - - - scoped_write_lock - ScopedLock - - - scoped_try_write_lock - ScopedTryLock - - - scoped_timed_write_lock - ScopedTimedLock - - - - - - - The timed_read_write_mutex class uses an - Unspecified - locking strategy, so attempts to recursively lock a timed_read_write_mutex - object or attempts to unlock one by threads that don't own a lock on it result in - undefined behavior. - This strategy allows implementations to be as efficient as possible - on any given platform. It is, however, recommended that - implementations include debugging support to detect misuse when - NDEBUG is not defined. - - Like all - read/write mutex models - in &Boost.Thread;, timed_read_write_mutex has two types of - scheduling policies, an - inter-class sheduling policy - between threads trying to obtain different types of locks and an - intra-class sheduling policy - between threads trying to obtain the same type of lock. - The timed_read_write_mutex class allows the - programmer to choose what - inter-class sheduling policy - will be used; however, like all read/write mutex models, - timed_read_write_mutex leaves the - intra-class sheduling policy as - Unspecified. - - - Self-deadlock is virtually guaranteed if a thread tries to - lock the same timed_read_write_mutex multiple times - unless all locks are read-locks (but see below) - - - - implementation-defined - - - - implementation-defined - - - - implementation-defined - - - - implementation-defined - - - - implementation-defined - - - - implementation-defined - - - - implementation-defined - - - - implementation-defined - - - - implementation-defined - - - - - boost::read_write_scheduling_policy - - - Constructs a timed_read_write_mutex object. - - - *this is in an unlocked state. - - - - - Destroys a timed_read_write_mutex object. - - *this is in an unlocked state. - - Danger: Destruction of a - locked mutex is a serious programming error resulting in undefined - behavior such as a program crash. - - - -
diff --git a/doc/recursive_mutex-ref.xml b/doc/recursive_mutex-ref.xml deleted file mode 100644 index de95494a..00000000 --- a/doc/recursive_mutex-ref.xml +++ /dev/null @@ -1,306 +0,0 @@ - - - %thread.entities; -]> - -
- - - - The recursive_mutex class is a model of the - Mutex concept. - - - - The recursive_mutex class is a model of the - Mutex concept. - It should be used to synchronize access to shared resources using - Recursive - locking mechanics. - - For classes that model related mutex concepts, see - recursive_try_mutex and recursive_timed_mutex. - - For Unspecified - locking mechanics, see mutex, - try_mutex, and timed_mutex. - - - The recursive_mutex class supplies the following typedef, - which models the specified locking strategy: - - - Supported Lock Types - - - - Lock Name - Lock Concept - - - - - scoped_lock - ScopedLock - - - -
-
- - The recursive_mutex class uses a - Recursive - locking strategy, so attempts to recursively lock a - recursive_mutex object - succeed and an internal "lock count" is maintained. - Attempts to unlock a recursive_mutex object - by threads that don't own a lock on it result in - undefined behavior. - - Like all - mutex models - in &Boost.Thread;, recursive_mutex leaves the - scheduling policy - as Unspecified. - Programmers should make no assumptions about the order in which - waiting threads acquire a lock. -
- - - boost::noncopyable - Exposition only - - - - implementation-defined - - - - Constructs a recursive_mutex object. - - - *this is in an unlocked state. - - - - - Destroys a recursive_mutex object. - - *this is in an unlocked state. - - Danger: Destruction of a - locked mutex is a serious programming error resulting in undefined - behavior such as a program crash. - -
- - - - The recursive_try_mutex class is a model of the - TryMutex concept. - - - - The recursive_try_mutex class is a model of the - TryMutex concept. - It should be used to synchronize access to shared resources using - Recursive - locking mechanics. - - For classes that model related mutex concepts, see - recursive_mutex and recursive_timed_mutex. - - For Unspecified - locking mechanics, see mutex, - try_mutex, and timed_mutex. - - - The recursive_try_mutex class supplies the following typedefs, - which model the specified locking strategies: - - - Supported Lock Types - - - - Lock Name - Lock Concept - - - - - scoped_lock - ScopedLock - - - scoped_try_lock - ScopedTryLock - - - -
-
- - The recursive_try_mutex class uses a - Recursive - locking strategy, so attempts to recursively lock a - recursive_try_mutex object - succeed and an internal "lock count" is maintained. - Attempts to unlock a recursive_mutex object - by threads that don't own a lock on it result in - undefined behavior. - - Like all - mutex models - in &Boost.Thread;, recursive_try_mutex leaves the - scheduling policy - as Unspecified. - Programmers should make no assumptions about the order in which - waiting threads acquire a lock. -
- - - boost::noncopyable - Exposition only - - - - implementation-defined - - - - implementation-defined - - - - Constructs a recursive_try_mutex object. - - - *this is in an unlocked state. - - - - - Destroys a recursive_try_mutex object. - - - *this is in an unlocked state. - - Danger: Destruction of a - locked mutex is a serious programming error resulting in undefined - behavior such as a program crash. - -
- - - - The recursive_timed_mutex class is a model of the - TimedMutex concept. - - - - The recursive_timed_mutex class is a model of the - TimedMutex concept. - It should be used to synchronize access to shared resources using - Recursive - locking mechanics. - - For classes that model related mutex concepts, see - recursive_mutex and recursive_try_mutex. - - For Unspecified - locking mechanics, see mutex, - try_mutex, and timed_mutex. - - - The recursive_timed_mutex class supplies the following typedefs, - which model the specified locking strategies: - - - Supported Lock Types - - - - Lock Name - Lock Concept - - - - - scoped_lock - ScopedLock - - - scoped_try_lock - ScopedTryLock - - - scoped_timed_lock - ScopedTimedLock - - - -
-
- - The recursive_timed_mutex class uses a - Recursive - locking strategy, so attempts to recursively lock a - recursive_timed_mutex object - succeed and an internal "lock count" is maintained. - Attempts to unlock a recursive_mutex object - by threads that don't own a lock on it result in - undefined behavior. - - Like all - mutex models - in &Boost.Thread;, recursive_timed_mutex leaves the - scheduling policy - as Unspecified. - Programmers should make no assumptions about the order in which - waiting threads acquire a lock. -
- - - boost::noncopyable - Exposition only - - - - implementation-defined - - - - implementation-defined - - - - implementation-defined - - - - Constructs a recursive_timed_mutex object. - - - *this is in an unlocked state. - - - - - Destroys a recursive_timed_mutex object. - - *this is in an unlocked state. - - Danger: Destruction of a - locked mutex is a serious programming error resulting in undefined - behavior such as a program crash. - -
-
-
diff --git a/doc/reference.xml b/doc/reference.xml deleted file mode 100644 index d6ea4d75..00000000 --- a/doc/reference.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - %thread.entities; -]> - - - - - - - - - - - - - - diff --git a/doc/release_notes.xml b/doc/release_notes.xml deleted file mode 100644 index 792f9fc4..00000000 --- a/doc/release_notes.xml +++ /dev/null @@ -1,204 +0,0 @@ - - - %thread.entities; -]> - -
- Release Notes -
- Boost 1.34.0 - -
- New team of maintainers - - - Since the original author William E. Kempf no longer is available to - maintain the &Boost.Thread; library, a new team has been formed - in an attempt to continue the work on &Boost.Thread;. - Fortunately William E. Kempf has given - - permission - to use his work under the boost license. - - - The team currently consists of - - - Anthony Williams, for the Win32 platform, - - - Roland Schwarz, for the linux platform, and various "housekeeping" tasks. - - - Volunteers for other platforms are welcome! - - - As the &Boost.Thread; was kind of orphaned over the last release, this release - attempts to fix the known bugs. Upcoming releases will bring in new things. - -
- -
- read_write_mutex still broken - - - - It has been decided not to release the Read/Write Mutex, since the current - implementation suffers from a serious bug. The documentation of the concepts - has been included though, giving the interested reader an opportunity to study the - original concepts. Please refer to the following links if you are interested - which problems led to the decision to held back this mutex type.The issue - has been discovered before 1.33 was released and the code has - been omitted from that release. A reworked mutex is expected to appear in 1.35. - Also see: - - read_write_mutex bug - and - - read_write_mutex fundamentally broken in 1.33 - - -
- -
-
- Boost 1.32.0 - -
- Documentation converted to BoostBook - - The documentation was converted to BoostBook format, - and a number of errors and inconsistencies were - fixed in the process. - Since this was a fairly large task, there are likely to be - more errors and inconsistencies remaining. If you find any, - please report them! -
- - - -
- Barrier functionality added - - A new class, boost::barrier, was added. -
- -
- Read/write mutex functionality added - - New classes, - boost::read_write_mutex, - boost::try_read_write_mutex, and - boost::timed_read_write_mutex - were added. - - Since the read/write mutex and related classes are new, - both interface and implementation are liable to change - in future releases of &Boost.Thread;. - The lock concepts and lock promotion in particular are - still under discussion and very likely to change. - -
- -
- Thread-specific pointer functionality changed - - The boost::thread_specific_ptr - constructor now takes an optional pointer to a cleanup function that - is called to release the thread-specific data that is being pointed - to by boost::thread_specific_ptr objects. - - Fixed: the number of available thread-specific storage "slots" - is too small on some platforms. - - Fixed: thread_specific_ptr::reset() - doesn't check error returned by tss::set() - (the tss::set() function now throws - if it fails instead of returning an error code). - - Fixed: calling - boost::thread_specific_ptr::reset() or - boost::thread_specific_ptr::release() - causes double-delete: once when - boost::thread_specific_ptr::reset() or - boost::thread_specific_ptr::release() - is called and once when - boost::thread_specific_ptr::~thread_specific_ptr() - is called. -
- -
- Mutex implementation changed for Win32 - - On Win32, boost::mutex, - boost::try_mutex, boost::recursive_mutex, - and boost::recursive_try_mutex now use a Win32 critical section - whenever possible; otherwise they use a Win32 mutex. As before, - boost::timed_mutex and - boost::recursive_timed_mutex use a Win32 mutex. -
- -
- Windows CE support improved - - Minor changes were made to make Boost.Thread work on Windows CE. -
-
-
diff --git a/doc/shared_mutex_ref.qbk b/doc/shared_mutex_ref.qbk new file mode 100644 index 00000000..4b2c2ec4 --- /dev/null +++ b/doc/shared_mutex_ref.qbk @@ -0,0 +1,35 @@ +[section:shared_mutex Class `shared_mutex`] + + class shared_mutex + { + public: + shared_mutex(); + ~shared_mutex(); + + void lock_shared(); + bool try_lock_shared(); + bool timed_lock_shared(system_time const& timeout); + void unlock_shared(); + + void lock(); + bool try_lock(); + bool timed_lock(system_time const& timeout); + void unlock(); + + void lock_upgrade(); + void unlock_upgrade(); + + void unlock_upgrade_and_lock(); + void unlock_and_lock_upgrade(); + void unlock_and_lock_shared(); + void unlock_upgrade_and_lock_shared(); + }; + +The class `boost::shared_mutex` provides an implementation of a multiple-reader / single-writer mutex. It implements the +__upgrade_lockable_concept__. + +Multiple concurrent calls to __lock_ref__, __try_lock_ref__, __timed_lock_ref__, __lock_shared_ref__, __try_lock_shared_ref__ and +__timed_lock_shared_ref__ shall be permitted. + + +[endsect] diff --git a/doc/thread-ref.xml b/doc/thread-ref.xml deleted file mode 100644 index 270302ca..00000000 --- a/doc/thread-ref.xml +++ /dev/null @@ -1,270 +0,0 @@ - - - %thread.entities; -]> - -
- - - - The thread class represents threads of - execution, and provides the functionality to create and manage - threads within the &Boost.Thread; library. See - for a precise description of - thread of execution, - and for definitions of threading-related terms and of thread states such as - blocked. - - - - A thread of execution - has an initial function. For the program's initial thread, the initial - function is main(). For other threads, the initial - function is operator() of the function object passed to - the thread object's constructor. - - A thread of execution is said to be "finished" - or to have "finished execution" when its initial function returns or - is terminated. This includes completion of all thread cleanup - handlers, and completion of the normal C++ function return behaviors, - such as destruction of automatic storage (stack) objects and releasing - any associated implementation resources. - - A thread object has an associated state which is either - "joinable" or "non-joinable". - - Except as described below, the policy used by an implementation - of &Boost.Thread; to schedule transitions between thread states is - unspecified. - - Just as the lifetime of a file may be different from the - lifetime of an iostream object which represents the file, the lifetime - of a thread of execution may be different from the - thread object which represents the thread of - execution. In particular, after a call to join(), - the thread of execution will no longer exist even though the - thread object continues to exist until the - end of its normal lifetime. The converse is also possible; if - a thread object is destroyed without - join() first having been called, the thread of execution - continues until its initial function completes. - - - - boost::noncopyable - Exposition only - - - - Constructs a thread object - representing the current thread of execution. - - *this is non-joinable. - - Danger: - *this is valid only within the current thread. - - - - - const boost::function0<void>& - - - - Starts a new thread of execution and constructs a - thread object representing it. - Copies threadfunc (which in turn copies - the function object wrapped by threadfunc) - to an internal location which persists for the lifetime - of the new thread of execution. Calls operator() - on the copy of the threadfunc function object - in the new thread of execution. - - - *this is joinable. - - boost::thread_resource_error if a new thread - of execution cannot be started. - - - - Destroys *this. The actual thread of - execution may continue to execute after the - thread object has been destroyed. - - - If *this is joinable the actual thread - of execution becomes "detached". Any resources used - by the thread will be reclaimed when the thread of execution - completes. To ensure such a thread of execution runs to completion - before the thread object is destroyed, call - join(). - - - - - bool - - - const thread& - - - The thread is non-terminated or *this - is joinable. - - true if *this and - rhs represent the same thread of - execution. - - - - bool - - - const thread& - - - The thread is non-terminated or *this - is joinable. - - !(*this==rhs). - - - - - - void - - *this is joinable. - - The current thread of execution blocks until the - initial function of the thread of execution represented by - *this finishes and all resources are - reclaimed. - - *this is non-joinable. - - If *this == thread() the result is - implementation-defined. If the implementation doesn't - detect this the result will be - deadlock. - - - - - - - void - - - const xtime& - - - The current thread of execution blocks until - xt is reached. - - - - void - - The current thread of execution is placed in the - ready - state. - - - Allow the current thread to give up the rest of its - time slice (or other scheduling quota) to another thread. - Particularly useful in non-preemptive implementations. - - - - - - - - The thread_group class provides a container - for easy grouping of threads to simplify several common thread - creation and management idioms. - - - - boost::noncopyable - Exposition only - - - - - Constructs an empty thread_group - container. - - - - Destroys each contained thread object. Destroys *this. - - Behavior is undefined if another thread references - *this during the execution of the destructor. - - - - - - thread* - - - const boost::function0<void>& - - - Creates a new thread object - that executes threadfunc and adds it to the - thread_group container object's list of managed - thread objects. - - Pointer to the newly created - thread object. - - - - void - - - thread* - - - Adds thrd to the - thread_group object's list of managed - thread objects. The thrd - object must have been allocated via operator new and will - be deleted when the group is destroyed. - - - - void - - - thread* - - - Removes thread from *this's - list of managed thread objects. - - ??? if - thrd is not in *this's list - of managed thread objects. - - - - void - - Calls join() on each of the managed - thread objects. - - - - -
diff --git a/doc/thread.qbk b/doc/thread.qbk new file mode 100644 index 00000000..aaf975fd --- /dev/null +++ b/doc/thread.qbk @@ -0,0 +1,150 @@ +[article Thread + [quickbook 1.4] + [authors [Williams, Anthony]] + [copyright 2007-8 Anthony Williams] + [purpose C++ Library for launching threads and synchronizing data between them] + [category text] + [license + 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]) + ] +] + +[template lockable_concept_link[link_text] [link thread.synchronization.mutex_concepts.lockable [link_text]]] +[def __lockable_concept__ [lockable_concept_link `Lockable` concept]] +[def __lockable_concept_type__ [lockable_concept_link `Lockable`]] + +[template timed_lockable_concept_link[link_text] [link thread.synchronization.mutex_concepts.timed_lockable [link_text]]] +[def __timed_lockable_concept__ [timed_lockable_concept_link `TimedLockable` concept]] +[def __timed_lockable_concept_type__ [timed_lockable_concept_link `TimedLockable`]] + +[template shared_lockable_concept_link[link_text] [link thread.synchronization.mutex_concepts.shared_lockable [link_text]]] +[def __shared_lockable_concept__ [shared_lockable_concept_link `SharedLockable` concept]] +[def __shared_lockable_concept_type__ [shared_lockable_concept_link `SharedLockable`]] + +[template upgrade_lockable_concept_link[link_text] [link thread.synchronization.mutex_concepts.upgrade_lockable [link_text]]] +[def __upgrade_lockable_concept__ [upgrade_lockable_concept_link `UpgradeLockable` concept]] +[def __upgrade_lockable_concept_type__ [upgrade_lockable_concept_link `UpgradeLockable`]] + + +[template lock_ref_link[link_text] [link thread.synchronization.mutex_concepts.lockable.lock [link_text]]] +[def __lock_ref__ [lock_ref_link `lock()`]] + +[template unlock_ref_link[link_text] [link thread.synchronization.mutex_concepts.lockable.unlock [link_text]]] +[def __unlock_ref__ [unlock_ref_link `unlock()`]] + +[template try_lock_ref_link[link_text] [link thread.synchronization.mutex_concepts.lockable.try_lock [link_text]]] +[def __try_lock_ref__ [try_lock_ref_link `try_lock()`]] + +[template timed_lock_ref_link[link_text] [link thread.synchronization.mutex_concepts.timed_lockable.timed_lock [link_text]]] +[def __timed_lock_ref__ [timed_lock_ref_link `timed_lock()`]] + +[template timed_lock_duration_ref_link[link_text] [link thread.synchronization.mutex_concepts.timed_lockable.timed_lock_duration [link_text]]] +[def __timed_lock_duration_ref__ [timed_lock_duration_ref_link `timed_lock()`]] + +[template lock_shared_ref_link[link_text] [link thread.synchronization.mutex_concepts.shared_lockable.lock_shared [link_text]]] +[def __lock_shared_ref__ [lock_shared_ref_link `lock_shared()`]] + +[template unlock_shared_ref_link[link_text] [link thread.synchronization.mutex_concepts.shared_lockable.unlock_shared [link_text]]] +[def __unlock_shared_ref__ [unlock_shared_ref_link `unlock_shared()`]] + +[template try_lock_shared_ref_link[link_text] [link thread.synchronization.mutex_concepts.shared_lockable.try_lock_shared [link_text]]] +[def __try_lock_shared_ref__ [try_lock_shared_ref_link `try_lock_shared()`]] + +[template timed_lock_shared_ref_link[link_text] [link thread.synchronization.mutex_concepts.shared_lockable.timed_lock_shared [link_text]]] +[def __timed_lock_shared_ref__ [timed_lock_shared_ref_link `timed_lock_shared()`]] + +[template timed_lock_shared_duration_ref_link[link_text] [link thread.synchronization.mutex_concepts.shared_lockable.timed_lock_shared_duration [link_text]]] +[def __timed_lock_shared_duration_ref__ [timed_lock_shared_duration_ref_link `timed_lock_shared()`]] + +[template lock_upgrade_ref_link[link_text] [link thread.synchronization.mutex_concepts.upgrade_lockable.lock_upgrade [link_text]]] +[def __lock_upgrade_ref__ [lock_upgrade_ref_link `lock_upgrade()`]] + +[template unlock_upgrade_ref_link[link_text] [link thread.synchronization.mutex_concepts.upgrade_lockable.unlock_upgrade [link_text]]] +[def __unlock_upgrade_ref__ [unlock_upgrade_ref_link `unlock_upgrade()`]] + +[template unlock_upgrade_and_lock_ref_link[link_text] [link thread.synchronization.mutex_concepts.upgrade_lockable.unlock_upgrade_and_lock [link_text]]] +[def __unlock_upgrade_and_lock_ref__ [unlock_upgrade_and_lock_ref_link `unlock_upgrade_and_lock()`]] + +[template unlock_and_lock_upgrade_ref_link[link_text] [link thread.synchronization.mutex_concepts.upgrade_lockable.unlock_and_lock_upgrade [link_text]]] +[def __unlock_and_lock_upgrade_ref__ [unlock_and_lock_upgrade_ref_link `unlock_and_lock_upgrade()`]] + +[template unlock_upgrade_and_lock_shared_ref_link[link_text] [link thread.synchronization.mutex_concepts.upgrade_lockable.unlock_upgrade_and_lock_shared [link_text]]] +[def __unlock_upgrade_and_lock_shared_ref__ [unlock_upgrade_and_lock_shared_ref_link `unlock_upgrade_and_lock_shared()`]] + +[template owns_lock_ref_link[link_text] [link thread.synchronization.locks.unique_lock.owns_lock [link_text]]] +[def __owns_lock_ref__ [owns_lock_ref_link `owns_lock()`]] + +[template owns_lock_shared_ref_link[link_text] [link thread.synchronization.locks.shared_lock.owns_lock [link_text]]] +[def __owns_lock_shared_ref__ [owns_lock_shared_ref_link `owns_lock()`]] + +[template mutex_func_ref_link[link_text] [link thread.synchronization.locks.unique_lock.mutex [link_text]]] +[def __mutex_func_ref__ [mutex_func_ref_link `mutex()`]] + +[def __boost_thread__ [*Boost.Thread]] +[def __not_a_thread__ ['Not-a-Thread]] +[def __interruption_points__ [link interruption_points ['interruption points]]] + +[def __mutex__ [link thread.synchronization.mutex_types.mutex `boost::mutex`]] +[def __try_mutex__ [link thread.synchronization.mutex_types.try_mutex `boost::try_mutex`]] +[def __timed_mutex__ [link thread.synchronization.mutex_types.timed_mutex `boost::timed_mutex`]] +[def __recursive_mutex__ [link thread.synchronization.mutex_types.recursive_mutex `boost::recursive_mutex`]] +[def __recursive_try_mutex__ [link thread.synchronization.mutex_types.recursive_try_mutex `boost::recursive_try_mutex`]] +[def __recursive_timed_mutex__ [link thread.synchronization.mutex_types.recursive_timed_mutex `boost::recursive_timed_mutex`]] +[def __shared_mutex__ [link thread.synchronization.mutex_types.shared_mutex `boost::shared_mutex`]] + +[def __lock_guard__ [link thread.synchronization.locks.lock_guard `boost::lock_guard`]] +[def __unique_lock__ [link thread.synchronization.locks.unique_lock `boost::unique_lock`]] +[def __shared_lock__ [link thread.synchronization.locks.shared_lock `boost::shared_lock`]] +[def __upgrade_lock__ [link thread.synchronization.locks.upgrade_lock `boost::upgrade_lock`]] +[def __upgrade_to_unique_lock__ [link thread.synchronization.locks.upgrade_to_unique_lock `boost::upgrade_to_unique_lock`]] + + +[def __thread__ [link thread.thread_management.thread `boost::thread`]] +[def __thread_id__ [link thread.thread_management.thread.id `boost::thread::id`]] +[template join_link[link_text] [link thread.thread_management.thread.join [link_text]]] +[def __join__ [join_link `join()`]] +[template timed_join_link[link_text] [link thread.thread_management.thread.timed_join [link_text]]] +[def __timed_join__ [timed_join_link `timed_join()`]] +[def __detach__ [link thread.thread_management.thread.detach `detach()`]] +[def __interrupt__ [link thread.thread_management.thread.interrupt `interrupt()`]] +[def __sleep__ [link thread.thread_management.this_thread.sleep `boost::this_thread::sleep()`]] + +[def __interruption_enabled__ [link thread.thread_management.this_thread.interruption_enabled `boost::this_thread::interruption_enabled()`]] +[def __interruption_requested__ [link thread.thread_management.this_thread.interruption_requested `boost::this_thread::interruption_requested()`]] +[def __interruption_point__ [link thread.thread_management.this_thread.interruption_point `boost::this_thread::interruption_point()`]] +[def __disable_interruption__ [link thread.thread_management.this_thread.disable_interruption `boost::this_thread::disable_interruption`]] +[def __restore_interruption__ [link thread.thread_management.this_thread.restore_interruption `boost::this_thread::restore_interruption`]] + +[def __thread_resource_error__ `boost::thread_resource_error`] +[def __thread_interrupted__ `boost::thread_interrupted`] +[def __barrier__ [link thread.synchronization.barriers.barrier `boost::barrier`]] + +[template cond_wait_link[link_text] [link thread.synchronization.condvar_ref.condition_variable.wait [link_text]]] +[def __cond_wait__ [cond_wait_link `wait()`]] +[template cond_timed_wait_link[link_text] [link thread.synchronization.condvar_ref.condition_variable.timed_wait [link_text]]] +[def __cond_timed_wait__ [cond_timed_wait_link `timed_wait()`]] +[template cond_any_wait_link[link_text] [link thread.synchronization.condvar_ref.condition_variable_any.wait [link_text]]] +[def __cond_any_wait__ [cond_any_wait_link `wait()`]] +[template cond_any_timed_wait_link[link_text] [link thread.synchronization.condvar_ref.condition_variable_any.timed_wait [link_text]]] +[def __cond_any_timed_wait__ [cond_any_timed_wait_link `timed_wait()`]] + +[def __blocked__ ['blocked]] + +[include overview.qbk] +[include changes.qbk] + +[include thread_ref.qbk] + +[section:synchronization Synchronization] +[include mutex_concepts.qbk] +[include mutexes.qbk] +[include condition_variables.qbk] +[include once.qbk] +[include barrier.qbk] +[endsect] + +[include tss.qbk] + +[include acknowledgements.qbk] diff --git a/doc/thread.xml b/doc/thread.xml deleted file mode 100644 index f5c30883..00000000 --- a/doc/thread.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - %thread.entities; -]> - - - - - William - E. - Kempf - - - 2001 - 2002 - 2003 - William E. Kempf - - - Subject to the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) - - Portable C++ multi-threading - - Boost.Thread - - Boost.Thread - - - - - - - - - - - - - - diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk new file mode 100644 index 00000000..caadc362 --- /dev/null +++ b/doc/thread_ref.qbk @@ -0,0 +1,933 @@ +[section:thread_management Thread Management] + +[heading Synopsis] + +The __thread__ class is responsible for launching and managing threads. Each __thread__ object represents a single thread of execution, +or __not_a_thread__, and at most one __thread__ object represents a given thread of execution: objects of type __thread__ are not +copyable. + +Objects of type __thread__ are movable, however, so they can be stored in move-aware containers, and returned from functions. This +allows the details of thread creation to be wrapped in a function. + + boost::thread make_thread(); + + void f() + { + boost::thread some_thread=make_thread(); + some_thread.join(); + } + +[heading Launching threads] + +A new thread is launched by passing an object of a callable type that can be invoked with no parameters to the constructor. The +object is then copied into internal storage, and invoked on the newly-created thread of execution. If the object must not (or +cannot) be copied, then `boost::ref` can be used to pass in a reference to the function object. In this case, the user of +__boost_thread__ must ensure that the referred-to object outlives the newly-created thread of execution. + + struct callable + { + void operator()(); + }; + + boost::thread copies_are_safe() + { + callable x; + return boost::thread(x); + } // x is destroyed, but the newly-created thread has a copy, so this is OK + + boost::thread oops() + { + callable x; + return boost::thread(boost::ref(x)); + } // x is destroyed, but the newly-created thread still has a reference + // this leads to undefined behaviour + +If you wish to construct an instance of __thread__ with a function or callable object that requires arguments to be supplied, +this can be done using `boost::bind`: + + void find_the_question(int the_answer); + + boost::thread deep_thought_2(boost::bind(find_the_question,42)); + +[heading Joining and detaching] + +When the __thread__ object that represents a thread of execution is destroyed the thread becomes ['detached]. Once a thread is +detached, it will continue executing until the invocation of the function or callable object supplied on construction has completed, +or the program is terminated. A thread can also be detached by explicitly invoking the __detach__ member function on the __thread__ +object. In this case, the __thread__ object ceases to represent the now-detached thread, and instead represents __not_a_thread__. + +In order to wait for a thread of execution to finish, the __join__ or __timed_join__ member functions of the __thread__ object must be +used. __join__ will block the calling thread until the thread represented by the __thread__ object has completed. If the thread of +execution represented by the __thread__ object has already completed, or the __thread__ object represents __not_a_thread__, then __join__ +returns immediately. __timed_join__ is similar, except that a call to __timed_join__ will also return if the thread being waited for +does not complete when the specified time has elapsed. + +[heading Interruption] + +A running thread can be ['interrupted] by invoking the __interrupt__ member function of the corresponding __thread__ object. When the +interrupted thread next executes one of the specified __interruption_points__ (or if it is currently __blocked__ whilst executing one) +with interruption enabled, then a __thread_interrupted__ exception will be thrown in the interrupted thread. If not caught, +this will cause the execution of the interrupted thread to terminate. As with any other exception, the stack will be unwound, and +destructors for objects of automatic storage duration will be executed. + +If a thread wishes to avoid being interrupted, it can create an instance of __disable_interruption__. Objects of this class disable +interruption for the thread that created them on construction, and restore the interruption state to whatever it was before on +destruction: + + void f() + { + // interruption enabled here + { + boost::this_thread::disable_interruption di; + // interruption disabled + { + boost::this_thread::disable_interruption di2; + // interruption still disabled + } // di2 destroyed, interruption state restored + // interruption still disabled + } // di destroyed, interruption state restored + // interruption now enabled + } + +The effects of an instance of __disable_interruption__ can be temporarily reversed by constructing an instance of +__restore_interruption__, passing in the __disable_interruption__ object in question. This will +restore the interruption state to what it was when the __disable_interruption__ object was constructed, and then +disable interruption again when the __restore_interruption__ object is destroyed. + + void g() + { + // interruption enabled here + { + boost::this_thread::disable_interruption di; + // interruption disabled + { + boost::this_thread::restore_interruption ri(di); + // interruption now enabled + } // ri destroyed, interruption disable again + } // di destroyed, interruption state restored + // interruption now enabled + } + +At any point, the interruption state for the current thread can be queried by calling __interruption_enabled__. + +[#interruption_points] + +[heading Predefined Interruption Points] + +The following functions are ['interruption points], which will throw __thread_interrupted__ if interruption is enabled for the +current thread, and interruption is requested for the current thread: + +* [join_link `boost::thread::join()`] +* [timed_join_link `boost::thread::timed_join()`] +* [cond_wait_link `boost::condition_variable::wait()`] +* [cond_timed_wait_link `boost::condition_variable::timed_wait()`] +* [cond_any_wait_link `boost::condition_variable_any::wait()`] +* [cond_any_timed_wait_link `boost::condition_variable_any::timed_wait()`] +* [link thread.thread_management.thread.sleep `boost::thread::sleep()`] +* __sleep__ +* __interruption_point__ + +[heading Thread IDs] + +Objects of class __thread_id__ can be used to identify threads. Each running thread of execution has a unique ID obtainable +from the corresponding __thread__ by calling the `get_id()` member function, or by calling `boost::this_thread::get_id()` from +within the thread. Objects of class __thread_id__ can be copied, and used as keys in associative containers: the full range of +comparison operators is provided. Thread IDs can also be written to an output stream using the stream insertion operator, though the +output format is unspecified. + +Each instance of __thread_id__ either refers to some thread, or __not_a_thread__. Instances that refer to __not_a_thread__ +compare equal to each other, but not equal to any instances that refer to an actual thread of execution. The comparison operators on +__thread_id__ yield a total order for every non-equal thread ID. + +[section:thread Class `thread`] + + class thread + { + public: + thread(); + ~thread(); + + template + explicit thread(F f); + + template + thread(detail::thread_move_t f); + + // move support + thread(detail::thread_move_t x); + thread& operator=(detail::thread_move_t x); + operator detail::thread_move_t(); + detail::thread_move_t move(); + + void swap(thread& x); + + class id; + id get_id() const; + + bool joinable() const; + void join(); + bool timed_join(const system_time& wait_until); + + template + bool timed_join(TimeDuration const& rel_time); + + void detach(); + + static unsigned hardware_concurrency(); + + typedef platform-specific-type native_handle_type; + native_handle_type native_handle(); + + void interrupt(); + bool interruption_requested() const; + + // backwards compatibility + bool operator==(const thread& other) const; + bool operator!=(const thread& other) const; + + static void yield(); + static void sleep(const system_time& xt); + }; + +[section:default_constructor Default Constructor] + + thread(); + +[variablelist + +[[Effects:] [Constructs a __thread__ instance that refers to __not_a_thread__.]] + +[[Throws:] [Nothing]] + +] + +[endsect] + +[section:callable_constructor Thread Constructor] + + template + thread(Callable func); + +[variablelist + +[[Preconditions:] [`Callable` must by copyable.]] + +[[Effects:] [`func` is copied into storage managed internally by the thread library, and that copy is invoked on a newly-created thread of execution.]] + +[[Postconditions:] [`*this` refers to the newly created thread of execution.]] + +[[Throws:] [__thread_resource_error__ if an error occurs.]] + +] + +[endsect] + +[section:destructor Thread Destructor] + + ~thread(); + +[variablelist + +[[Effects:] [If `*this` has an associated thread of execution, calls __detach__. Destroys `*this`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:joinable Member function `joinable()`] + + bool joinable() const; + +[variablelist + +[[Returns:] [`true` if `*this` refers to a thread of execution, `false` otherwise.]] + +[[Throws:] [Nothing]] + +] + + +[endsect] + +[section:join Member function `join()`] + + void join(); + +[variablelist + +[[Preconditions:] [`this->get_id()!=boost::this_thread::get_id()`]] + +[[Effects:] [If `*this` refers to a thread of execution, waits for that thread of execution to complete.]] + +[[Postconditions:] [If `*this` refers to a thread of execution on entry, that thread of execution has completed. `*this` no longer refers to any thread of execution.]] + +[[Throws:] [__thread_interrupted__ if the current thread of execution is interrupted.]] + +[[Notes:] [`join()` is one of the predefined __interruption_points__.]] + +] + +[endsect] + +[section:timed_join Member function `timed_join()`] + + bool timed_join(const system_time& wait_until); + + template + bool timed_join(TimeDuration const& rel_time); + +[variablelist + +[[Preconditions:] [`this->get_id()!=boost::this_thread::get_id()`]] + +[[Effects:] [If `*this` refers to a thread of execution, waits for that thread of execution to complete, the time `wait_until` has +been reach or the specified duration `rel_time` has elapsed. If `*this` doesn't refer to a thread of execution, returns immediately.]] + +[[Returns:] [`true` if `*this` refers to a thread of execution on entry, and that thread of execution has completed before the call +times out, `false` otherwise.]] + +[[Postconditions:] [If `*this` refers to a thread of execution on entry, and `timed_join` returns `true`, that thread of execution +has completed, and `*this` no longer refers to any thread of execution. If this call to `timed_join` returns `false`, `*this` is +unchanged.]] + +[[Throws:] [__thread_interrupted__ if the current thread of execution is interrupted.]] + +[[Notes:] [`timed_join()` is one of the predefined __interruption_points__.]] + +] + +[endsect] + +[section:detach Member function `detach()`] + + void detach(); + +[variablelist + +[[Effects:] [If `*this` refers to a thread of execution, that thread of execution becomes detached, and longer has an associated __thread__ object.]] + +[[Postconditions:] [`*this` no longer refers to any thread of execution.]] + +[[Throws:] [Nothing]] + +] + +[endsect] + + +[section:get_id Member function `get_id()`] + + thread::id get_id() const; + +[variablelist + +[[Returns:] [If `*this` refers to a thread of execution, an instance of __thread_id__ that represents that thread. Otherwise returns +a default-constructed __thread_id__.]] + +[[Throws:] [Nothing]] + +] + +[endsect] + +[section:interrupt Member function `interrupt()`] + + void interrupt(); + +[variablelist + +[[Effects:] [If `*this` refers to a thread of execution, request that the thread will be interrupted the next time it enters one of +the predefined __interruption_points__ with interruption enabled, or if it is currently __blocked__ in a call to one of the +predefined __interruption_points__ with interruption enabled .]] + +[[Throws:] [Nothing]] + +] + + +[endsect] + +[section:hardware_concurrency Static member function `hardware_concurrency()`] + + unsigned hardware_concurrency(); + +[variablelist + +[[Returns:] [The number of hardware threads available on the current system (e.g. number of CPUs or cores or hyperthreading units), +or 0 if this information is not available.]] + +[[Throws:] [Nothing]] + +] + +[endsect] + +[section:equals `operator==`] + + bool operator==(const thread& other) const; + +[variablelist + +[[Returns:] [`get_id()==other.get_id()`]] + +] + +[endsect] + +[section:not_equals `operator!=`] + + bool operator!=(const thread& other) const; + +[variablelist + +[[Returns:] [`get_id()!=other.get_id()`]] + +] + +[endsect] + +[section:sleep Static member function `sleep()`] + + void sleep(system_time const& abs_time); + +[variablelist + +[[Effects:] [Suspends the current thread until the specified time has been reached.]] + +[[Throws:] [__thread_interrupted__ if the current thread of execution is interrupted.]] + +[[Notes:] [`sleep()` is one of the predefined __interruption_points__.]] + +] + +[endsect] + +[section:yield Static member function `yield()`] + + void yield(); + +[variablelist + +[[Effects:] [See [link thread.thread_management.this_thread.yield `boost::this_thread::yield()`].]] + +] + +[endsect] + + +[section:id Class `boost::thread::id`] + + class thread::id + { + public: + id(); + + bool operator==(const id& y) const; + bool operator!=(const id& y) const; + bool operator<(const id& y) const; + bool operator>(const id& y) const; + bool operator<=(const id& y) const; + bool operator>=(const id& y) const; + + template + friend std::basic_ostream& + operator<<(std::basic_ostream& os, const id& x); + }; + +[section:constructor Default constructor] + + id(); + +[variablelist + +[[Effects:] [Constructs a __thread_id__ instance that represents __not_a_thread__.]] + +[[Throws:] [Nothing]] + +] + +[endsect] + +[section:is_equal `operator==`] + + bool operator==(const id& y) const; + +[variablelist + +[[Returns:] [`true` if `*this` and `y` both represent the same thread of execution, or both represent __not_a_thread__, `false` +otherwise.]] + +[[Throws:] [Nothing]] + +] + +[endsect] + +[section:not_equal `operator!=`] + + bool operator!=(const id& y) const; + +[variablelist + +[[Returns:] [`true` if `*this` and `y` represent the different threads of execution, or one represents a thread of execution, and +the other represent __not_a_thread__, `false` otherwise.]] + +[[Throws:] [Nothing]] + +] + +[endsect] + +[section:less_than `operator<`] + + bool operator<(const id& y) const; + +[variablelist + +[[Returns:] [`true` if `*this!=y` is `true` and the implementation-defined total order of __thread_id__ values places `*this` before +`y`, `false` otherwise.]] + +[[Throws:] [Nothing]] + +[[Note:] [A __thread_id__ instance representing __not_a_thread__ will always compare less than an instance representing a thread of +execution.]] + +] + +[endsect] + + +[section:greater_than `operator>`] + + bool operator>(const id& y) const; + +[variablelist + +[[Returns:] [`y<*this`]] + +[[Throws:] [Nothing]] + +] + +[endsect] + +[section:less_than_or_equal `operator>=`] + + bool operator<=(const id& y) const; + +[variablelist + +[[Returns:] [`!(y<*this)`]] + +[[Throws:] [Nothing]] + +] + +[endsect] + +[section:greater_than_or_equal `operator>=`] + + bool operator>=(const id& y) const; + +[variablelist + +[[Returns:] [`!(*this + friend std::basic_ostream& + operator<<(std::basic_ostream& os, const id& x); + +[variablelist + +[[Effects:] [Writes a representation of the __thread_id__ instance `x` to the stream `os`, such that the representation of two +instances of __thread_id__ `a` and `b` is the same if `a==b`, and different if `a!=b`.]] + +[[Returns:] [`os`]] + +] + +[endsect] + + +[endsect] + +[endsect] + +[section:this_thread Namespace `this_thread`] + +[section:get_id Non-member function `get_id()`] + + namespace this_thread + { + thread::id get_id(); + } + +[variablelist + +[[Returns:] [An instance of __thread_id__ that represents that currently executing thread.]] + +[[Throws:] [__thread_resource_error__ if an error occurs.]] + +] + +[endsect] + +[section:interruption_point Non-member function `interruption_point()`] + + namespace this_thread + { + void interruption_point(); + } + +[variablelist + +[[Effects:] [Check to see if the current thread has been interrupted.]] + +[[Throws:] [__thread_interrupted__ if __interruption_enabled__ and __interruption_requested__ both return `true`.]] + +] + +[endsect] + +[section:interruption_requested Non-member function `interruption_requested()`] + + namespace this_thread + { + bool interruption_requested(); + } + +[variablelist + +[[Returns:] [`true` if interruption has been requested for the current thread, `false` otherwise.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:interruption_enabled Non-member function `interruption_enabled()`] + + namespace this_thread + { + bool interruption_enabled(); + } + +[variablelist + +[[Returns:] [`true` if interruption has been enabled for the current thread, `false` otherwise.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:sleep Non-member function `sleep()`] + + namespace this_thread + { + template + void sleep(TimeDuration const& rel_time); + } + +[variablelist + +[[Effects:] [Suspends the current thread until the specified time has elapsed.]] + +[[Throws:] [__thread_interrupted__ if the current thread of execution is interrupted.]] + +[[Notes:] [`sleep()` is one of the predefined __interruption_points__.]] + +] + +[endsect] + +[section:yield Non-member function `yield()`] + + namespace this_thread + { + void yield(); + } + +[variablelist + +[[Effects:] [Gives up the remainder of the current thread's time slice, to allow other threads to run.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:disable_interruption Class `disable_interruption`] + + namespace this_thread + { + class disable_interruption + { + public: + disable_interruption(); + ~disable_interruption(); + }; + } + +`boost::this_thread::disable_interruption` disables interruption for the current thread on construction, and restores the prior +interruption state on destruction. Instances of `disable_interruption` cannot be copied or moved. + +[section:constructor Constructor] + + disable_interruption(); + +[variablelist + +[[Effects:] [Stores the current state of __interruption_enabled__ and disables interruption for the current thread.]] + +[[Postconditions:] [__interruption_enabled__ returns `false` for the current thread.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:destructor Destructor] + + ~disable_interruption(); + +[variablelist + +[[Preconditions:] [Must be called from the same thread from which `*this` was constructed.]] + +[[Effects:] [Restores the current state of __interruption_enabled__ for the current thread to that prior to the construction of `*this`.]] + +[[Postconditions:] [__interruption_enabled__ for the current thread returns the value stored in the constructor of `*this`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[endsect] + +[section:restore_interruption Class `restore_interruption`] + + namespace this_thread + { + class restore_interruption + { + public: + explicit restore_interruption(disable_interruption& disabler); + ~restore_interruption(); + }; + } + +On construction of an instance of `boost::this_thread::restore_interruption`, the interruption state for the current thread is +restored to the interruption state stored by the constructor of the supplied instance of __disable_interruption__. When the instance +is destroyed, interruption is again disabled. Instances of `restore_interruption` cannot be copied or moved. + +[section:constructor Constructor] + + explicit restore_interruption(disable_interruption& disabler); + +[variablelist + +[[Preconditions:] [Must be called from the same thread from which `disabler` was constructed.]] + +[[Effects:] [Restores the current state of __interruption_enabled__ for the current thread to that prior to the construction of `disabler`.]] + +[[Postconditions:] [__interruption_enabled__ for the current thread returns the value stored in the constructor of `disabler`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:destructor Destructor] + + ~restore_interruption(); + +[variablelist + +[[Preconditions:] [Must be called from the same thread from which `*this` was constructed.]] + +[[Effects:] [Disables interruption for the current thread.]] + +[[Postconditions:] [__interruption_enabled__ for the current thread returns `false`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[endsect] + +[section:atthreadexit Non-member function template `at_thread_exit()`] + + template + void at_thread_exit(Callable func); + +[variablelist + +[[Effects:] [A copy of `func` is taken and stored to in thread-specific storage. This copy is invoked when the current thread exits.]] + +[[Postconditions:] [A copy of `func` has been saved for invocation on thread exit.]] + +[[Throws:] [`std::bad_alloc` if memory cannot be allocated for the copy of the function, __thread_resource_error__ if any other +error occurs within the thread library. Any exception thrown whilst copying `func` into internal storage.]] + +] + +[endsect] + +[endsect] + +[section:threadgroup Class `thread_group`] + + class thread_group: + private noncopyable + { + public: + thread_group(); + ~thread_group(); + + thread* create_thread(const function0& threadfunc); + void add_thread(thread* thrd); + void remove_thread(thread* thrd); + void join_all(); + void interrupt_all(); + int size() const; + }; + +`thread_group` provides for a collection of threads that are related in some fashion. New threads can be added to the group with +`add_thread` and `create_thread` member functions. `thread_group` is not copyable or movable. + +[section:constructor Constructor] + + thread_group(); + +[variablelist + +[[Effects:] [Create a new thread group with no threads.]] + +] + +[endsect] + +[section:destructor Destructor] + + ~thread_group(); + +[variablelist + +[[Effects:] [Destroy `*this` and `delete` all __thread__ objects in the group.]] + +] + +[endsect] + +[section:create_thread Member function `create_thread()`] + + thread* create_thread(const function0& threadfunc); + +[variablelist + +[[Effects:] [Create a new __thread__ object as-if by `new thread(threadfunc)` and add it to the group.]] + +[[Postcondition:] [`this->size()` is increased by one, the new thread is running.]] + +[[Returns:] [A pointer to the new __thread__ object.]] + +] + +[endsect] + +[section:add_thread Member function `add_thread()`] + + void add_thread(thread* thrd); + +[variablelist + +[[Precondition:] [The expression `delete thrd` is well-formed and will not result in undefined behaviour.]] + +[[Effects:] [Take ownership of the __thread__ object pointed to by `thrd` and add it to the group.]] + +[[Postcondition:] [`this->size()` is increased by one.]] + +] + +[endsect] + +[section:remove_thread Member function `remove_thread()`] + + void remove_thread(thread* thrd); + +[variablelist + +[[Effects:] [If `thrd` is a member of the group, remove it without calling `delete`.]] + +[[Postcondition:] [If `thrd` was a member of the group, `this->size()` is decreased by one.]] + +] + +[endsect] + +[section:join_all Member function `join_all()`] + + void join_all(); + +[variablelist + +[[Effects:] [Call `join()` on each __thread__ object in the group.]] + +[[Postcondition:] [Every thread in the group has terminated.]] + +[[Note:] [Since __join__ is one of the predefined __interruption_points__, `join_all()` is also an interruption point.]] + +] + +[endsect] + +[section:interrupt_all Member function `interrupt_all()`] + + void interrupt_all(); + +[variablelist + +[[Effects:] [Call `interrupt()` on each __thread__ object in the group.]] + +] + +[endsect] + +[section:size Member function `size()`] + + int size(); + +[variablelist + +[[Returns:] [The number of threads in the group.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + + +[endsect] + +[endsect] diff --git a/doc/tss-ref.xml b/doc/tss-ref.xml deleted file mode 100644 index faacad1f..00000000 --- a/doc/tss-ref.xml +++ /dev/null @@ -1,206 +0,0 @@ - - - %thread.entities; -]> - -
- - - - The thread_specific_ptr class defines - an interface for using thread specific storage. - - - - Thread specific storage is data associated with - individual threads and is often used to make operations - that rely on global data - thread-safe. - - - Template thread_specific_ptr - stores a pointer to an object obtained on a thread-by-thread - basis and calls a specified cleanup handler on the contained - pointer when the thread terminates. The cleanup handlers are - called in the reverse order of construction of the - thread_specific_ptrs, and for the - initial thread are called by the destructor, providing the - same ordering guarantees as for normal declarations. Each - thread initially stores the null pointer in each - thread_specific_ptr instance. - - The template thread_specific_ptr - is useful in the following cases: - - An interface was originally written assuming - a single thread of control and it is being ported to a - multithreaded environment. - - Each thread of control invokes sequences of - methods that share data that are physically unique - for each thread, but must be logically accessed - through a globally visible access point instead of - being explicitly passed. - - - - - - boost::noncopyable - Exposition only - - - - The expression delete get() is well - formed. - - A thread-specific data key is allocated and visible to - all threads in the process. Upon creation, the value - NULL will be associated with the new key in all - active threads. A cleanup method is registered with the key - that will call delete on the value associated - with the key for a thread when it exits. When a thread exits, - if a key has a registered cleanup method and the thread has a - non-NULL value associated with that key, the value - of the key is set to NULL and then the cleanup - method is called with the previously associated value as its - sole argument. The order in which registered cleanup methods - are called when a thread exits is undefined. If after all the - cleanup methods have been called for all non-NULL - values, there are still some non-NULL values - with associated cleanup handlers the result is undefined - behavior. - - boost::thread_resource_error if - the necessary resources can not be obtained. - - There may be an implementation specific limit to the - number of thread specific storage objects that can be created, - and this limit may be small. - - The most common need for cleanup will be to call - delete on the associated value. If other forms - of cleanup are required the overloaded constructor should be - called instead. - - - - - void (*cleanup)(void*) - - - A thread-specific data key is allocated and visible to - all threads in the process. Upon creation, the value - NULL will be associated with the new key in all - active threads. The cleanup method is registered - with the key and will be called for a thread with the value - associated with the key for that thread when it exits. When a - thread exits, if a key has a registered cleanup method and the - thread has a non-NULL value associated with that - key, the value of the key is set to NULL and then - the cleanup method is called with the previously associated - value as its sole argument. The order in which registered - cleanup methods are called when a thread exits is undefined. - If after all the cleanup methods have been called for all - non-NULL values, there are still some - non-NULL values with associated cleanup handlers - the result is undefined behavior. - - boost::thread_resource_error if - the necessary resources can not be obtained. - - There may be an implementation specific limit to the - number of thread specific storage objects that can be created, - and this limit may be small. - - There is the occasional need to register - specialized cleanup methods, or to register no cleanup method - at all (done by passing NULL to this constructor. - - - - - Deletes the thread-specific data key allocated by the - constructor. The thread-specific data values associated with - the key need not be NULL. It is the responsibility - of the application to perform any cleanup actions for data - associated with the key. - - Does not destroy any data that may be stored in any - thread's thread specific storage. For this reason you should - not destroy a thread_specific_ptr object - until you are certain there are no threads running that have - made use of its thread specific storage. - - Associated data is not cleaned up because registered - cleanup methods need to be run in the thread that allocated the - associated data to be guarranteed to work correctly. There's no - safe way to inject the call into another thread's execution - path, making it impossible to call the cleanup methods safely. - - - - - - T* - - *this holds the null pointer - for the current thread. - - this->get() prior to the call. - - This method provides a mechanism for the user to - relinquish control of the data associated with the - thread-specific key. - - - - void - - - T* - 0 - - - If this->get() != p && - this->get() != NULL then call the - associated cleanup function. - - *this holds the pointer - p for the current thread. - - - - - - T* - - The object stored in thread specific storage for - the current thread for *this. - - Each thread initially returns 0. - - - - T* - - this->get(). - - - - T& - - this->get() != 0 - - this->get(). - - - - -
diff --git a/doc/tss.qbk b/doc/tss.qbk new file mode 100644 index 00000000..b6e1ff69 --- /dev/null +++ b/doc/tss.qbk @@ -0,0 +1,175 @@ +[section Thread Local Storage] + +[heading Synopsis] + +Thread local storage allows multi-threaded applications to have a separate instance of a given data item for each thread. Where a +single-threaded application would use static or global data, this could lead to contention, deadlock or data corruption in a +multi-threaded application. One example is the C `errno` variable, used for storing the error code related to functions from the +Standard C library. It is common practice (and required by POSIX) for compilers that support multi-threaded applications to provide +a separate instance of `errno` for each thread, in order to avoid different threads competing to read or update the value. + +Though compilers often provide this facility in the form of extensions to the declaration syntax (such as `__declspec(thread)` or +`__thread` annotations on `static` or namespace-scope variable declarations), such support is non-portable, and is often limited in +some way, such as only supporting POD types. + +[heading Portable thread-local storage with `boost::thread_specific_ptr`] + +`boost::thread_specific_ptr` provides a portable mechanism for thread-local storage that works on all compilers supported by +__boost_thread__. Each instance of `boost::thread_specific_ptr` represents a pointer to an object (such as `errno`) where each +thread must have a distinct value. The value for the current thread can be obtained using the `get()` member function, or by using +the `*` and `->` pointer deference operators. Initially the pointer has a value of `NULL` in each thread, but the value for the +current thread can be set using the `reset()` member function. + +If the value of the pointer for the current thread is changed using `reset()`, then the previous value is destroyed by calling the +cleanup routine. Alternatively, the stored value can be reset to `NULL` and the prior value returned by calling the `release()` +member function, allowing the application to take back responsibility for destroying the object. + +[heading Cleanup at thread exit] + +When a thread exits, the objects associated with each `boost::thread_specific_ptr` instance are destroyed. By default, the object +pointed to by a pointer `p` is destroyed by invoking `delete p`, but this can be overridden for a specific instance of +`boost::thread_specific_ptr` by providing a cleanup routine to the constructor. In this case, the object is destroyed by invoking +`func(p)` where `func` is the cleanup routine supplied to the constructor. The cleanup functions are called in an unspecified +order. If a cleanup routine sets the value of associated with an instance of `boost::thread_specific_ptr` that has already been +cleaned up, that value is added to the cleanup list. Cleanup finishes when there are no outstanding instances of +`boost::thread_specific_ptr` with values. + + +[section:thread_specific_ptr Class `thread_specific_ptr`] + + template + class thread_specific_ptr + { + public: + thread_specific_ptr(); + explicit thread_specific_ptr(void (*cleanup_function)(T*)); + ~thread_specific_ptr(); + + T* get() const; + T* operator->() const; + T& operator*() const; + + T* release(); + void reset(T* new_value=0); + }; + +[section:default_constructor `thread_specific_ptr();`] + +[variablelist + +[[Requires:] [`delete this->get()` is well-formed.]] + +[[Effects:] [Construct a `thread_specific_ptr` object for storing a pointer to an object of type `T` specific to each thread. The +default `delete`-based cleanup function will be used to destroy any thread-local objects when `reset()` is called, or the thread +exits.]] + +[[Throws:] [`boost::thread_resource_error` if an error occurs.]] + +] + +[endsect] + +[section:constructor_with_custom_cleanup `explicit thread_specific_ptr(void (*cleanup_function)(T*));`] + +[variablelist + +[[Requires:] [`cleanup_function(this->get())` does not throw any exceptions.]] + +[[Effects:] [Construct a `thread_specific_ptr` object for storing a pointer to an object of type `T` specific to each thread. The +supplied `cleanup_function` will be used to destroy any thread-local objects when `reset()` is called, or the thread exits.]] + +[[Throws:] [`boost::thread_resource_error` if an error occurs.]] + +] + +[endsect] + +[section:destructor `~thread_specific_ptr();`] + +[variablelist + +[[Effects:] [Calls `this->reset()` to clean up the associated value for the current thread, and destroys `*this`.]] + +[[Throws:] [Nothing.]] + +] + +[note Care needs to be taken to ensure that any threads still running after an instance of `boost::thread_specific_ptr` has been +destroyed do not call any member functions on that instance.] + +[endsect] + +[section:get `T* get() const;`] + +[variablelist + +[[Returns:] [The pointer associated with the current thread.]] + +[[Throws:] [Nothing.]] + +] + +[note The initial value associated with an instance of `boost::thread_specific_ptr` is `NULL` for each thread.] + +[endsect] + +[section:operator_arrow `T* operator->() const;`] + +[variablelist + +[[Returns:] [`this->get()`]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:operator_star `T& operator*() const;`] + +[variablelist + +[[Requires:] [`this->get` is not `NULL`.]] + +[[Returns:] [`*(this->get())`]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:reset `void reset(T* new_value=0);`] + +[variablelist + +[[Effects:] [If `this->get()!=new_value` and `this->get()` is non-`NULL`, invoke `delete this->get()` or +`cleanup_function(this->get())` as appropriate. Store `new_value` as the pointer associated with the current thread.]] + +[[Postcondition:] [`this->get()==new_value`]] + +[[Throws:] [`boost::thread_resource_error` if an error occurs.]] + +] + +[endsect] + +[section:release `T* release();`] + +[variablelist + +[[Effects:] [Return `this->get()` and store `NULL` as the pointer associated with the current thread without invoking the cleanup +function.]] + +[[Postcondition:] [`this->get()==0`]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + + +[endsect] + +[endsect] diff --git a/doc/xtime-ref.xml b/doc/xtime-ref.xml deleted file mode 100644 index 566a28d1..00000000 --- a/doc/xtime-ref.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - %thread.entities; -]> - -
- - - - - - Specifies the clock type to use when creating - an object of type xtime. - - - - The only clock type supported by &Boost.Thread; is - TIME_UTC. The epoch for TIME_UTC - is 1970-01-01 00:00:00. - - - - - - An object of type xtime - defines a time that is used to perform high-resolution time operations. - This is a temporary solution that will be replaced by a more robust time - library once available in Boost. - - - - The xtime type is used to represent a point on - some time scale or a duration in time. This type may be proposed for the C standard by - Markus Kuhn. &Boost.Thread; provides only a very minimal implementation of this - proposal; it is expected that a full implementation (or some other time - library) will be provided in Boost as a separate library, at which time &Boost.Thread; - will deprecate its own implementation. - - Note that the resolution is - implementation specific. For many implementations the best resolution - of time is far more than one nanosecond, and even when the resolution - is reasonably good, the latency of a call to xtime_get() - may be significant. For maximum portability, avoid durations of less than - one second. - - - - - int - - - xtime* - - - - int - - - - xtp represents the current point in - time as a duration since the epoch specified by - clock_type. - - - - clock_type if successful, otherwise 0. - - - - - - platform-specific-type - - - -
From 76e53c7bc519fca423d5977d2d68438193332bb0 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 19 Mar 2008 17:25:13 +0000 Subject: [PATCH 100/214] Removed some warnings: those from issue #1640 and others [SVN r43730] --- include/boost/thread/detail/move.hpp | 2 + include/boost/thread/pthread/shared_mutex.hpp | 49 ++++++++ include/boost/thread/pthread/thread.hpp | 17 ++- include/boost/thread/pthread/thread_data.hpp | 1 + .../boost/thread/win32/basic_timed_mutex.hpp | 22 ++++ .../boost/thread/win32/condition_variable.hpp | 13 +- include/boost/thread/win32/once.hpp | 2 + include/boost/thread/win32/shared_mutex.hpp | 114 +++++++++++++++++- include/boost/thread/win32/thread.hpp | 19 ++- src/pthread/thread.cpp | 2 +- src/win32/tss_pe.cpp | 9 +- test/condition_test_common.hpp | 2 + test/shared_mutex_locking_thread.hpp | 8 ++ test/test_condition.cpp | 3 + test/test_shared_mutex_part_2.cpp | 4 + test/test_tss.cpp | 2 +- test/util.inl | 5 +- 17 files changed, 261 insertions(+), 13 deletions(-) diff --git a/include/boost/thread/detail/move.hpp b/include/boost/thread/detail/move.hpp index 011ae4ea..99beb5bb 100644 --- a/include/boost/thread/detail/move.hpp +++ b/include/boost/thread/detail/move.hpp @@ -22,6 +22,8 @@ namespace boost { return &t; } + private: + void operator=(thread_move_t&); }; } diff --git a/include/boost/thread/pthread/shared_mutex.hpp b/include/boost/thread/pthread/shared_mutex.hpp index 4cc095a2..4465f559 100644 --- a/include/boost/thread/pthread/shared_mutex.hpp +++ b/include/boost/thread/pthread/shared_mutex.hpp @@ -57,7 +57,14 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true) +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif { if(!state.exclusive && !state.exclusive_waiting_blocked) { @@ -89,7 +96,14 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true) +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif { if(!state.exclusive && !state.exclusive_waiting_blocked) { @@ -130,7 +144,14 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true) +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif { if(state.shared_count || state.exclusive) { @@ -150,7 +171,14 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true) +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif { if(state.shared_count || state.exclusive) { @@ -196,7 +224,14 @@ namespace boost { boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true) +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif { if(!state.exclusive && !state.exclusive_waiting_blocked && !state.upgrade) { @@ -213,7 +248,14 @@ namespace boost { boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true) +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif { if(!state.exclusive && !state.exclusive_waiting_blocked && !state.upgrade) { @@ -262,7 +304,14 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); --state.shared_count; +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true) +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif { if(!state.shared_count) { diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index f539d568..6a2ac3d9 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -22,6 +22,12 @@ #include #include "thread_data.hpp" #include +#include + +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4251) +#endif namespace boost { @@ -319,7 +325,7 @@ namespace boost } } - class BOOST_THREAD_DECL thread_group : private noncopyable + class BOOST_THREAD_DECL thread_group { public: thread_group(); @@ -330,13 +336,20 @@ namespace boost void remove_thread(thread* thrd); void join_all(); void interrupt_all(); - int size() const; + size_t size() const; private: + thread_group(thread_group&); + void operator=(thread_group&); + std::list m_threads; mutex m_mutex; }; } // namespace boost +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif + #endif diff --git a/include/boost/thread/pthread/thread_data.hpp b/include/boost/thread/pthread/thread_data.hpp index 234ba882..52ca40e5 100644 --- a/include/boost/thread/pthread/thread_data.hpp +++ b/include/boost/thread/pthread/thread_data.hpp @@ -72,6 +72,7 @@ namespace boost } } + void operator=(interruption_checker&); public: explicit interruption_checker(pthread_cond_t* cond): thread_info(detail::get_current_thread_data()) diff --git a/include/boost/thread/win32/basic_timed_mutex.hpp b/include/boost/thread/win32/basic_timed_mutex.hpp index 8ee199df..207c0920 100644 --- a/include/boost/thread/win32/basic_timed_mutex.hpp +++ b/include/boost/thread/win32/basic_timed_mutex.hpp @@ -33,7 +33,14 @@ namespace boost void destroy() { +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4312) +#endif void* const old_event=BOOST_INTERLOCKED_EXCHANGE_POINTER(&event,0); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif if(old_event) { win32::CloseHandle(old_event); @@ -64,7 +71,14 @@ namespace boost bool timed_lock(::boost::system_time const& wait_until) { long old_count=active_count; +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true) +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif { long const current_count=BOOST_INTERLOCKED_COMPARE_EXCHANGE(&active_count,(old_count+1)|lock_flag_value,old_count); if(current_count==old_count) @@ -139,7 +153,15 @@ namespace boost if(!current_event) { void* const new_event=win32::create_anonymous_event(win32::auto_reset_event,win32::event_initially_reset); +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4311) +#pragma warning(disable:4312) +#endif void* const old_event=BOOST_INTERLOCKED_COMPARE_EXCHANGE_POINTER(&event,new_event,0); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif if(old_event!=0) { win32::CloseHandle(new_event); diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index bfd1f1ff..6bd4efd6 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -55,10 +55,17 @@ namespace boost } else { - active_generation_count=(last_active_entry-generations)+1; + active_generation_count=unsigned(last_active_entry-generations)+1; } - + +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4996) +#endif std::copy_backward(generations,generations+active_generation_count-1,generations+active_generation_count); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif generations[0]=list_entry(); } @@ -108,6 +115,8 @@ namespace boost } } + private: + void operator=(relocker&); }; diff --git a/include/boost/thread/win32/once.hpp b/include/boost/thread/win32/once.hpp index b11be97a..719eaea5 100644 --- a/include/boost/thread/win32/once.hpp +++ b/include/boost/thread/win32/once.hpp @@ -46,6 +46,8 @@ namespace boost { BOOST_VERIFY(win32::ReleaseMutex(mutex_handle)!=0); } + private: + void operator=(win32_mutex_scoped_lock&); }; #ifdef BOOST_NO_ANSI_APIS diff --git a/include/boost/thread/win32/shared_mutex.hpp b/include/boost/thread/win32/shared_mutex.hpp index 3b322b1f..1c9fd1be 100644 --- a/include/boost/thread/win32/shared_mutex.hpp +++ b/include/boost/thread/win32/shared_mutex.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_THREAD_WIN32_SHARED_MUTEX_HPP #define BOOST_THREAD_WIN32_SHARED_MUTEX_HPP -// (C) Copyright 2006-7 Anthony Williams +// (C) Copyright 2006-8 Anthony Williams // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -104,7 +104,14 @@ namespace boost } old_state=current_state; } +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif return !(old_state.exclusive| old_state.exclusive_waiting_blocked); } @@ -115,7 +122,14 @@ namespace boost bool timed_lock_shared(boost::system_time const& wait_until) { +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true) +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif { state_data old_state=state; do @@ -137,7 +151,14 @@ namespace boost } old_state=current_state; } +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif if(!(old_state.exclusive| old_state.exclusive_waiting_blocked)) { @@ -169,7 +190,14 @@ namespace boost } old_state=current_state; } +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif if(!(old_state.exclusive| old_state.exclusive_waiting_blocked)) { @@ -226,7 +254,14 @@ namespace boost } old_state=current_state; } +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif } void lock() @@ -236,7 +271,14 @@ namespace boost bool timed_lock(boost::system_time const& wait_until) { +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true) +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif { state_data old_state=state; @@ -260,7 +302,14 @@ namespace boost } old_state=current_state; } +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif if(!old_state.shared_count && !old_state.exclusive) { @@ -291,7 +340,14 @@ namespace boost } old_state=current_state; } +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif if(!old_state.shared_count && !old_state.exclusive) { return true; @@ -323,13 +379,27 @@ namespace boost } old_state=current_state; } +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif release_waiters(old_state); } void lock_upgrade() { +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true) +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif { state_data old_state=state; do @@ -352,7 +422,14 @@ namespace boost } old_state=current_state; } +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif if(!(old_state.exclusive|| old_state.exclusive_waiting_blocked|| old_state.upgrade)) { @@ -393,7 +470,14 @@ namespace boost } old_state=current_state; } +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif } void unlock_upgrade_and_lock() @@ -421,7 +505,14 @@ namespace boost } old_state=current_state; } +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif } void unlock_and_lock_upgrade() @@ -447,7 +538,14 @@ namespace boost } old_state=current_state; } +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif release_waiters(old_state); } @@ -473,7 +571,14 @@ namespace boost } old_state=current_state; } +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif release_waiters(old_state); } @@ -498,7 +603,14 @@ namespace boost } old_state=current_state; } +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4127) +#endif while(true); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif release_waiters(old_state); } diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 61c2908a..90c73224 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -21,6 +21,13 @@ #include #include #include +#include +#include + +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4251) +#endif namespace boost { @@ -476,9 +483,9 @@ namespace boost thread* create_thread(F threadfunc) { boost::lock_guard guard(m); - thread* const new_thread=new thread(threadfunc); - threads.push_back(new_thread); - return new_thread; + std::auto_ptr new_thread(new thread(threadfunc)); + threads.push_back(new_thread.get()); + return new_thread.release(); } void add_thread(thread* thrd) @@ -524,7 +531,7 @@ namespace boost } } - int size() const + size_t size() const { boost::lock_guard guard(m); return threads.size(); @@ -536,4 +543,8 @@ namespace boost }; } +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif + #endif diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 0add81c6..754e0af5 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -683,7 +683,7 @@ namespace boost } - int thread_group::size() const + size_t thread_group::size() const { return m_threads.size(); } diff --git a/src/win32/tss_pe.cpp b/src/win32/tss_pe.cpp index dec71c3b..bd2e61d8 100644 --- a/src/win32/tss_pe.cpp +++ b/src/win32/tss_pe.cpp @@ -164,6 +164,10 @@ extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata #endif #endif +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4189) +#endif PVAPI on_tls_prepare(void) { //The following line has an important side effect: @@ -201,6 +205,9 @@ extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata return INIRETSUCCESS; } +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif PVAPI on_process_init(void) { @@ -228,7 +235,7 @@ extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata return INIRETSUCCESS; } - void NTAPI on_tls_callback(HINSTANCE h, DWORD dwReason, PVOID pv) + void NTAPI on_tls_callback(HINSTANCE /*h*/, DWORD dwReason, PVOID /*pv*/) { switch (dwReason) { diff --git a/test/condition_test_common.hpp b/test/condition_test_common.hpp index df7d5edc..38f77909 100644 --- a/test/condition_test_common.hpp +++ b/test/condition_test_common.hpp @@ -34,6 +34,8 @@ struct wait_for_flag { return flag; } + private: + void operator=(check_flag&); }; diff --git a/test/shared_mutex_locking_thread.hpp b/test/shared_mutex_locking_thread.hpp index 1fccbddf..0d071bd8 100644 --- a/test/shared_mutex_locking_thread.hpp +++ b/test/shared_mutex_locking_thread.hpp @@ -1,6 +1,12 @@ #ifndef SHARED_MUTEX_LOCKING_THREAD_HPP #define SHARED_MUTEX_LOCKING_THREAD_HPP +// (C) Copyright 2008 Anthony Williams +// +// 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) + #include #include #include @@ -56,6 +62,8 @@ public: --simultaneous_running_count; } } +private: + void operator=(locking_thread&); }; diff --git a/test/test_condition.cpp b/test/test_condition.cpp index 96b1e86e..ea008bfe 100644 --- a/test/test_condition.cpp +++ b/test/test_condition.cpp @@ -43,6 +43,9 @@ struct cond_predicate int& _var; int _val; +private: + void operator=(cond_predicate&); + }; void condition_test_waits(condition_test_data* data) diff --git a/test/test_shared_mutex_part_2.cpp b/test/test_shared_mutex_part_2.cpp index 5666a956..ae7348df 100644 --- a/test/test_shared_mutex_part_2.cpp +++ b/test/test_shared_mutex_part_2.cpp @@ -116,6 +116,8 @@ namespace boost::mutex& unblocked_mutex; unsigned& unblocked_count; + void operator=(simple_writing_thread&); + public: simple_writing_thread(boost::shared_mutex& rwm_, boost::mutex& finish_mutex_, @@ -182,6 +184,8 @@ namespace boost::mutex& unblocked_mutex; unsigned& unblocked_count; + void operator=(simple_reading_thread&); + public: simple_reading_thread(boost::shared_mutex& rwm_, boost::mutex& finish_mutex_, diff --git a/test/test_tss.cpp b/test/test_tss.cpp index 51dcb7ff..bbd78d42 100644 --- a/test/test_tss.cpp +++ b/test/test_tss.cpp @@ -66,7 +66,7 @@ void test_tss_thread() #if defined(BOOST_THREAD_PLATFORM_WIN32) typedef HANDLE native_thread_t; - DWORD WINAPI test_tss_thread_native(LPVOID lpParameter) + DWORD WINAPI test_tss_thread_native(LPVOID /*lpParameter*/) { test_tss_thread(); return 0; diff --git a/test/util.inl b/test/util.inl index 417b255d..5c761d50 100644 --- a/test/util.inl +++ b/test/util.inl @@ -1,6 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf -// Copyright (C) 2007 Anthony Williams +// Copyright (C) 2007-8 Anthony Williams // // 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) @@ -123,6 +123,7 @@ public: private: F func; execution_monitor& monitor; + void operator=(indirect_adapter&); }; template @@ -165,6 +166,8 @@ public: void operator()() const { (param.*func)(); } private: + void operator=(thread_member_binder&); + R (T::*func)(); T& param; }; From 58d5110e61388a4cbf4b08937d73c2bf986fd3b5 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 24 Mar 2008 21:44:36 +0000 Subject: [PATCH 101/214] removed forward declaration for undefined type exclusive_lock [SVN r43847] --- include/boost/thread/detail/tss_hooks.hpp | 2 +- include/boost/thread/locks.hpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/include/boost/thread/detail/tss_hooks.hpp b/include/boost/thread/detail/tss_hooks.hpp index 4b18fe97..99a976f9 100644 --- a/include/boost/thread/detail/tss_hooks.hpp +++ b/include/boost/thread/detail/tss_hooks.hpp @@ -59,7 +59,7 @@ //a method for doing so has been discovered. //May be omitted; may be called multiple times. - extern "C" BOOST_THREAD_DECL void on_thread_exit(void); + extern "C" BOOST_THREAD_DECL void __cdecl on_thread_exit(void); //Function to be called just be fore a thread ends //in an exe or dll that uses Boost.Threads. //Must be called in the context of the thread diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 7d9e8693..c056206b 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -26,9 +26,6 @@ namespace boost template class shared_lock; - template - class exclusive_lock; - template class upgrade_lock; From 6f13227eda411e560c48056400376154b3a9466f Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 7 Apr 2008 13:09:36 +0000 Subject: [PATCH 102/214] Added locked-> owns_lock change to breaking changes [SVN r44089] --- doc/changes.qbk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/changes.qbk b/doc/changes.qbk index 030dbc3a..4a1ccbb3 100644 --- a/doc/changes.qbk +++ b/doc/changes.qbk @@ -44,6 +44,8 @@ functions. but did not lock it on construction. This facility has now been replaced with the constructor that takes a `boost::defer_lock_type` as the second parameter: ``boost::mutex::scoped_lock some_lock(some_mutex,boost::defer_lock);`` +* The `locked()` member function of the `scoped_lock` types has been renamed to __owns_lock_ref__. + * The broken `boost::read_write_mutex` has been replaced with __shared_mutex__. From 8696b610ca507d53b611537f4dccfa4c14c77ab9 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 9 Apr 2008 19:33:06 +0000 Subject: [PATCH 103/214] Added test for trac ticket #1803: condition_variable::notify_one may fail to wake a waiting thread on win32 [SVN r44136] --- test/test_condition_notify_one.cpp | 42 ++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/test/test_condition_notify_one.cpp b/test/test_condition_notify_one.cpp index 45447bd4..a5d82bec 100644 --- a/test/test_condition_notify_one.cpp +++ b/test/test_condition_notify_one.cpp @@ -92,6 +92,47 @@ void do_test_condition_notify_one_wakes_from_relative_timed_wait_with_predicate( BOOST_CHECK(data.woken); } +namespace +{ + boost::mutex multiple_wake_mutex; + boost::condition_variable multiple_wake_cond; + unsigned multiple_wake_count=0; + + void wait_for_condvar_and_increase_count() + { + boost::mutex::scoped_lock lk(multiple_wake_mutex); + multiple_wake_cond.wait(lk); + ++multiple_wake_count; + } + +} + + +void do_test_multiple_notify_one_calls_wakes_multiple_threads() +{ + boost::thread thread1(wait_for_condvar_and_increase_count); + boost::thread thread2(wait_for_condvar_and_increase_count); + + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + multiple_wake_cond.notify_one(); + + boost::thread thread3(wait_for_condvar_and_increase_count); + + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + multiple_wake_cond.notify_one(); + multiple_wake_cond.notify_one(); + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + + { + boost::mutex::scoped_lock lk(multiple_wake_mutex); + BOOST_CHECK(multiple_wake_count==3); + } + + thread1.join(); + thread2.join(); + thread3.join(); +} + void test_condition_notify_one() { timed_test(&do_test_condition_notify_one_wakes_from_wait, timeout_seconds, execution_monitor::use_mutex); @@ -99,6 +140,7 @@ void test_condition_notify_one() timed_test(&do_test_condition_notify_one_wakes_from_timed_wait, timeout_seconds, execution_monitor::use_mutex); timed_test(&do_test_condition_notify_one_wakes_from_timed_wait_with_predicate, timeout_seconds, execution_monitor::use_mutex); timed_test(&do_test_condition_notify_one_wakes_from_relative_timed_wait_with_predicate, timeout_seconds, execution_monitor::use_mutex); + timed_test(&do_test_multiple_notify_one_calls_wakes_multiple_threads, timeout_seconds, execution_monitor::use_mutex); } From 86f9480da4c8fbdd53ddcd14cf7ed16cb9c0e18d Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 10 Apr 2008 13:14:43 +0000 Subject: [PATCH 104/214] fix for notify problem in trac ticket #1803 [SVN r44146] --- .../boost/thread/win32/condition_variable.hpp | 64 +++++++++++++++---- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index 6bd4efd6..01ba1703 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -34,6 +34,12 @@ namespace boost list_entry(): semaphore(0),count(0),notified(0) {} + + void release(unsigned count=1) + { + detail::win32::ReleaseSemaphore(semaphore,count,0); + } + }; BOOST_STATIC_CONSTANT(unsigned,generation_count=3); @@ -77,7 +83,7 @@ namespace boost { detail::win32::ReleaseSemaphore(wake_sem,count_to_wake,0); } - detail::win32::ReleaseSemaphore(entry.semaphore,count_to_wake,0); + entry.release(count_to_wake); entry.count=0; dispose_entry(entry); } @@ -124,6 +130,7 @@ namespace boost void start_wait_loop_first_time(relocker& locker, detail::win32::handle_manager& local_wake_sem) { + detail::interlocked_write_release(&total_count,total_count+1); locker.unlock(); if(!wake_sem) { @@ -141,6 +148,15 @@ namespace boost active_generation_count=1; } } + + void ensure_generation_present() + { + if(!generations[0].semaphore) + { + generations[0].semaphore=detail::win32::create_anonymous_semaphore(0,LONG_MAX); + BOOST_ASSERT(generations[0].semaphore); + } + } template void start_wait_loop(relocker& locker, @@ -148,16 +164,11 @@ namespace boost detail::win32::handle_manager& sem) { boost::mutex::scoped_lock internal_lock(internal_mutex); - detail::interlocked_write_release(&total_count,total_count+1); if(!local_wake_sem) { start_wait_loop_first_time(locker,local_wake_sem); } - if(!generations[0].semaphore) - { - generations[0].semaphore=detail::win32::create_anonymous_semaphore(0,LONG_MAX); - BOOST_ASSERT(generations[0].semaphore); - } + ensure_generation_present(); ++generations[0].count; sem=detail::win32::duplicate_handle(generations[0].semaphore); } @@ -222,15 +233,23 @@ namespace boost if(detail::interlocked_read_acquire(&total_count)) { boost::mutex::scoped_lock internal_lock(internal_mutex); + if(!total_count) + { + return; + } detail::win32::ReleaseSemaphore(wake_sem,1,0); + detail::interlocked_write_release(&total_count,total_count-1); + + unsigned waiting_count=0; + for(unsigned generation=active_generation_count;generation!=0;--generation) { list_entry& entry=generations[generation-1]; + waiting_count+=entry.count; if(entry.count) { - detail::interlocked_write_release(&total_count,total_count-1); entry.notified=true; - detail::win32::ReleaseSemaphore(entry.semaphore,1,0); + entry.release(); if(!--entry.count) { dispose_entry(entry); @@ -241,6 +260,11 @@ namespace boost } } } + if(waiting_count<=total_count) + { + ensure_generation_present(); + generations[0].release(); + } } } @@ -265,9 +289,18 @@ namespace boost } class condition_variable: - public detail::basic_condition_variable + private detail::basic_condition_variable { + private: + condition_variable(condition_variable&); + void operator=(condition_variable&); public: + condition_variable() + {} + + using detail::basic_condition_variable::notify_one; + using detail::basic_condition_variable::notify_all; + void wait(unique_lock& m) { do_wait(m,detail::timeout::sentinel()); @@ -313,9 +346,18 @@ namespace boost }; class condition_variable_any: - public detail::basic_condition_variable + private detail::basic_condition_variable { + private: + condition_variable_any(condition_variable_any&); + void operator=(condition_variable_any&); public: + condition_variable_any() + {} + + using detail::basic_condition_variable::notify_one; + using detail::basic_condition_variable::notify_all; + template void wait(lock_type& m) { From 343d0497725dfa85042f61ad212f36572fb5ff2a Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 10 Apr 2008 13:27:44 +0000 Subject: [PATCH 105/214] fix for trac ticket #1804 [SVN r44147] --- include/boost/thread/pthread/mutex.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index 89a2bb94..3044a25b 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -136,7 +136,7 @@ namespace boost { struct timespec const timeout=detail::get_timespec(abs_time); int const res=pthread_mutex_timedlock(&m,&timeout); - BOOST_ASSERT(!res || res==EBUSY); + BOOST_ASSERT(!res || res==ETIMEDOUT); return !res; } #else From e9fb470b06225df3684346c457702418614e79ac Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 10 Apr 2008 13:35:07 +0000 Subject: [PATCH 106/214] Added native_handle to thread on posix platforms [SVN r44148] --- include/boost/thread/pthread/thread.hpp | 3 +++ src/pthread/thread.cpp | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index 6a2ac3d9..813ced09 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -214,6 +214,9 @@ namespace boost static void sleep(const system_time& xt); static void yield(); + typedef pthread_t native_handle_type; + native_handle_type native_handle(); + // extensions void interrupt(); bool interruption_requested() const; diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 754e0af5..3cd6a86a 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -472,6 +472,21 @@ namespace boost return false; } } + + thread::native_handle_type thread::native_handle() + { + detail::thread_data_ptr const local_thread_info=get_thread_info(); + if(local_thread_info) + { + lock_guard lk(local_thread_info->data_mutex); + return local_thread_info->thread_handle; + } + else + { + return pthread_t(); + } + } + namespace this_thread From c40f47a78a47ef7b455503ffcb5d1c6e29cbb2f6 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 10 Apr 2008 14:07:39 +0000 Subject: [PATCH 107/214] added overloads of timed_lock_shared with a relative timeout to shared_mutex [SVN r44149] --- include/boost/thread/pthread/shared_mutex.hpp | 18 ++++ include/boost/thread/win32/shared_mutex.hpp | 12 +++ test/Jamfile.v2 | 1 + test/shared_mutex_locking_thread.hpp | 31 ++++++ test/test_shared_mutex_part_2.cpp | 62 ------------ test/test_shared_mutex_timed_locks.cpp | 94 +++++++++++++++++++ 6 files changed, 156 insertions(+), 62 deletions(-) create mode 100644 test/test_shared_mutex_timed_locks.cpp diff --git a/include/boost/thread/pthread/shared_mutex.hpp b/include/boost/thread/pthread/shared_mutex.hpp index 4465f559..1648150d 100644 --- a/include/boost/thread/pthread/shared_mutex.hpp +++ b/include/boost/thread/pthread/shared_mutex.hpp @@ -118,6 +118,12 @@ namespace boost } } + template + bool timed_lock_shared(TimeDuration const & relative_time) + { + return timed_lock_shared(get_system_time()+relative_time); + } + void unlock_shared() { boost::mutex::scoped_lock lock(state_change); @@ -196,6 +202,12 @@ namespace boost } } + template + bool timed_lock(TimeDuration const & relative_time) + { + return timed_lock(get_system_time()+relative_time); + } + bool try_lock() { boost::mutex::scoped_lock lock(state_change); @@ -271,6 +283,12 @@ namespace boost } } + template + bool timed_lock_upgrade(TimeDuration const & relative_time) + { + return timed_lock(get_system_time()+relative_time); + } + bool try_lock_upgrade() { boost::mutex::scoped_lock lock(state_change); diff --git a/include/boost/thread/win32/shared_mutex.hpp b/include/boost/thread/win32/shared_mutex.hpp index 1c9fd1be..1862cfdd 100644 --- a/include/boost/thread/win32/shared_mutex.hpp +++ b/include/boost/thread/win32/shared_mutex.hpp @@ -120,6 +120,12 @@ namespace boost BOOST_VERIFY(timed_lock_shared(::boost::detail::get_system_time_sentinel())); } + template + bool timed_lock_shared(TimeDuration const & relative_time) + { + return timed_lock_shared(get_system_time()+relative_time); + } + bool timed_lock_shared(boost::system_time const& wait_until) { #ifdef BOOST_MSVC @@ -269,6 +275,12 @@ namespace boost BOOST_VERIFY(timed_lock(::boost::detail::get_system_time_sentinel())); } + template + bool timed_lock(TimeDuration const & relative_time) + { + return timed_lock(get_system_time()+relative_time); + } + bool timed_lock(boost::system_time const& wait_until) { #ifdef BOOST_MSVC diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 0145cf5b..04dab7af 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -51,6 +51,7 @@ rule thread-run ( sources ) [ thread-run test_barrier.cpp ] [ thread-run test_shared_mutex.cpp ] [ thread-run test_shared_mutex_part_2.cpp ] + [ thread-run test_shared_mutex_timed_locks.cpp ] [ thread-run test_lock_concept.cpp ] ; } diff --git a/test/shared_mutex_locking_thread.hpp b/test/shared_mutex_locking_thread.hpp index 0d071bd8..47e49b35 100644 --- a/test/shared_mutex_locking_thread.hpp +++ b/test/shared_mutex_locking_thread.hpp @@ -66,5 +66,36 @@ private: void operator=(locking_thread&); }; +class simple_writing_thread +{ + boost::shared_mutex& rwm; + boost::mutex& finish_mutex; + boost::mutex& unblocked_mutex; + unsigned& unblocked_count; + + void operator=(simple_writing_thread&); + +public: + simple_writing_thread(boost::shared_mutex& rwm_, + boost::mutex& finish_mutex_, + boost::mutex& unblocked_mutex_, + unsigned& unblocked_count_): + rwm(rwm_),finish_mutex(finish_mutex_), + unblocked_mutex(unblocked_mutex_),unblocked_count(unblocked_count_) + {} + + void operator()() + { + boost::unique_lock lk(rwm); + + { + boost::mutex::scoped_lock ulk(unblocked_mutex); + ++unblocked_count; + } + + boost::mutex::scoped_lock flk(finish_mutex); + } +}; + #endif diff --git a/test/test_shared_mutex_part_2.cpp b/test/test_shared_mutex_part_2.cpp index ae7348df..9f3db593 100644 --- a/test/test_shared_mutex_part_2.cpp +++ b/test/test_shared_mutex_part_2.cpp @@ -107,40 +107,6 @@ void test_can_lock_upgrade_if_currently_locked_shared() CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,reader_count+1); } -namespace -{ - class simple_writing_thread - { - boost::shared_mutex& rwm; - boost::mutex& finish_mutex; - boost::mutex& unblocked_mutex; - unsigned& unblocked_count; - - void operator=(simple_writing_thread&); - - public: - simple_writing_thread(boost::shared_mutex& rwm_, - boost::mutex& finish_mutex_, - boost::mutex& unblocked_mutex_, - unsigned& unblocked_count_): - rwm(rwm_),finish_mutex(finish_mutex_), - unblocked_mutex(unblocked_mutex_),unblocked_count(unblocked_count_) - {} - - void operator()() - { - boost::unique_lock lk(rwm); - - { - boost::mutex::scoped_lock ulk(unblocked_mutex); - ++unblocked_count; - } - - boost::mutex::scoped_lock flk(finish_mutex); - } - }; -} - void test_if_other_thread_has_write_lock_try_lock_shared_returns_false() { @@ -232,33 +198,6 @@ void test_if_other_thread_has_shared_lock_try_lock_shared_returns_true() writer.join(); } -void test_timed_lock_shared_times_out_if_write_lock_held() -{ - boost::shared_mutex rw_mutex; - boost::mutex finish_mutex; - boost::mutex unblocked_mutex; - unsigned unblocked_count=0; - boost::mutex::scoped_lock finish_lock(finish_mutex); - boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); - boost::thread::sleep(delay(1)); - CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); - - boost::system_time const start=boost::get_system_time(); - boost::system_time const timeout=start+boost::posix_time::milliseconds(2000); - boost::posix_time::milliseconds const timeout_resolution(20); - bool const timed_lock_succeeded=rw_mutex.timed_lock_shared(timeout); - BOOST_CHECK((timeout-timeout_resolution)add(BOOST_TEST_CASE(&test_if_other_thread_has_write_lock_try_lock_shared_returns_false)); test->add(BOOST_TEST_CASE(&test_if_no_thread_has_lock_try_lock_shared_returns_true)); test->add(BOOST_TEST_CASE(&test_if_other_thread_has_shared_lock_try_lock_shared_returns_true)); - test->add(BOOST_TEST_CASE(&test_timed_lock_shared_times_out_if_write_lock_held)); return test; } diff --git a/test/test_shared_mutex_timed_locks.cpp b/test/test_shared_mutex_timed_locks.cpp new file mode 100644 index 00000000..f4083023 --- /dev/null +++ b/test/test_shared_mutex_timed_locks.cpp @@ -0,0 +1,94 @@ +// (C) Copyright 2006-7 Anthony Williams +// 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) + +#include +#include +#include +#include "util.inl" +#include "shared_mutex_locking_thread.hpp" + +#define CHECK_LOCKED_VALUE_EQUAL(mutex_name,value,expected_value) \ + { \ + boost::mutex::scoped_lock lock(mutex_name); \ + BOOST_CHECK_EQUAL(value,expected_value); \ + } + + +void test_timed_lock_shared_times_out_if_write_lock_held() +{ + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + unsigned unblocked_count=0; + boost::mutex::scoped_lock finish_lock(finish_mutex); + boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); + + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+boost::posix_time::milliseconds(500); + boost::posix_time::milliseconds const timeout_resolution(50); + bool timed_lock_succeeded=rw_mutex.timed_lock_shared(timeout); + BOOST_CHECK((timeout-timeout_resolution)add(BOOST_TEST_CASE(&test_timed_lock_shared_times_out_if_write_lock_held)); + test->add(BOOST_TEST_CASE(&test_timed_lock_shared_succeeds_if_no_lock_held)); + + return test; +} From 8b916d21b1aed5d4d93c07c4c9948e1009f62fda Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 10 Apr 2008 14:15:26 +0000 Subject: [PATCH 108/214] added tests for plain timed_lock on shared_mutex [SVN r44150] --- test/shared_mutex_locking_thread.hpp | 31 ++++++ test/test_shared_mutex_part_2.cpp | 34 ------ test/test_shared_mutex_timed_locks.cpp | 141 +++++++++++++++++++++++++ 3 files changed, 172 insertions(+), 34 deletions(-) diff --git a/test/shared_mutex_locking_thread.hpp b/test/shared_mutex_locking_thread.hpp index 47e49b35..98a415b1 100644 --- a/test/shared_mutex_locking_thread.hpp +++ b/test/shared_mutex_locking_thread.hpp @@ -97,5 +97,36 @@ public: } }; +class simple_reading_thread +{ + boost::shared_mutex& rwm; + boost::mutex& finish_mutex; + boost::mutex& unblocked_mutex; + unsigned& unblocked_count; + + void operator=(simple_reading_thread&); + +public: + simple_reading_thread(boost::shared_mutex& rwm_, + boost::mutex& finish_mutex_, + boost::mutex& unblocked_mutex_, + unsigned& unblocked_count_): + rwm(rwm_),finish_mutex(finish_mutex_), + unblocked_mutex(unblocked_mutex_),unblocked_count(unblocked_count_) + {} + + void operator()() + { + boost::shared_lock lk(rwm); + + { + boost::mutex::scoped_lock ulk(unblocked_mutex); + ++unblocked_count; + } + + boost::mutex::scoped_lock flk(finish_mutex); + } +}; + #endif diff --git a/test/test_shared_mutex_part_2.cpp b/test/test_shared_mutex_part_2.cpp index 9f3db593..2daa8582 100644 --- a/test/test_shared_mutex_part_2.cpp +++ b/test/test_shared_mutex_part_2.cpp @@ -141,40 +141,6 @@ void test_if_no_thread_has_lock_try_lock_shared_returns_true() } } -namespace -{ - class simple_reading_thread - { - boost::shared_mutex& rwm; - boost::mutex& finish_mutex; - boost::mutex& unblocked_mutex; - unsigned& unblocked_count; - - void operator=(simple_reading_thread&); - - public: - simple_reading_thread(boost::shared_mutex& rwm_, - boost::mutex& finish_mutex_, - boost::mutex& unblocked_mutex_, - unsigned& unblocked_count_): - rwm(rwm_),finish_mutex(finish_mutex_), - unblocked_mutex(unblocked_mutex_),unblocked_count(unblocked_count_) - {} - - void operator()() - { - boost::shared_lock lk(rwm); - - { - boost::mutex::scoped_lock ulk(unblocked_mutex); - ++unblocked_count; - } - - boost::mutex::scoped_lock flk(finish_mutex); - } - }; -} - void test_if_other_thread_has_shared_lock_try_lock_shared_returns_true() { diff --git a/test/test_shared_mutex_timed_locks.cpp b/test/test_shared_mutex_timed_locks.cpp index f4083023..815f7d3e 100644 --- a/test/test_shared_mutex_timed_locks.cpp +++ b/test/test_shared_mutex_timed_locks.cpp @@ -81,6 +81,143 @@ void test_timed_lock_shared_succeeds_if_no_lock_held() } +void test_timed_lock_shared_succeeds_if_read_lock_held() +{ + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + unsigned unblocked_count=0; + boost::mutex::scoped_lock finish_lock(finish_mutex); + boost::thread reader(simple_reading_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); + + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+boost::posix_time::milliseconds(500); + boost::posix_time::milliseconds const timeout_resolution(50); + bool timed_lock_succeeded=rw_mutex.timed_lock_shared(timeout); + BOOST_CHECK(boost::get_system_time()add(BOOST_TEST_CASE(&test_timed_lock_shared_times_out_if_write_lock_held)); test->add(BOOST_TEST_CASE(&test_timed_lock_shared_succeeds_if_no_lock_held)); + test->add(BOOST_TEST_CASE(&test_timed_lock_shared_succeeds_if_read_lock_held)); + test->add(BOOST_TEST_CASE(&test_timed_lock_times_out_if_write_lock_held)); + test->add(BOOST_TEST_CASE(&test_timed_lock_times_out_if_read_lock_held)); + test->add(BOOST_TEST_CASE(&test_timed_lock_succeeds_if_no_lock_held)); return test; } From 08dc521daf1a537bafd4a6f433ef7fa985c920f1 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 10 Apr 2008 15:52:01 +0000 Subject: [PATCH 109/214] Added native_handle to condition_variable on pthreads [SVN r44152] --- include/boost/thread/pthread/condition_variable_fwd.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/boost/thread/pthread/condition_variable_fwd.hpp b/include/boost/thread/pthread/condition_variable_fwd.hpp index 1d1fbdf4..37884dd9 100644 --- a/include/boost/thread/pthread/condition_variable_fwd.hpp +++ b/include/boost/thread/pthread/condition_variable_fwd.hpp @@ -58,6 +58,12 @@ namespace boost return timed_wait(m,get_system_time()+wait_duration,pred); } + typedef pthread_cond_t* native_handle_type; + native_handle_type native_handle() + { + return &cond; + } + void notify_one(); void notify_all(); }; From a3695bd4a0eb20390d60633d27d3eb7fffa51263 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 10 Apr 2008 18:34:42 +0000 Subject: [PATCH 110/214] Updated thread.hpp as catch-all header [SVN r44153] --- include/boost/thread.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/boost/thread.hpp b/include/boost/thread.hpp index 625e69ec..f0a39c3b 100644 --- a/include/boost/thread.hpp +++ b/include/boost/thread.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf +// (C) Copyright 2008 Anthony Williams // // 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) @@ -10,12 +11,15 @@ #define BOOST_THREAD_WEK01082003_HPP #include -#include +#include #include #include #include #include #include -#include +#include +#include +#include +#include #endif From 795cc23f3e5cf01d6edb3c14dda129552b61a929 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 11 Apr 2008 08:52:09 +0000 Subject: [PATCH 111/214] Added test and fix for win32 condition_variable broadcast bug similar to #1803 [SVN r44168] --- .../boost/thread/win32/condition_variable.hpp | 38 ++++++++++------- test/test_condition_notify_all.cpp | 42 +++++++++++++++++++ 2 files changed, 66 insertions(+), 14 deletions(-) diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index 01ba1703..e606258d 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -35,9 +35,9 @@ namespace boost semaphore(0),count(0),notified(0) {} - void release(unsigned count=1) + void release(unsigned count_to_release=1) { - detail::win32::ReleaseSemaphore(semaphore,count,0); + detail::win32::ReleaseSemaphore(semaphore,count_to_release,0); } }; @@ -47,6 +47,13 @@ namespace boost list_entry generations[generation_count]; detail::win32::handle wake_sem; + void wake_waiters(long count_to_wake) + { + detail::interlocked_write_release(&total_count,total_count-count_to_wake); + detail::win32::ReleaseSemaphore(wake_sem,count_to_wake,0); + } + + static bool no_waiters(list_entry const& entry) { return entry.count==0; @@ -57,7 +64,7 @@ namespace boost list_entry* const last_active_entry=std::remove_if(generations,generations+generation_count,no_waiters); if(last_active_entry==generations+generation_count) { - broadcast_entry(generations[generation_count-1],false); + broadcast_entry(generations[generation_count-1]); } else { @@ -75,15 +82,9 @@ namespace boost generations[0]=list_entry(); } - void broadcast_entry(list_entry& entry,bool wake) + void broadcast_entry(list_entry& entry) { - long const count_to_wake=entry.count; - detail::interlocked_write_release(&total_count,total_count-count_to_wake); - if(wake) - { - detail::win32::ReleaseSemaphore(wake_sem,count_to_wake,0); - } - entry.release(count_to_wake); + entry.release(entry.count); entry.count=0; dispose_entry(entry); } @@ -237,8 +238,7 @@ namespace boost { return; } - detail::win32::ReleaseSemaphore(wake_sem,1,0); - detail::interlocked_write_release(&total_count,total_count-1); + wake_waiters(1); unsigned waiting_count=0; @@ -262,6 +262,7 @@ namespace boost } if(waiting_count<=total_count) { + shift_generations_down(); ensure_generation_present(); generations[0].release(); } @@ -273,14 +274,23 @@ namespace boost if(detail::interlocked_read_acquire(&total_count)) { boost::mutex::scoped_lock internal_lock(internal_mutex); + long waiting_count=total_count; + + wake_waiters(total_count); for(unsigned generation=active_generation_count;generation!=0;--generation) { list_entry& entry=generations[generation-1]; if(entry.count) { - broadcast_entry(entry,true); + waiting_count-=entry.count; + broadcast_entry(entry); } } + if(waiting_count) + { + ensure_generation_present(); + generations[0].release(waiting_count); + } active_generation_count=0; } } diff --git a/test/test_condition_notify_all.cpp b/test/test_condition_notify_all.cpp index 22074b44..11a983eb 100644 --- a/test/test_condition_notify_all.cpp +++ b/test/test_condition_notify_all.cpp @@ -159,6 +159,47 @@ void do_test_condition_notify_all_wakes_from_relative_timed_wait_with_predicate( } } +namespace +{ + boost::mutex multiple_wake_mutex; + boost::condition_variable multiple_wake_cond; + unsigned multiple_wake_count=0; + + void wait_for_condvar_and_increase_count() + { + boost::mutex::scoped_lock lk(multiple_wake_mutex); + multiple_wake_cond.wait(lk); + ++multiple_wake_count; + } + +} + + +void do_test_notify_all_following_notify_one_wakes_all_threads() +{ + boost::thread thread1(wait_for_condvar_and_increase_count); + boost::thread thread2(wait_for_condvar_and_increase_count); + + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + multiple_wake_cond.notify_one(); + + boost::thread thread3(wait_for_condvar_and_increase_count); + + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + multiple_wake_cond.notify_one(); + multiple_wake_cond.notify_all(); + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + + { + boost::mutex::scoped_lock lk(multiple_wake_mutex); + BOOST_CHECK(multiple_wake_count==3); + } + + thread1.join(); + thread2.join(); + thread3.join(); +} + void test_condition_notify_all() { timed_test(&do_test_condition_notify_all_wakes_from_wait, timeout_seconds); @@ -166,6 +207,7 @@ void test_condition_notify_all() timed_test(&do_test_condition_notify_all_wakes_from_timed_wait, timeout_seconds); timed_test(&do_test_condition_notify_all_wakes_from_timed_wait_with_predicate, timeout_seconds); timed_test(&do_test_condition_notify_all_wakes_from_relative_timed_wait_with_predicate, timeout_seconds); + timed_test(&do_test_notify_all_following_notify_one_wakes_all_threads, timeout_seconds); } From f91986ad0ddf383f67e8a0a0f61c7f90a1336b15 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Sun, 13 Apr 2008 15:50:08 +0000 Subject: [PATCH 112/214] Added extended adopt/defer/try constructors to upgrade_lock [SVN r44370] --- include/boost/thread/locks.hpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index c056206b..741a2a47 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -400,13 +400,16 @@ namespace boost { lock(); } - upgrade_lock(Mutex& m_,bool do_lock): + upgrade_lock(Mutex& m_,adopt_lock_t): + m(&m_),is_locked(true) + {} + upgrade_lock(Mutex& m_,defer_lock_t): + m(&m_),is_locked(false) + {} + upgrade_lock(Mutex& m_,try_to_lock_t): m(&m_),is_locked(false) { - if(do_lock) - { - lock(); - } + try_lock(); } upgrade_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) From 6e1a866b13bcfa0e88bf7adfff59983d3ebad3bf Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 14 Apr 2008 21:04:33 +0000 Subject: [PATCH 113/214] Fix for issue #1657 [SVN r44424] --- example/condition.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/example/condition.cpp b/example/condition.cpp index ee2136b2..096c35d8 100644 --- a/example/condition.cpp +++ b/example/condition.cpp @@ -46,11 +46,16 @@ private: bounded_buffer buf(2); +boost::mutex io_mutex; + void sender() { int n = 0; while (n < 100) { buf.send(n); - std::cout << "sent: " << n << std::endl; + { + boost::mutex::scoped_lock io_lock(io_mutex); + std::cout << "sent: " << n << std::endl; + } ++n; } buf.send(-1); @@ -60,7 +65,10 @@ void receiver() { int n; do { n = buf.receive(); - std::cout << "received: " << n << std::endl; + { + boost::mutex::scoped_lock io_lock(io_mutex); + std::cout << "received: " << n << std::endl; + } } while (n != -1); // -1 indicates end of buffer } From c26a4cf08295250b5c56a8c9c73b4c523042af1e Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 21 Apr 2008 16:20:31 +0000 Subject: [PATCH 114/214] added private copy assignment operator and copy constructor to remove warnings [SVN r44698] --- include/boost/thread/win32/thread.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 90c73224..3b043b84 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -185,6 +185,9 @@ namespace boost { f(); } + private: + void operator=(thread_data&); + thread_data(thread_data&); }; mutable boost::mutex thread_info_mutex; From bc89df04cb86764eaf886acae1565a4cad78b08a Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 21 Apr 2008 16:22:16 +0000 Subject: [PATCH 115/214] Revamped condition variable to try and fix swallowed-notify problems (trac issue #1834) [SVN r44699] --- .../boost/thread/win32/condition_variable.hpp | 236 +++++++----------- 1 file changed, 92 insertions(+), 144 deletions(-) diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index e606258d..bf597682 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -14,6 +14,8 @@ #include #include "interlocked_read.hpp" #include +#include +#include namespace boost { @@ -27,25 +29,43 @@ namespace boost struct list_entry { - detail::win32::handle semaphore; - long count; + detail::win32::handle_manager semaphore; + detail::win32::handle_manager wake_sem; + long waiters; bool notified; + long references; list_entry(): - semaphore(0),count(0),notified(0) + semaphore(detail::win32::create_anonymous_semaphore(0,LONG_MAX)), + wake_sem(0), + waiters(1),notified(false),references(0) {} - void release(unsigned count_to_release=1) + void release(unsigned count_to_release) { + notified=true; detail::win32::ReleaseSemaphore(semaphore,count_to_release,0); } - + + friend void intrusive_ptr_add_ref(list_entry * p) + { + BOOST_INTERLOCKED_INCREMENT(&p->references); + } + + friend void intrusive_ptr_release(list_entry * p) + { + if(!BOOST_INTERLOCKED_DECREMENT(&p->references)) + { + delete p; + } + } }; - BOOST_STATIC_CONSTANT(unsigned,generation_count=3); + typedef boost::intrusive_ptr entry_ptr; + typedef std::vector generation_list; - list_entry generations[generation_count]; - detail::win32::handle wake_sem; + generation_list generations; + detail::win32::handle_manager wake_sem; void wake_waiters(long count_to_wake) { @@ -53,53 +73,6 @@ namespace boost detail::win32::ReleaseSemaphore(wake_sem,count_to_wake,0); } - - static bool no_waiters(list_entry const& entry) - { - return entry.count==0; - } - - void shift_generations_down() - { - list_entry* const last_active_entry=std::remove_if(generations,generations+generation_count,no_waiters); - if(last_active_entry==generations+generation_count) - { - broadcast_entry(generations[generation_count-1]); - } - else - { - active_generation_count=unsigned(last_active_entry-generations)+1; - } - -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4996) -#endif - std::copy_backward(generations,generations+active_generation_count-1,generations+active_generation_count); -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif - generations[0]=list_entry(); - } - - void broadcast_entry(list_entry& entry) - { - entry.release(entry.count); - entry.count=0; - dispose_entry(entry); - } - - - void dispose_entry(list_entry& entry) - { - if(entry.semaphore) - { - BOOST_VERIFY(detail::win32::CloseHandle(entry.semaphore)); - entry.semaphore=0; - } - entry.notified=false; - } - template struct relocker { @@ -123,77 +96,79 @@ namespace boost } private: + relocker(relocker&); void operator=(relocker&); }; - template - void start_wait_loop_first_time(relocker& locker, - detail::win32::handle_manager& local_wake_sem) + entry_ptr get_wait_entry() { - detail::interlocked_write_release(&total_count,total_count+1); - locker.unlock(); + boost::lock_guard internal_lock(internal_mutex); + if(!wake_sem) { wake_sem=detail::win32::create_anonymous_semaphore(0,LONG_MAX); BOOST_ASSERT(wake_sem); } - local_wake_sem=detail::win32::duplicate_handle(wake_sem); - - if(generations[0].notified) - { - shift_generations_down(); - } - else if(!active_generation_count) - { - active_generation_count=1; - } - } - void ensure_generation_present() - { - if(!generations[0].semaphore) + detail::interlocked_write_release(&total_count,total_count+1); + if(generations.empty() || generations.back()->notified) { - generations[0].semaphore=detail::win32::create_anonymous_semaphore(0,LONG_MAX); - BOOST_ASSERT(generations[0].semaphore); + entry_ptr new_entry(new list_entry); + new_entry->wake_sem=wake_sem.duplicate(); + generations.push_back(new_entry); + return new_entry; + } + else + { + BOOST_INTERLOCKED_INCREMENT(&generations.back()->waiters); + return generations.back(); } } - template - void start_wait_loop(relocker& locker, - detail::win32::handle_manager& local_wake_sem, - detail::win32::handle_manager& sem) + struct entry_manager { - boost::mutex::scoped_lock internal_lock(internal_mutex); - if(!local_wake_sem) + entry_ptr const entry; + + entry_manager(entry_ptr const& entry_): + entry(entry_) + {} + + ~entry_manager() { - start_wait_loop_first_time(locker,local_wake_sem); + BOOST_INTERLOCKED_DECREMENT(&entry->waiters); } - ensure_generation_present(); - ++generations[0].count; - sem=detail::win32::duplicate_handle(generations[0].semaphore); - } + + list_entry* operator->() + { + return entry.get(); + } + + private: + void operator=(entry_manager&); + entry_manager(entry_manager&); + }; + protected: template bool do_wait(lock_type& lock,timeout wait_until) { - detail::win32::handle_manager local_wake_sem; - detail::win32::handle_manager sem; - bool woken=false; - relocker locker(lock); - + + entry_manager entry=get_wait_entry(); + + locker.unlock(); + + bool woken=false; while(!woken) { - start_wait_loop(locker,local_wake_sem,sem); - - if(!this_thread::interruptible_wait(sem,wait_until)) + if(!this_thread::interruptible_wait(entry->semaphore,wait_until)) { return false; } - unsigned long const woken_result=detail::win32::WaitForSingleObject(local_wake_sem,0); + unsigned long const woken_result=detail::win32::WaitForSingleObject(entry->wake_sem,0); BOOST_ASSERT(woken_result==detail::win32::timeout || woken_result==0); woken=(woken_result==0); @@ -214,21 +189,19 @@ namespace boost basic_condition_variable(const basic_condition_variable& other); basic_condition_variable& operator=(const basic_condition_variable& other); + + static bool no_waiters(entry_ptr const& entry) + { + return !detail::interlocked_read_acquire(&entry->waiters); + } public: basic_condition_variable(): total_count(0),active_generation_count(0),wake_sem(0) {} ~basic_condition_variable() - { - for(unsigned i=0;irelease(1); } + generations.erase(std::remove_if(generations.begin(),generations.end(),no_waiters),generations.end()); } } @@ -274,24 +228,18 @@ namespace boost if(detail::interlocked_read_acquire(&total_count)) { boost::mutex::scoped_lock internal_lock(internal_mutex); - long waiting_count=total_count; - + if(!total_count) + { + return; + } wake_waiters(total_count); - for(unsigned generation=active_generation_count;generation!=0;--generation) + for(generation_list::iterator it=generations.begin(), + end=generations.end(); + it!=end;++it) { - list_entry& entry=generations[generation-1]; - if(entry.count) - { - waiting_count-=entry.count; - broadcast_entry(entry); - } + (*it)->release(detail::interlocked_read_acquire(&(*it)->waiters)); } - if(waiting_count) - { - ensure_generation_present(); - generations[0].release(waiting_count); - } - active_generation_count=0; + wake_sem=detail::win32::handle(0); } } From de67d2e27e6e21459f08d1229f6ffb99a0a80591 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Sat, 26 Apr 2008 07:34:46 +0000 Subject: [PATCH 116/214] Fixed g++ compile error [SVN r44773] --- include/boost/thread/win32/condition_variable.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index bf597682..c74d3cc4 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -156,7 +156,7 @@ namespace boost { relocker locker(lock); - entry_manager entry=get_wait_entry(); + entry_manager entry(get_wait_entry()); locker.unlock(); From 918b920670053a9b0cd334e2d165a5d2e5674b41 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 28 Apr 2008 09:00:58 +0000 Subject: [PATCH 117/214] Added detail::try_lock_wrapper for use as scoped_try_lock typedefs, to fix issue #1873 [SVN r44838] --- include/boost/thread/detail/move.hpp | 5 ++ include/boost/thread/locks.hpp | 66 +++++++++++++++++++ include/boost/thread/pthread/mutex.hpp | 4 +- .../boost/thread/pthread/recursive_mutex.hpp | 4 +- include/boost/thread/win32/mutex.hpp | 4 +- .../boost/thread/win32/recursive_mutex.hpp | 4 +- test/test_lock_concept.cpp | 61 +++++++++++++++++ 7 files changed, 140 insertions(+), 8 deletions(-) diff --git a/include/boost/thread/detail/move.hpp b/include/boost/thread/detail/move.hpp index 99beb5bb..d4732317 100644 --- a/include/boost/thread/detail/move.hpp +++ b/include/boost/thread/detail/move.hpp @@ -18,6 +18,11 @@ namespace boost t(t_) {} + T& operator*() const + { + return t; + } + T* operator->() const { return &t; diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 741a2a47..c1952948 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -587,6 +587,72 @@ namespace boost } }; + namespace detail + { + template + class try_lock_wrapper: + private unique_lock + { + typedef unique_lock base; + public: + explicit try_lock_wrapper(Mutex& m): + base(m,try_to_lock) + {} + + try_lock_wrapper(Mutex& m_,adopt_lock_t): + base(m_,adopt_lock) + {} + try_lock_wrapper(Mutex& m_,defer_lock_t): + base(m_,defer_lock) + {} + try_lock_wrapper(Mutex& m_,try_to_lock_t): + base(m,try_to_lock) + {} + try_lock_wrapper(detail::thread_move_t > other): + base(detail::thread_move_t(*other)) + {} + + operator detail::thread_move_t >() + { + return move(); + } + + detail::thread_move_t > move() + { + return detail::thread_move_t >(*this); + } + + try_lock_wrapper& operator=(detail::thread_move_t > other) + { + try_lock_wrapper temp(other); + swap(temp); + return *this; + } + + void swap(try_lock_wrapper& other) + { + base::swap(other); + } + void swap(detail::thread_move_t > other) + { + base::swap(*other); + } + + using base::lock; + using base::try_lock; + using base::unlock; + using base::owns_lock; + using base::operator!; + using base::mutex; + using base::release; + + typedef void (unique_lock::*bool_type)(); + operator bool_type() const + { + return owns_lock()?&unique_lock::lock:0; + } + }; + } } #endif diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index 3044a25b..9a228817 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -69,7 +69,7 @@ namespace boost } typedef unique_lock scoped_lock; - typedef scoped_lock scoped_try_lock; + typedef detail::try_lock_wrapper scoped_try_lock; }; typedef mutex try_mutex; @@ -187,7 +187,7 @@ namespace boost #endif typedef unique_lock scoped_timed_lock; - typedef scoped_timed_lock scoped_try_lock; + typedef detail::try_lock_wrapper scoped_try_lock; typedef scoped_timed_lock scoped_lock; }; diff --git a/include/boost/thread/pthread/recursive_mutex.hpp b/include/boost/thread/pthread/recursive_mutex.hpp index f496822d..b19a1adf 100644 --- a/include/boost/thread/pthread/recursive_mutex.hpp +++ b/include/boost/thread/pthread/recursive_mutex.hpp @@ -77,7 +77,7 @@ namespace boost return !res; } typedef unique_lock scoped_lock; - typedef scoped_lock scoped_try_lock; + typedef detail::try_lock_wrapper scoped_try_lock; }; typedef recursive_mutex recursive_try_mutex; @@ -239,7 +239,7 @@ namespace boost #endif typedef unique_lock scoped_timed_lock; - typedef scoped_timed_lock scoped_try_lock; + typedef detail::try_lock_wrapper scoped_try_lock; typedef scoped_timed_lock scoped_lock; }; diff --git a/include/boost/thread/win32/mutex.hpp b/include/boost/thread/win32/mutex.hpp index 29759a9a..41787559 100644 --- a/include/boost/thread/win32/mutex.hpp +++ b/include/boost/thread/win32/mutex.hpp @@ -32,7 +32,7 @@ namespace boost } typedef unique_lock scoped_lock; - typedef scoped_lock scoped_try_lock; + typedef detail::try_lock_wrapper scoped_try_lock; }; typedef mutex try_mutex; @@ -53,7 +53,7 @@ namespace boost } typedef unique_lock scoped_timed_lock; - typedef scoped_timed_lock scoped_try_lock; + typedef detail::try_lock_wrapper scoped_try_lock; typedef scoped_timed_lock scoped_lock; }; } diff --git a/include/boost/thread/win32/recursive_mutex.hpp b/include/boost/thread/win32/recursive_mutex.hpp index 855dd15b..7f7541a1 100644 --- a/include/boost/thread/win32/recursive_mutex.hpp +++ b/include/boost/thread/win32/recursive_mutex.hpp @@ -32,7 +32,7 @@ namespace boost } typedef unique_lock scoped_lock; - typedef scoped_lock scoped_try_lock; + typedef detail::try_lock_wrapper scoped_try_lock; }; typedef recursive_mutex recursive_try_mutex; @@ -52,7 +52,7 @@ namespace boost } typedef unique_lock scoped_timed_lock; - typedef scoped_timed_lock scoped_try_lock; + typedef detail::try_lock_wrapper scoped_try_lock; typedef scoped_timed_lock scoped_lock; }; } diff --git a/test/test_lock_concept.cpp b/test/test_lock_concept.cpp index b0f5dc72..e9f0cf0a 100644 --- a/test/test_lock_concept.cpp +++ b/test/test_lock_concept.cpp @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include template struct test_initially_locked @@ -22,6 +24,64 @@ struct test_initially_locked } }; +template +struct test_initially_unlocked_if_other_thread_has_lock +{ + Mutex m; + boost::mutex done_mutex; + bool done; + bool locked; + boost::condition_variable done_cond; + + test_initially_unlocked_if_other_thread_has_lock(): + done(false),locked(false) + {} + + void locking_thread() + { + Lock lock(m); + + boost::lock_guard lk(done_mutex); + locked=lock.owns_lock(); + done=true; + done_cond.notify_one(); + } + + bool is_done() const + { + return done; + } + + + void operator()() + { + Lock lock(m); + + typedef test_initially_unlocked_if_other_thread_has_lock this_type; + + boost::thread t(&this_type::locking_thread,this); + + try + { + { + boost::mutex::scoped_lock lk(done_mutex); + BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), + boost::bind(&this_type::is_done,this))); + BOOST_CHECK(!locked); + } + + lock.unlock(); + t.join(); + } + catch(...) + { + lock.unlock(); + t.join(); + throw; + } + } +}; + template struct test_initially_unlocked_with_defer_lock_parameter { @@ -144,6 +204,7 @@ BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_try_lock_concept,Mutex) typedef typename Mutex::scoped_try_lock Lock; test_initially_locked()(); + test_initially_unlocked_if_other_thread_has_lock()(); test_initially_unlocked_with_defer_lock_parameter()(); test_initially_locked_with_adopt_lock_parameter()(); test_unlocked_after_unlock_called()(); From a5c02b73dc0e7be43a3a240ebc3dd0b53b6829d9 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 28 Apr 2008 09:10:38 +0000 Subject: [PATCH 118/214] Added entry to breaking changes about default-constructed threads and the current thread: issue #1835 [SVN r44840] --- doc/changes.qbk | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/changes.qbk b/doc/changes.qbk index 4a1ccbb3..30c4a2c2 100644 --- a/doc/changes.qbk +++ b/doc/changes.qbk @@ -46,7 +46,10 @@ but did not lock it on construction. This facility has now been replaced with th * The `locked()` member function of the `scoped_lock` types has been renamed to __owns_lock_ref__. +* You can no longer obtain a __thread__ instance representing the current thread: a default-constructed __thread__ object is not +associated with any thread. The only use for such a thread object was to support the comparison operators: this functionality has +been moved to __thread_id__. + * The broken `boost::read_write_mutex` has been replaced with __shared_mutex__. - [endsect] From 1c5c07098354754262b382a207d86096495f2492 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 28 Apr 2008 12:26:27 +0000 Subject: [PATCH 119/214] Updated locks.hpp to work with gcc as well as msvc [SVN r44846] --- include/boost/thread/locks.hpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index c1952948..71ec5373 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -606,7 +606,7 @@ namespace boost base(m_,defer_lock) {} try_lock_wrapper(Mutex& m_,try_to_lock_t): - base(m,try_to_lock) + base(m_,try_to_lock) {} try_lock_wrapper(detail::thread_move_t > other): base(detail::thread_move_t(*other)) @@ -645,12 +645,8 @@ namespace boost using base::operator!; using base::mutex; using base::release; - - typedef void (unique_lock::*bool_type)(); - operator bool_type() const - { - return owns_lock()?&unique_lock::lock:0; - } + typedef typename base::bool_type bool_type; + using base::operator bool_type; }; } } From ec735d3e9bafec24d2661bc4fda75f7be7a0d770 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Sun, 4 May 2008 09:52:54 +0000 Subject: [PATCH 120/214] Simplified move support [SVN r45108] --- include/boost/thread/detail/move.hpp | 17 ++++- include/boost/thread/locks.hpp | 37 ---------- include/boost/thread/pthread/thread.hpp | 10 +-- include/boost/thread/win32/thread.hpp | 9 +-- test/Jamfile.v2 | 2 + .../no_implicit_assign_from_lvalue_thread.cpp | 15 ++++ test/no_implicit_move_from_lvalue_thread.cpp | 14 ++++ test/test_move_function.cpp | 74 ++++++++++++++++++- 8 files changed, 123 insertions(+), 55 deletions(-) create mode 100644 test/no_implicit_assign_from_lvalue_thread.cpp create mode 100644 test/no_implicit_move_from_lvalue_thread.cpp diff --git a/include/boost/thread/detail/move.hpp b/include/boost/thread/detail/move.hpp index d4732317..48744932 100644 --- a/include/boost/thread/detail/move.hpp +++ b/include/boost/thread/detail/move.hpp @@ -6,6 +6,9 @@ #ifndef BOOST_THREAD_MOVE_HPP #define BOOST_THREAD_MOVE_HPP +#include +#include + namespace boost { namespace detail @@ -14,7 +17,7 @@ namespace boost struct thread_move_t { T& t; - thread_move_t(T& t_): + explicit thread_move_t(T& t_): t(t_) {} @@ -31,6 +34,18 @@ namespace boost void operator=(thread_move_t&); }; } + + template + typename enable_if >, detail::thread_move_t >::type move(T& t) + { + return t; + } + + template + detail::thread_move_t move(detail::thread_move_t t) + { + return t; + } } diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 71ec5373..8fe1263f 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -204,18 +204,6 @@ namespace boost friend class upgrade_lock; }; - template - inline detail::thread_move_t > move(unique_lock & x) - { - return x.move(); - } - - template - inline detail::thread_move_t > move(detail::thread_move_t > x) - { - return x; - } - template class shared_lock { @@ -372,19 +360,6 @@ namespace boost }; - template - inline detail::thread_move_t > move(shared_lock & x) - { - return x.move(); - } - - template - inline detail::thread_move_t > move(detail::thread_move_t > x) - { - return x; - } - - template class upgrade_lock { @@ -511,18 +486,6 @@ namespace boost }; - template - inline detail::thread_move_t > move(upgrade_lock & x) - { - return x.move(); - } - - template - inline detail::thread_move_t > move(detail::thread_move_t > x) - { - return x; - } - template unique_lock::unique_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index 813ced09..cdfade0e 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -222,16 +222,10 @@ namespace boost bool interruption_requested() const; }; - inline detail::thread_move_t move(thread& x) + inline detail::thread_move_t move(detail::thread_move_t t) { - return x.move(); + return t; } - - inline detail::thread_move_t move(detail::thread_move_t x) - { - return x; - } - template struct thread::thread_data >: diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 3b043b84..22fa6212 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -281,14 +281,9 @@ namespace boost bool interruption_requested() const; }; - inline detail::thread_move_t move(thread& x) + inline detail::thread_move_t move(detail::thread_move_t t) { - return x.move(); - } - - inline detail::thread_move_t move(detail::thread_move_t x) - { - return x; + return t; } template diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 04dab7af..405e83bc 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -53,5 +53,7 @@ rule thread-run ( sources ) [ thread-run test_shared_mutex_part_2.cpp ] [ thread-run test_shared_mutex_timed_locks.cpp ] [ thread-run test_lock_concept.cpp ] + [ compile-fail no_implicit_move_from_lvalue_thread.cpp ] + [ compile-fail no_implicit_assign_from_lvalue_thread.cpp ] ; } diff --git a/test/no_implicit_assign_from_lvalue_thread.cpp b/test/no_implicit_assign_from_lvalue_thread.cpp new file mode 100644 index 00000000..02b6893e --- /dev/null +++ b/test/no_implicit_assign_from_lvalue_thread.cpp @@ -0,0 +1,15 @@ +// Copyright (C) 2008 Anthony Williams +// +// 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) +#include + +void do_nothing() +{} + +void test() +{ + boost::thread t1(do_nothing); + boost::thread t2; + t2=t1; +} diff --git a/test/no_implicit_move_from_lvalue_thread.cpp b/test/no_implicit_move_from_lvalue_thread.cpp new file mode 100644 index 00000000..5c4600ec --- /dev/null +++ b/test/no_implicit_move_from_lvalue_thread.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2008 Anthony Williams +// +// 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) +#include + +void do_nothing() +{} + +void test() +{ + boost::thread t1(do_nothing); + boost::thread t2(t1); +} diff --git a/test/test_move_function.cpp b/test/test_move_function.cpp index 153a4cf1..61e08b77 100644 --- a/test/test_move_function.cpp +++ b/test/test_move_function.cpp @@ -1,10 +1,11 @@ -// Copyright (C) 2007 Anthony Williams +// Copyright (C) 2007-8 Anthony Williams // // 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) #include #include #include +#include void do_nothing() {} @@ -20,13 +21,36 @@ void test_thread_move_from_lvalue_on_construction() dest.join(); } +void test_thread_move_from_lvalue_on_assignment() +{ + boost::thread src(do_nothing); + boost::thread::id src_id=src.get_id(); + boost::thread dest; + dest=boost::move(src); + boost::thread::id dest_id=dest.get_id(); + BOOST_CHECK(src_id==dest_id); + BOOST_CHECK(src.get_id()==boost::thread::id()); + dest.join(); +} + +boost::thread start_thread() +{ + return boost::thread(do_nothing); +} + void test_thread_move_from_rvalue_on_construction() { - boost::thread x(boost::move(boost::thread(do_nothing))); + boost::thread x(start_thread()); BOOST_CHECK(x.get_id()!=boost::thread::id()); x.join(); } +void test_thread_move_from_rvalue_using_explicit_move() +{ + boost::thread x(boost::move(start_thread())); + BOOST_CHECK(x.get_id()!=boost::thread::id()); + x.join(); +} void test_unique_lock_move_from_lvalue_on_construction() { @@ -42,6 +66,48 @@ void test_unique_lock_move_from_lvalue_on_construction() BOOST_CHECK(l2.mutex()==&m); } +boost::unique_lock get_lock(boost::mutex& m) +{ + return boost::unique_lock(m); +} + + +void test_unique_lock_move_from_rvalue_on_construction() +{ + boost::mutex m; + boost::unique_lock l(get_lock(m)); + BOOST_CHECK(l.owns_lock()); + BOOST_CHECK(l.mutex()==&m); +} + +namespace user +{ + template + T move(T& t) + { + return t.move(); + } + + bool move_called=false; + + struct nc: + public boost::shared_ptr + { + nc move() + { + move_called=true; + return nc(); + } + }; +} + +void test_move_for_user_defined_type_unaffected() +{ + user::nc src; + user::nc dest=move(src); + BOOST_CHECK(user::move_called); +} + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = @@ -49,6 +115,10 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) test->add(BOOST_TEST_CASE(test_thread_move_from_lvalue_on_construction)); test->add(BOOST_TEST_CASE(test_thread_move_from_rvalue_on_construction)); + test->add(BOOST_TEST_CASE(test_thread_move_from_rvalue_using_explicit_move)); + test->add(BOOST_TEST_CASE(test_thread_move_from_lvalue_on_assignment)); test->add(BOOST_TEST_CASE(test_unique_lock_move_from_lvalue_on_construction)); + test->add(BOOST_TEST_CASE(test_unique_lock_move_from_rvalue_on_construction)); + test->add(BOOST_TEST_CASE(test_move_for_user_defined_type_unaffected)); return test; } From 0516b86a6ecbeba15e391a5b7b7cecec54d48802 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Sun, 4 May 2008 22:39:52 +0000 Subject: [PATCH 121/214] new BTS-based mutex implementation on win32 [SVN r45119] --- .../boost/thread/win32/basic_timed_mutex.hpp | 56 ++++----- .../boost/thread/win32/thread_primitives.hpp | 109 ++++++++++++++++++ 2 files changed, 137 insertions(+), 28 deletions(-) diff --git a/include/boost/thread/win32/basic_timed_mutex.hpp b/include/boost/thread/win32/basic_timed_mutex.hpp index 207c0920..56b4622a 100644 --- a/include/boost/thread/win32/basic_timed_mutex.hpp +++ b/include/boost/thread/win32/basic_timed_mutex.hpp @@ -21,7 +21,10 @@ namespace boost { struct basic_timed_mutex { - BOOST_STATIC_CONSTANT(long,lock_flag_value=0x80000000); + BOOST_STATIC_CONSTANT(unsigned char,lock_flag_bit=31); + BOOST_STATIC_CONSTANT(unsigned char,event_set_flag_bit=30); + BOOST_STATIC_CONSTANT(long,lock_flag_value=1<offset) + long const offset=lock_flag_value; + long const old_count=BOOST_INTERLOCKED_EXCHANGE_ADD(&active_count,lock_flag_value); + if(!(old_count&event_set_flag_value) && (old_count>offset)) { - win32::SetEvent(get_event()); + if(!win32::interlocked_bit_test_and_set(&active_count,event_set_flag_bit)) + { + win32::SetEvent(get_event()); + } } } diff --git a/include/boost/thread/win32/thread_primitives.hpp b/include/boost/thread/win32/thread_primitives.hpp index f8f66b15..3109f142 100644 --- a/include/boost/thread/win32/thread_primitives.hpp +++ b/include/boost/thread/win32/thread_primitives.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #if defined( BOOST_USE_WINDOWS_H ) @@ -277,5 +278,113 @@ namespace boost } } +#if defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN) +#if MSC_VER>=1400 +extern "C" unsigned char _interlockedbittestandset(long *a,long b); +extern "C" unsigned char _interlockedbittestandreset(long *a,long b); + +#pragma intrinsic(_interlockedbittestandset) +#pragma intrinsic(_interlockedbittestandreset) + +namespace boost +{ + namespace detail + { + namespace win32 + { + inline bool interlocked_bit_test_and_set(long* x,long bit) + { + return _interlockedbittestandset(x,bit); + } + + inline bool interlocked_bit_test_and_reset(long* x,long bit) + { + return _interlockedbittestandreset(x,bit); + } + + } + } +} +#define BOOST_THREAD_BTS_DEFINED +#elif defined(_M_IX86) +namespace boost +{ + namespace detail + { + namespace win32 + { + inline bool interlocked_bit_test_and_set(long* x,long bit) + { + __asm { + mov eax,bit; + mov edx,x; + lock bts [edx],eax; + setc al; + }; + } + + inline bool interlocked_bit_test_and_reset(long* x,long bit) + { + __asm { + mov eax,bit; + mov edx,x; + lock btr [edx],eax; + setc al; + }; + } + + } + } +} +#define BOOST_THREAD_BTS_DEFINED +#endif +#endif + +#ifndef BOOST_THREAD_BTS_DEFINED + +namespace boost +{ + namespace detail + { + namespace win32 + { + inline bool interlocked_bit_test_and_set(long* x,long bit) + { + long const value=1< Date: Thu, 8 May 2008 12:59:10 +0000 Subject: [PATCH 122/214] Added native_handle to mutex types where possible [SVN r45210] --- include/boost/thread/pthread/mutex.hpp | 7 +++++++ include/boost/thread/pthread/recursive_mutex.hpp | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index 9a228817..f99838d6 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -139,6 +139,13 @@ namespace boost BOOST_ASSERT(!res || res==ETIMEDOUT); return !res; } + + typedef pthread_mutex_t* native_handle_type; + native_handle_type native_handle() + { + return &m; + } + #else void lock() { diff --git a/include/boost/thread/pthread/recursive_mutex.hpp b/include/boost/thread/pthread/recursive_mutex.hpp index b19a1adf..4b50b440 100644 --- a/include/boost/thread/pthread/recursive_mutex.hpp +++ b/include/boost/thread/pthread/recursive_mutex.hpp @@ -76,6 +76,13 @@ namespace boost BOOST_ASSERT(!res || res==EBUSY); return !res; } + + typedef pthread_mutex_t* native_handle_type; + native_handle_type native_handle() + { + return &m; + } + typedef unique_lock scoped_lock; typedef detail::try_lock_wrapper scoped_try_lock; }; @@ -171,6 +178,13 @@ namespace boost BOOST_ASSERT(!res || res==EBUSY); return !res; } + + typedef pthread_mutex_t* native_handle_type; + native_handle_type native_handle() + { + return &m; + } + #else void lock() { From 3926fd3a20509770da0ea9709f1aaa87a857fad0 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 8 May 2008 12:59:59 +0000 Subject: [PATCH 123/214] Added docs for native_handle [SVN r45211] --- doc/mutexes.qbk | 77 ++++++++++++++++++++++++++++++++++++++++++++++ doc/thread_ref.qbk | 16 ++++++++++ 2 files changed, 93 insertions(+) diff --git a/doc/mutexes.qbk b/doc/mutexes.qbk index 020f767f..cf920235 100644 --- a/doc/mutexes.qbk +++ b/doc/mutexes.qbk @@ -12,6 +12,9 @@ void lock(); bool try_lock(); void unlock(); + + typedef platform-specific-type native_handle_type; + native_handle_type native_handle(); typedef unique_lock scoped_lock; typedef scoped_lock scoped_try_lock; @@ -20,6 +23,23 @@ __mutex__ implements the __lockable_concept__ to provide an exclusive-ownership mutex. At most one thread can own the lock on a given instance of __mutex__ at any time. Multiple concurrent calls to __lock_ref__, __try_lock_ref__ and __unlock_ref__ shall be permitted. +[section:nativehandle Member function `native_handle()`] + + typedef platform-specific-type native_handle_type; + native_handle_type native_handle(); + +[variablelist + +[[Effects:] [Returns an instance of `native_handle_type` that can be used with platform-specific APIs to manipulate the underlying +implementation. If no such instance exists, `native_handle()` and `native_handle_type` are not present.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + + [endsect] [section:try_mutex Typedef `try_mutex`] @@ -47,6 +67,9 @@ __try_mutex__ is a `typedef` to __mutex__, provided for backwards compatibility template bool timed_lock(TimeDuration const & relative_time); + typedef platform-specific-type native_handle_type; + native_handle_type native_handle(); + typedef unique_lock scoped_timed_lock; typedef scoped_timed_lock scoped_try_lock; typedef scoped_timed_lock scoped_lock; @@ -56,6 +79,22 @@ __timed_mutex__ implements the __timed_lockable_concept__ to provide an exclusiv lock on a given instance of __timed_mutex__ at any time. Multiple concurrent calls to __lock_ref__, __try_lock_ref__, __timed_lock_ref__, __timed_lock_duration_ref__ and __unlock_ref__ shall be permitted. +[section:nativehandle Member function `native_handle()`] + + typedef platform-specific-type native_handle_type; + native_handle_type native_handle(); + +[variablelist + +[[Effects:] [Returns an instance of `native_handle_type` that can be used with platform-specific APIs to manipulate the underlying +implementation. If no such instance exists, `native_handle()` and `native_handle_type` are not present.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + [endsect] [section:recursive_mutex Class `recursive_mutex`] @@ -70,6 +109,9 @@ __timed_lock_ref__, __timed_lock_duration_ref__ and __unlock_ref__ shall be perm void lock(); bool try_lock(); void unlock(); + + typedef platform-specific-type native_handle_type; + native_handle_type native_handle(); typedef unique_lock scoped_lock; typedef scoped_lock scoped_try_lock; @@ -81,6 +123,22 @@ __unlock_ref__ shall be permitted. A thread that already has exclusive ownership __lock_ref__ or __try_lock_ref__ to acquire an additional level of ownership of the mutex. __unlock_ref__ must be called once for each level of ownership acquired by a single thread before ownership can be acquired by another thread. +[section:nativehandle Member function `native_handle()`] + + typedef platform-specific-type native_handle_type; + native_handle_type native_handle(); + +[variablelist + +[[Effects:] [Returns an instance of `native_handle_type` that can be used with platform-specific APIs to manipulate the underlying +implementation. If no such instance exists, `native_handle()` and `native_handle_type` are not present.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + [endsect] [section:recursive_try_mutex Typedef `recursive_try_mutex`] @@ -108,6 +166,9 @@ __recursive_try_mutex__ is a `typedef` to __recursive_mutex__, provided for back template bool timed_lock(TimeDuration const & relative_time); + + typedef platform-specific-type native_handle_type; + native_handle_type native_handle(); typedef unique_lock scoped_lock; typedef scoped_lock scoped_try_lock; @@ -121,6 +182,22 @@ exclusive ownership of a given __recursive_timed_mutex__ instance can call __loc __timed_lock_duration_ref__ or __try_lock_ref__ to acquire an additional level of ownership of the mutex. __unlock_ref__ must be called once for each level of ownership acquired by a single thread before ownership can be acquired by another thread. +[section:nativehandle Member function `native_handle()`] + + typedef platform-specific-type native_handle_type; + native_handle_type native_handle(); + +[variablelist + +[[Effects:] [Returns an instance of `native_handle_type` that can be used with platform-specific APIs to manipulate the underlying +implementation. If no such instance exists, `native_handle()` and `native_handle_type` are not present.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + [endsect] [include shared_mutex_ref.qbk] diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk index caadc362..50c05e77 100644 --- a/doc/thread_ref.qbk +++ b/doc/thread_ref.qbk @@ -364,6 +364,22 @@ or 0 if this information is not available.]] [endsect] +[section:nativehandle Member function `native_handle()`] + + typedef platform-specific-type native_handle_type; + native_handle_type native_handle(); + +[variablelist + +[[Effects:] [Returns an instance of `native_handle_type` that can be used with platform-specific APIs to manipulate the underlying +implementation. If no such instance exists, `native_handle()` and `native_handle_type` are not present.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + [section:equals `operator==`] bool operator==(const thread& other) const; From 70d9dbc45aaa746b162b989698cb05906b1fdf6e Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 8 May 2008 14:34:40 +0000 Subject: [PATCH 124/214] Added default constructor to lock types [SVN r45212] --- doc/mutex_concepts.qbk | 30 ++++++++++++++++++++++++++++++ include/boost/thread/locks.hpp | 26 +++++++++++++++++++++++--- test/test_lock_concept.cpp | 12 ++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/doc/mutex_concepts.qbk b/doc/mutex_concepts.qbk index f99acd18..2fc5cb97 100644 --- a/doc/mutex_concepts.qbk +++ b/doc/mutex_concepts.qbk @@ -373,6 +373,7 @@ object passed to the constructor.]] class unique_lock { public: + unique_lock(); explicit unique_lock(Lockable& m_); unique_lock(Lockable& m_,adopt_lock_t); unique_lock(Lockable& m_,defer_lock_t); @@ -426,6 +427,20 @@ The member functions of __unique_lock__ are not thread-safe. In particular, __un __lockable_concept_type__ object by a particular thread, and the member functions that release ownership of the lock state (including the destructor) must be called by the same thread that acquired ownership of the lock state. +[section:defaultconstructor `unique_lock()`] + +[variablelist + +[[Effects:] [Creates a lock object with no associated mutex.]] + +[[Postcondition:] [__owns_lock_ref__ returns `false`. __mutex_func_ref__ returns `NULL`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + [section:constructor `unique_lock(Lockable & m)`] [variablelist @@ -599,6 +614,7 @@ __owns_lock_ref__ returns `false`.]] class shared_lock { public: + shared_lock(); explicit shared_lock(Lockable& m_); shared_lock(Lockable& m_,adopt_lock_t); shared_lock(Lockable& m_,defer_lock_t); @@ -644,6 +660,20 @@ The member functions of __shared_lock__ are not thread-safe. In particular, __sh ownership of a __lockable_concept_type__ object by a particular thread, and the member functions that release ownership of the lock state (including the destructor) must be called by the same thread that acquired ownership of the lock state. +[section:defaultconstructor `shared_lock()`] + +[variablelist + +[[Effects:] [Creates a lock object with no associated mutex.]] + +[[Postcondition:] [__owns_lock_ref__ returns `false`. __mutex_func_ref__ returns `NULL`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + [section:constructor `shared_lock(Lockable & m)`] [variablelist diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 8fe1263f..b26b4aed 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -62,6 +62,10 @@ namespace boost explicit unique_lock(unique_lock&); unique_lock& operator=(unique_lock&); public: + unique_lock(): + m(0),is_locked(false) + {} + explicit unique_lock(Mutex& m_): m(&m_),is_locked(false) { @@ -214,6 +218,10 @@ namespace boost explicit shared_lock(shared_lock&); shared_lock& operator=(shared_lock&); public: + shared_lock(): + m(0),is_locked(false) + {} + explicit shared_lock(Mutex& m_): m(&m_),is_locked(false) { @@ -240,26 +248,29 @@ namespace boost m(other->m),is_locked(other->is_locked) { other->is_locked=false; + other->m=0; } shared_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { - other->is_locked=false; if(is_locked) { m->unlock_and_lock_shared(); } + other->is_locked=false; + other->m=0; } shared_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { - other->is_locked=false; if(is_locked) { m->unlock_upgrade_and_lock_shared(); } + other->is_locked=false; + other->m=0; } operator detail::thread_move_t >() @@ -370,6 +381,10 @@ namespace boost explicit upgrade_lock(upgrade_lock&); upgrade_lock& operator=(upgrade_lock&); public: + upgrade_lock(): + m(0),is_locked(false) + {} + explicit upgrade_lock(Mutex& m_): m(&m_),is_locked(false) { @@ -390,16 +405,18 @@ namespace boost m(other->m),is_locked(other->is_locked) { other->is_locked=false; + other->m=0; } upgrade_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { - other->is_locked=false; if(is_locked) { m->unlock_and_lock_upgrade(); } + other->is_locked=false; + other->m=0; } operator detail::thread_move_t >() @@ -558,6 +575,9 @@ namespace boost { typedef unique_lock base; public: + try_lock_wrapper() + {} + explicit try_lock_wrapper(Mutex& m): base(m,try_to_lock) {} diff --git a/test/test_lock_concept.cpp b/test/test_lock_concept.cpp index e9f0cf0a..6b8b483f 100644 --- a/test/test_lock_concept.cpp +++ b/test/test_lock_concept.cpp @@ -185,11 +185,22 @@ struct test_throws_if_unlock_called_when_already_unlocked BOOST_CHECK_THROW( lock.unlock(), boost::lock_error ); } }; +template +struct test_default_constructed_has_no_mutex_and_unlocked +{ + void operator()() const + { + Lock l; + BOOST_CHECK(!l.mutex()); + BOOST_CHECK(!l.owns_lock()); + }; +}; BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_lock_concept,Mutex) { typedef typename Mutex::scoped_lock Lock; + test_default_constructed_has_no_mutex_and_unlocked()(); test_initially_locked()(); test_initially_unlocked_with_defer_lock_parameter()(); test_initially_locked_with_adopt_lock_parameter()(); @@ -203,6 +214,7 @@ BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_try_lock_concept,Mutex) { typedef typename Mutex::scoped_try_lock Lock; + test_default_constructed_has_no_mutex_and_unlocked()(); test_initially_locked()(); test_initially_unlocked_if_other_thread_has_lock()(); test_initially_unlocked_with_defer_lock_parameter()(); From 86097fa038c532c34c3f06d54605d8884d50c23b Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 9 May 2008 07:47:14 +0000 Subject: [PATCH 125/214] Use _WIN32 rather than WIN32 to prevent include of [SVN r45241] --- include/boost/thread/pthread/mutex.hpp | 2 +- include/boost/thread/pthread/recursive_mutex.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index f99838d6..e1fa2593 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -11,7 +11,7 @@ #include #include #include -#ifndef WIN32 +#ifndef _WIN32 #include #endif #include diff --git a/include/boost/thread/pthread/recursive_mutex.hpp b/include/boost/thread/pthread/recursive_mutex.hpp index 4b50b440..d0ddcef8 100644 --- a/include/boost/thread/pthread/recursive_mutex.hpp +++ b/include/boost/thread/pthread/recursive_mutex.hpp @@ -11,7 +11,7 @@ #include #include #include -#ifndef WIN32 +#ifndef _WIN32 #include #endif #include From 33d9f9774cecbc33fbde746f3dd2c42ad7cfd7a9 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 9 May 2008 07:48:44 +0000 Subject: [PATCH 126/214] Test and fix for bug #1905 [SVN r45242] --- include/boost/thread/pthread/shared_mutex.hpp | 146 +++++------------- include/boost/thread/win32/shared_mutex.hpp | 17 +- test/test_shared_mutex_timed_locks.cpp | 33 ++++ 3 files changed, 79 insertions(+), 117 deletions(-) diff --git a/include/boost/thread/pthread/shared_mutex.hpp b/include/boost/thread/pthread/shared_mutex.hpp index 1648150d..d44b101c 100644 --- a/include/boost/thread/pthread/shared_mutex.hpp +++ b/include/boost/thread/pthread/shared_mutex.hpp @@ -44,7 +44,7 @@ namespace boost public: shared_mutex() { - state_data state_={0}; + state_data state_={0,0,0,0}; state=state_; } @@ -57,23 +57,11 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true) -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif + while(state.exclusive || state.exclusive_waiting_blocked) { - if(!state.exclusive && !state.exclusive_waiting_blocked) - { - ++state.shared_count; - return; - } - shared_cond.wait(lock); } + ++state.shared_count; } bool try_lock_shared() @@ -96,26 +84,15 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true) -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif + while(state.exclusive || state.exclusive_waiting_blocked) { - if(!state.exclusive && !state.exclusive_waiting_blocked) - { - ++state.shared_count; - return true; - } - if(!shared_cond.timed_wait(lock,timeout)) { return false; } } + ++state.shared_count; + return true; } template @@ -150,56 +127,35 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true) -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif + while(state.shared_count || state.exclusive) { - if(state.shared_count || state.exclusive) - { - state.exclusive_waiting_blocked=true; - } - else - { - state.exclusive=true; - return; - } + state.exclusive_waiting_blocked=true; exclusive_cond.wait(lock); } + state.exclusive=true; } bool timed_lock(system_time const& timeout) { boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); - -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true) -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif + + while(state.shared_count || state.exclusive) { - if(state.shared_count || state.exclusive) - { - state.exclusive_waiting_blocked=true; - } - else - { - state.exclusive=true; - return true; - } + state.exclusive_waiting_blocked=true; if(!exclusive_cond.timed_wait(lock,timeout)) { - return false; + if(state.shared_count || state.exclusive) + { + state.exclusive_waiting_blocked=false; + exclusive_cond.notify_one(); + return false; + } + break; } } + state.exclusive=true; + return true; } template @@ -236,51 +192,32 @@ namespace boost { boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true) -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif + while(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) { - if(!state.exclusive && !state.exclusive_waiting_blocked && !state.upgrade) - { - ++state.shared_count; - state.upgrade=true; - return; - } - shared_cond.wait(lock); } + ++state.shared_count; + state.upgrade=true; } bool timed_lock_upgrade(system_time const& timeout) { boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true) -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif + while(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) { - if(!state.exclusive && !state.exclusive_waiting_blocked && !state.upgrade) - { - ++state.shared_count; - state.upgrade=true; - return true; - } - if(!shared_cond.timed_wait(lock,timeout)) { - return false; + if(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) + { + return false; + } + break; } } + ++state.shared_count; + state.upgrade=true; + return true; } template @@ -322,23 +259,12 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; boost::mutex::scoped_lock lock(state_change); --state.shared_count; -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true) -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif + while(state.shared_count) { - if(!state.shared_count) - { - state.upgrade=false; - state.exclusive=true; - break; - } upgrade_cond.wait(lock); } + state.upgrade=false; + state.exclusive=true; } void unlock_and_lock_upgrade() diff --git a/include/boost/thread/win32/shared_mutex.hpp b/include/boost/thread/win32/shared_mutex.hpp index 1862cfdd..0b7d6fae 100644 --- a/include/boost/thread/win32/shared_mutex.hpp +++ b/include/boost/thread/win32/shared_mutex.hpp @@ -23,12 +23,12 @@ namespace boost private: struct state_data { - unsigned shared_count:11; - unsigned shared_waiting:11; - unsigned exclusive:1; - unsigned upgrade:1; - unsigned exclusive_waiting:7; - unsigned exclusive_waiting_blocked:1; + unsigned shared_count:11, + shared_waiting:11, + exclusive:1, + upgrade:1, + exclusive_waiting:7, + exclusive_waiting_blocked:1; friend bool operator==(state_data const& lhs,state_data const& rhs) { @@ -337,7 +337,10 @@ namespace boost { if(new_state.exclusive_waiting) { - --new_state.exclusive_waiting; + if(!--new_state.exclusive_waiting) + { + new_state.exclusive_waiting_blocked=false; + } } } else diff --git a/test/test_shared_mutex_timed_locks.cpp b/test/test_shared_mutex_timed_locks.cpp index 815f7d3e..04efbc9b 100644 --- a/test/test_shared_mutex_timed_locks.cpp +++ b/test/test_shared_mutex_timed_locks.cpp @@ -218,6 +218,38 @@ void test_timed_lock_times_out_if_read_lock_held() reader.join(); } +void test_timed_lock_times_out_but_read_lock_succeeds_if_read_lock_held() +{ + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + unsigned unblocked_count=0; + boost::mutex::scoped_lock finish_lock(finish_mutex); + boost::thread reader(simple_reading_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); + boost::this_thread::sleep(boost::posix_time::seconds(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); + + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+boost::posix_time::milliseconds(500); + bool timed_lock_succeeded=rw_mutex.timed_lock(timeout); + BOOST_CHECK(!timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock(); + } + + boost::posix_time::milliseconds const wait_duration(500); + timed_lock_succeeded=rw_mutex.timed_lock_shared(wait_duration); + BOOST_CHECK(timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock_shared(); + } + + finish_lock.unlock(); + reader.join(); +} + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { @@ -230,6 +262,7 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) test->add(BOOST_TEST_CASE(&test_timed_lock_times_out_if_write_lock_held)); test->add(BOOST_TEST_CASE(&test_timed_lock_times_out_if_read_lock_held)); test->add(BOOST_TEST_CASE(&test_timed_lock_succeeds_if_no_lock_held)); + test->add(BOOST_TEST_CASE(&test_timed_lock_times_out_but_read_lock_succeeds_if_read_lock_held)); return test; } From ea0961b7f6c80ff4e10713640396698cd6306816 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 9 May 2008 07:49:22 +0000 Subject: [PATCH 127/214] Fixed type truncation warning [SVN r45243] --- include/boost/thread/pthread/timespec.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/thread/pthread/timespec.hpp b/include/boost/thread/pthread/timespec.hpp index f77bd3b7..0837e97e 100644 --- a/include/boost/thread/pthread/timespec.hpp +++ b/include/boost/thread/pthread/timespec.hpp @@ -19,7 +19,7 @@ namespace boost boost::posix_time::time_duration const time_since_epoch=abs_time-boost::posix_time::from_time_t(0); timeout.tv_sec=time_since_epoch.total_seconds(); - timeout.tv_nsec=time_since_epoch.fractional_seconds()*(1000000000/time_since_epoch.ticks_per_second()); + timeout.tv_nsec=(long)(time_since_epoch.fractional_seconds()*(1000000000l/time_since_epoch.ticks_per_second())); return timeout; } } From dc5d03a6dc6de3c98a4fa781bfc49f15db838206 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 9 May 2008 07:59:57 +0000 Subject: [PATCH 128/214] Cleaned up to remove warnings [SVN r45244] --- include/boost/thread/win32/basic_timed_mutex.hpp | 11 ++--------- src/win32/thread.cpp | 3 +++ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/include/boost/thread/win32/basic_timed_mutex.hpp b/include/boost/thread/win32/basic_timed_mutex.hpp index 56b4622a..77a9332f 100644 --- a/include/boost/thread/win32/basic_timed_mutex.hpp +++ b/include/boost/thread/win32/basic_timed_mutex.hpp @@ -67,14 +67,7 @@ namespace boost return true; } long old_count=active_count; -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true) -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif + for(;;) { long const new_count=(old_count&lock_flag_value)?(old_count+1):(old_count|lock_flag_value); long const current=BOOST_INTERLOCKED_COMPARE_EXCHANGE(&active_count,new_count,old_count); @@ -99,7 +92,7 @@ namespace boost } old_count&=~lock_flag_value; old_count|=event_set_flag_value; - while(true) + for(;;) { long const new_count=((old_count&lock_flag_value)?old_count:((old_count-1)|lock_flag_value))&~event_set_flag_value; long const current=BOOST_INTERLOCKED_COMPARE_EXCHANGE(&active_count,new_count,old_count); diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index d64c78dd..40c3588b 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -218,6 +218,9 @@ namespace boost void run() {} + private: + externally_launched_thread(externally_launched_thread&); + void operator=(externally_launched_thread&); }; void make_external_thread_data() From f77285f375092c6a79828341f016dd8f37e340d0 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 12 May 2008 09:04:02 +0000 Subject: [PATCH 129/214] Updated docs to make it explicit that terminate is called if a thread function throws an exception [SVN r45294] --- doc/thread_ref.qbk | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk index 50c05e77..849bc3c7 100644 --- a/doc/thread_ref.qbk +++ b/doc/thread_ref.qbk @@ -43,11 +43,21 @@ __boost_thread__ must ensure that the referred-to object outlives the newly-crea // this leads to undefined behaviour If you wish to construct an instance of __thread__ with a function or callable object that requires arguments to be supplied, -this can be done using `boost::bind`: +this can be done by passing additional arguments to the __thread__ constructor: void find_the_question(int the_answer); - boost::thread deep_thought_2(boost::bind(find_the_question,42)); + boost::thread deep_thought_2(find_the_question,42); + +The arguments are ['copied] into the internal thread structure: if a reference is required, use `boost::ref`, just as for references +to callable functions. + +There is an unspecified limit on the number of additional arguments that can be passed. + +[heading Exceptions in thread functions] + +If the function or callable object passed to the __thread__ constructor propagates an exception when invoked that is not of type +__thread_interrupted__, `std::terminate()` is called. [heading Joining and detaching] @@ -212,7 +222,9 @@ __thread_id__ yield a total order for every non-equal thread ID. [[Preconditions:] [`Callable` must by copyable.]] -[[Effects:] [`func` is copied into storage managed internally by the thread library, and that copy is invoked on a newly-created thread of execution.]] +[[Effects:] [`func` is copied into storage managed internally by the thread library, and that copy is invoked on a newly-created +thread of execution. If this invocation results in an exception being propagated into the internals of the thread library that is +not of type __thread_interrupted__, then `std::terminate()` will be called.]] [[Postconditions:] [`*this` refers to the newly created thread of execution.]] From 6d5e7f63a79463aa61a76b1c0c224a100de42399 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Sun, 18 May 2008 08:45:44 +0000 Subject: [PATCH 130/214] Added beginnings of real rvalue-reference support [SVN r45479] --- include/boost/thread/win32/thread.hpp | 30 ++++++++++++++++++- .../boost/thread/win32/thread_heap_alloc.hpp | 19 +++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 22fa6212..ab881c9e 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -174,13 +174,18 @@ namespace boost { F f; +#ifdef BOOST_HAS_RVALUE_REFS + thread_data(F&& f_): + f(static_cast(f_)) + {} +#else thread_data(F f_): f(f_) {} thread_data(detail::thread_move_t f_): f(f_) {} - +#endif void run() { f(); @@ -201,27 +206,50 @@ namespace boost detail::thread_data_ptr get_thread_info() const; +#ifdef BOOST_HAS_RVALUE_REFS + template + static inline detail::thread_data_ptr make_thread_info(F&& f) + { + return detail::heap_new >(static_cast(f)); + } +#else template static inline detail::thread_data_ptr make_thread_info(F f) { return detail::heap_new >(f); } + template + static inline detail::thread_data_ptr make_thread_info(boost::detail::thread_move_t f) + { + return detail::heap_new >(f); + } +#endif public: thread(); ~thread(); +#ifdef BOOST_HAS_RVALUE_REFS + template + thread(F&& f): + thread_info(make_thread_info(static_cast(f))) + { + start_thread(); + } +#else template explicit thread(F f): thread_info(make_thread_info(f)) { start_thread(); } + template thread(detail::thread_move_t f): thread_info(make_thread_info(f)) { start_thread(); } +#endif template thread(F f,A1 a1): diff --git a/include/boost/thread/win32/thread_heap_alloc.hpp b/include/boost/thread/win32/thread_heap_alloc.hpp index ee47e0e6..b419f57e 100644 --- a/include/boost/thread/win32/thread_heap_alloc.hpp +++ b/include/boost/thread/win32/thread_heap_alloc.hpp @@ -84,6 +84,23 @@ namespace boost } } +#ifdef BOOST_HAS_RVALUE_REFS + template + T* heap_new(A1&& a1) + { + void* const heap_memory=allocate_raw_heap_memory(sizeof(T)); + try + { + T* const data=new (heap_memory) T(static_cast(a1)); + return data; + } + catch(...) + { + free_raw_heap_memory(heap_memory); + throw; + } + } +#else template T* heap_new(A1 a1) { @@ -99,7 +116,7 @@ namespace boost throw; } } - +#endif template T* heap_new(A1 a1,A2 a2) { From 4a4f87e0178c1f28c9fb3637df89461e8479f17d Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Sun, 18 May 2008 09:10:20 +0000 Subject: [PATCH 131/214] support for a generic lock() function [SVN r45481] --- include/boost/thread/locks.hpp | 258 +++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index b26b4aed..e2a9e6eb 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -631,6 +631,264 @@ namespace boost typedef typename base::bool_type bool_type; using base::operator bool_type; }; + + template + unsigned try_lock_internal(MutexType1& m1,MutexType2& m2) + { + boost::unique_lock l1(m1,boost::try_to_lock); + if(!l1) + { + return 1; + } + if(!m2.try_lock()) + { + return 2; + } + l1.release(); + return 0; + } + + template + unsigned try_lock_internal(MutexType1& m1,MutexType2& m2,MutexType3& m3) + { + boost::unique_lock l1(m1,boost::try_to_lock); + if(!l1) + { + return 1; + } + if(unsigned const failed_lock=try_lock_internal(m2,m3)) + { + return failed_lock+1; + } + l1.release(); + return 0; + } + + + template + unsigned try_lock_internal(MutexType1& m1,MutexType2& m2,MutexType3& m3, + MutexType4& m4) + { + boost::unique_lock l1(m1,boost::try_to_lock); + if(!l1) + { + return 1; + } + if(unsigned const failed_lock=try_lock_internal(m2,m3,m4)) + { + return failed_lock+1; + } + l1.release(); + return 0; + } + + template + unsigned try_lock_internal(MutexType1& m1,MutexType2& m2,MutexType3& m3, + MutexType4& m4,MutexType5& m5) + { + boost::unique_lock l1(m1,boost::try_to_lock); + if(!l1) + { + return 1; + } + if(unsigned const failed_lock=try_lock_internal(m2,m3,m4,m5)) + { + return failed_lock+1; + } + l1.release(); + return 0; + } + + + template + unsigned lock_helper(MutexType1& m1,MutexType2& m2) + { + boost::unique_lock l1(m1); + if(!m2.try_lock()) + { + return 1; + } + l1.release(); + return 0; + } + + template + unsigned lock_helper(MutexType1& m1,MutexType2& m2,MutexType3& m3) + { + boost::unique_lock l1(m1); + if(unsigned const failed_lock=try_lock_internal(m2,m3)) + { + return failed_lock; + } + l1.release(); + return 0; + } + + template + unsigned lock_helper(MutexType1& m1,MutexType2& m2,MutexType3& m3, + MutexType4& m4) + { + boost::unique_lock l1(m1); + if(unsigned const failed_lock=try_lock_internal(m2,m3,m4)) + { + return failed_lock; + } + l1.release(); + return 0; + } + + template + unsigned lock_helper(MutexType1& m1,MutexType2& m2,MutexType3& m3, + MutexType4& m4,MutexType5& m5) + { + boost::unique_lock l1(m1); + if(unsigned const failed_lock=try_lock_internal(m2,m3,m4,m5)) + { + return failed_lock; + } + l1.release(); + return 0; + } + } + + template + void lock(MutexType1& m1,MutexType2& m2) + { + unsigned const lock_count=2; + unsigned lock_first=0; + while(true) + { + switch(lock_first) + { + case 0: + lock_first=detail::lock_helper(m1,m2); + if(!lock_first) + return; + break; + case 1: + lock_first=detail::lock_helper(m2,m1); + if(!lock_first) + return; + lock_first=(lock_first+1)%lock_count; + break; + } + } + } + + template + void lock(MutexType1& m1,MutexType2& m2,MutexType3& m3) + { + unsigned const lock_count=3; + unsigned lock_first=0; + while(true) + { + switch(lock_first) + { + case 0: + lock_first=detail::lock_helper(m1,m2,m3); + if(!lock_first) + return; + break; + case 1: + lock_first=detail::lock_helper(m2,m3,m1); + if(!lock_first) + return; + lock_first=(lock_first+1)%lock_count; + break; + case 2: + lock_first=detail::lock_helper(m3,m1,m2); + if(!lock_first) + return; + lock_first=(lock_first+2)%lock_count; + break; + } + } + } + + template + void lock(MutexType1& m1,MutexType2& m2,MutexType3& m3, + MutexType4& m4) + { + unsigned const lock_count=4; + unsigned lock_first=0; + while(true) + { + switch(lock_first) + { + case 0: + lock_first=detail::lock_helper(m1,m2,m3,m4); + if(!lock_first) + return; + break; + case 1: + lock_first=detail::lock_helper(m2,m3,m4,m1); + if(!lock_first) + return; + lock_first=(lock_first+1)%lock_count; + break; + case 2: + lock_first=detail::lock_helper(m3,m4,m1,m2); + if(!lock_first) + return; + lock_first=(lock_first+2)%lock_count; + break; + case 3: + lock_first=detail::lock_helper(m4,m1,m2,m3); + if(!lock_first) + return; + lock_first=(lock_first+3)%lock_count; + break; + } + } + } + + template + void lock(MutexType1& m1,MutexType2& m2,MutexType3& m3, + MutexType4& m4,MutexType5& m5) + { + unsigned const lock_count=5; + unsigned lock_first=0; + while(true) + { + switch(lock_first) + { + case 0: + lock_first=detail::lock_helper(m1,m2,m3,m4,m5); + if(!lock_first) + return; + break; + case 1: + lock_first=detail::lock_helper(m2,m3,m4,m5,m1); + if(!lock_first) + return; + lock_first=(lock_first+1)%lock_count; + break; + case 2: + lock_first=detail::lock_helper(m3,m4,m5,m1,m2); + if(!lock_first) + return; + lock_first=(lock_first+2)%lock_count; + break; + case 3: + lock_first=detail::lock_helper(m4,m5,m1,m2,m3); + if(!lock_first) + return; + lock_first=(lock_first+3)%lock_count; + break; + case 4: + lock_first=detail::lock_helper(m5,m1,m2,m3,m4); + if(!lock_first) + return; + lock_first=(lock_first+4)%lock_count; + break; + } + } } } From 61b940b705802017103ddf5b6209ab985d55bb88 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 21 May 2008 09:51:53 +0000 Subject: [PATCH 132/214] Renamed namespace user to user_test_ns to try and avoid a name clash on some platforms [SVN r45601] --- test/test_move_function.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_move_function.cpp b/test/test_move_function.cpp index 61e08b77..6400ada8 100644 --- a/test/test_move_function.cpp +++ b/test/test_move_function.cpp @@ -80,7 +80,7 @@ void test_unique_lock_move_from_rvalue_on_construction() BOOST_CHECK(l.mutex()==&m); } -namespace user +namespace user_test_ns { template T move(T& t) @@ -103,9 +103,9 @@ namespace user void test_move_for_user_defined_type_unaffected() { - user::nc src; - user::nc dest=move(src); - BOOST_CHECK(user::move_called); + user_test_ns::nc src; + user_test_ns::nc dest=move(src); + BOOST_CHECK(user_test_ns::move_called); } boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) From 2ac2eb2a614e4e6b590592aaefe187008073a2ce Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 21 May 2008 10:39:47 +0000 Subject: [PATCH 133/214] try_lock_wrapper has its own operator bool_type to avoid problems with a using declaration [SVN r45602] --- include/boost/thread/locks.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index e2a9e6eb..0fe66bb6 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -629,7 +629,10 @@ namespace boost using base::mutex; using base::release; typedef typename base::bool_type bool_type; - using base::operator bool_type; + operator bool_type() const + { + return static_cast(*this); + } }; template From 080654e3ef22c4f0fa550861d6cf74d5cd935e59 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 21 May 2008 13:38:04 +0000 Subject: [PATCH 134/214] New tests for a normal function with one argument, and a member function with 0 or 1 arguments [SVN r45607] --- test/test_thread_launching.cpp | 60 +++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/test/test_thread_launching.cpp b/test/test_thread_launching.cpp index 0e03d69d..7a4fd38c 100644 --- a/test/test_thread_launching.cpp +++ b/test/test_thread_launching.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2007 Anthony Williams +// Copyright (C) 2007-8 Anthony Williams // // 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) @@ -23,6 +23,20 @@ void test_thread_function_no_arguments() BOOST_CHECK(normal_function_called); } +int nfoa_res=0; + +void normal_function_one_arg(int i) +{ + nfoa_res=i; +} + +void test_thread_function_one_argument() +{ + boost::thread function(normal_function_one_arg,42); + function.join(); + BOOST_CHECK_EQUAL(42,nfoa_res); +} + struct callable_no_args { static bool called; @@ -150,6 +164,47 @@ void test_thread_callable_object_multiple_arguments() BOOST_CHECK_EQUAL(callable_multiple_arg::called_two_arg2,dbl); } +struct X +{ + bool function_called; + int arg_value; + + X(): + function_called(false), + arg_value(0) + {} + + + void f0() + { + function_called=true; + } + + void f1(int i) + { + arg_value=i; + } + +}; + +void test_thread_member_function_no_arguments() +{ + X x; + + boost::thread function(&X::f0,&x); + function.join(); + BOOST_CHECK(x.function_called); +} + + +void test_thread_member_function_one_argument() +{ + X x; + boost::thread function(&X::f1,&x,42); + function.join(); + BOOST_CHECK_EQUAL(42,x.arg_value); +} + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { @@ -157,9 +212,12 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) BOOST_TEST_SUITE("Boost.Threads: thread launching test suite"); test->add(BOOST_TEST_CASE(test_thread_function_no_arguments)); + test->add(BOOST_TEST_CASE(test_thread_function_one_argument)); test->add(BOOST_TEST_CASE(test_thread_callable_object_no_arguments)); test->add(BOOST_TEST_CASE(test_thread_callable_object_ref_no_arguments)); test->add(BOOST_TEST_CASE(test_thread_callable_object_one_argument)); test->add(BOOST_TEST_CASE(test_thread_callable_object_multiple_arguments)); + test->add(BOOST_TEST_CASE(test_thread_member_function_no_arguments)); + test->add(BOOST_TEST_CASE(test_thread_member_function_one_argument)); return test; } From 01f99da03a70c906e6bef58a12c15501a1999d3e Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Wed, 21 May 2008 20:44:08 +0000 Subject: [PATCH 135/214] Extended boost::thread to 9 arguments, hopefully fixed member function ambiguity. [SVN r45621] --- include/boost/thread/pthread/thread.hpp | 50 ++++++++- include/boost/thread/win32/thread.hpp | 50 ++++++++- test/Jamfile.v2 | 15 +-- test/test_thread_mf.cpp | 136 ++++++++++++++++++++++++ 4 files changed, 238 insertions(+), 13 deletions(-) create mode 100644 test/test_thread_mf.cpp diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index cdfade0e..74500c4f 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -166,19 +166,63 @@ namespace boost template thread(F f,A1 a1): - thread_info(make_thread_info(boost::bind(f,a1))) + thread_info(make_thread_info(boost::bind(boost::type(),f,a1))) { start_thread(); } + template thread(F f,A1 a1,A2 a2): - thread_info(make_thread_info(boost::bind(f,a1,a2))) + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2))) { start_thread(); } + template thread(F f,A1 a1,A2 a2,A3 a3): - thread_info(make_thread_info(boost::bind(f,a1,a2,a3))) + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3))) + { + start_thread(); + } + + template + thread(F f,A1 a1,A2 a2,A3 a3,A4 a4): + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4))) + { + start_thread(); + } + + template + thread(F f,A1 a1,A2 a2,A3 a3,A4 a4,A5 a5): + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4,a5))) + { + start_thread(); + } + + template + thread(F f,A1 a1,A2 a2,A3 a3,A4 a4,A5 a5,A6 a6): + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4,a5,a6))) + { + start_thread(); + } + + template + thread(F f,A1 a1,A2 a2,A3 a3,A4 a4,A5 a5,A6 a6,A7 a7): + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4,a5,a6,a7))) + { + start_thread(); + } + + template + thread(F f,A1 a1,A2 a2,A3 a3,A4 a4,A5 a5,A6 a6,A7 a7,A8 a8): + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4,a5,a6,a7,a8))) + { + start_thread(); + } + + template + thread(F f,A1 a1,A2 a2,A3 a3,A4 a4,A5 a5,A6 a6,A7 a7,A8 a8,A9 a9): + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4,a5,a6,a7,a8,a9))) { start_thread(); } diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index ab881c9e..e9aef652 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -253,19 +253,63 @@ namespace boost template thread(F f,A1 a1): - thread_info(make_thread_info(boost::bind(f,a1))) + thread_info(make_thread_info(boost::bind(boost::type(),f,a1))) { start_thread(); } + template thread(F f,A1 a1,A2 a2): - thread_info(make_thread_info(boost::bind(f,a1,a2))) + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2))) { start_thread(); } + template thread(F f,A1 a1,A2 a2,A3 a3): - thread_info(make_thread_info(boost::bind(f,a1,a2,a3))) + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3))) + { + start_thread(); + } + + template + thread(F f,A1 a1,A2 a2,A3 a3,A4 a4): + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4))) + { + start_thread(); + } + + template + thread(F f,A1 a1,A2 a2,A3 a3,A4 a4,A5 a5): + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4,a5))) + { + start_thread(); + } + + template + thread(F f,A1 a1,A2 a2,A3 a3,A4 a4,A5 a5,A6 a6): + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4,a5,a6))) + { + start_thread(); + } + + template + thread(F f,A1 a1,A2 a2,A3 a3,A4 a4,A5 a5,A6 a6,A7 a7): + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4,a5,a6,a7))) + { + start_thread(); + } + + template + thread(F f,A1 a1,A2 a2,A3 a3,A4 a4,A5 a5,A6 a6,A7 a7,A8 a8): + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4,a5,a6,a7,a8))) + { + start_thread(); + } + + template + thread(F f,A1 a1,A2 a2,A3 a3,A4 a4,A5 a5,A6 a6,A7 a7,A8 a8,A9 a9): + thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4,a5,a6,a7,a8,a9))) { start_thread(); } diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 405e83bc..7811c740 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -22,16 +22,16 @@ project : requirements /boost/test//boost_unit_test_framework/static multi ; - + rule thread-run ( sources ) { return - [ run $(sources) ../build//boost_thread ] - [ run $(sources) ../src/tss_null.cpp ../build//boost_thread/static - : : : : $(sources[1]:B)_lib ] - ; + [ run $(sources) ../build//boost_thread ] + [ run $(sources) ../src/tss_null.cpp ../build//boost_thread/static + : : : : $(sources[1]:B)_lib ] + ; } - + { test-suite "threads" : [ thread-run test_thread.cpp ] @@ -39,6 +39,7 @@ rule thread-run ( sources ) [ thread-run test_hardware_concurrency.cpp ] [ thread-run test_thread_move.cpp ] [ thread-run test_thread_launching.cpp ] + [ thread-run test_thread_mf.cpp ] [ thread-run test_move_function.cpp ] [ thread-run test_mutex.cpp ] [ thread-run test_condition_notify_one.cpp ] @@ -48,7 +49,7 @@ rule thread-run ( sources ) [ thread-run test_tss.cpp ] [ thread-run test_once.cpp ] [ thread-run test_xtime.cpp ] - [ thread-run test_barrier.cpp ] + [ thread-run test_barrier.cpp ] [ thread-run test_shared_mutex.cpp ] [ thread-run test_shared_mutex_part_2.cpp ] [ thread-run test_shared_mutex_timed_locks.cpp ] diff --git a/test/test_thread_mf.cpp b/test/test_thread_mf.cpp new file mode 100644 index 00000000..fa1f8a9e --- /dev/null +++ b/test/test_thread_mf.cpp @@ -0,0 +1,136 @@ +// +// Copyright (C) 2008 Peter Dimov +// +// 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 +// + +#include +#include + +struct X +{ + mutable unsigned int hash; + + X(): hash(0) {} + + int f0() { f1(17); return 0; } + int g0() const { g1(17); return 0; } + + int f1(int a1) { hash = (hash * 17041 + a1) % 32768; return 0; } + int g1(int a1) const { hash = (hash * 17041 + a1 * 2) % 32768; return 0; } + + int f2(int a1, int a2) { f1(a1); f1(a2); return 0; } + int g2(int a1, int a2) const { g1(a1); g1(a2); return 0; } + + int f3(int a1, int a2, int a3) { f2(a1, a2); f1(a3); return 0; } + int g3(int a1, int a2, int a3) const { g2(a1, a2); g1(a3); return 0; } + + int f4(int a1, int a2, int a3, int a4) { f3(a1, a2, a3); f1(a4); return 0; } + int g4(int a1, int a2, int a3, int a4) const { g3(a1, a2, a3); g1(a4); return 0; } + + int f5(int a1, int a2, int a3, int a4, int a5) { f4(a1, a2, a3, a4); f1(a5); return 0; } + int g5(int a1, int a2, int a3, int a4, int a5) const { g4(a1, a2, a3, a4); g1(a5); return 0; } + + int f6(int a1, int a2, int a3, int a4, int a5, int a6) { f5(a1, a2, a3, a4, a5); f1(a6); return 0; } + int g6(int a1, int a2, int a3, int a4, int a5, int a6) const { g5(a1, a2, a3, a4, a5); g1(a6); return 0; } + + int f7(int a1, int a2, int a3, int a4, int a5, int a6, int a7) { f6(a1, a2, a3, a4, a5, a6); f1(a7); return 0; } + int g7(int a1, int a2, int a3, int a4, int a5, int a6, int a7) const { g6(a1, a2, a3, a4, a5, a6); g1(a7); return 0; } + + int f8(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) { f7(a1, a2, a3, a4, a5, a6, a7); f1(a8); return 0; } + int g8(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) const { g7(a1, a2, a3, a4, a5, a6, a7); g1(a8); return 0; } +}; + +int main() +{ + using namespace boost; + + X x; + + // 0 + + thread( &X::f0, &x ).join(); + thread( &X::f0, ref(x) ).join(); + + thread( &X::g0, &x ).join(); + thread( &X::g0, x ).join(); + thread( &X::g0, ref(x) ).join(); + + // 1 + + thread( &X::f1, &x, 1 ).join(); + thread( &X::f1, ref(x), 1 ).join(); + + thread( &X::g1, &x, 1 ).join(); + thread( &X::g1, x, 1 ).join(); + thread( &X::g1, ref(x), 1 ).join(); + + // 2 + + thread( &X::f2, &x, 1, 2 ).join(); + thread( &X::f2, ref(x), 1, 2 ).join(); + + thread( &X::g2, &x, 1, 2 ).join(); + thread( &X::g2, x, 1, 2 ).join(); + thread( &X::g2, ref(x), 1, 2 ).join(); + + // 3 + + thread( &X::f3, &x, 1, 2, 3 ).join(); + thread( &X::f3, ref(x), 1, 2, 3 ).join(); + + thread( &X::g3, &x, 1, 2, 3 ).join(); + thread( &X::g3, x, 1, 2, 3 ).join(); + thread( &X::g3, ref(x), 1, 2, 3 ).join(); + + // 4 + + thread( &X::f4, &x, 1, 2, 3, 4 ).join(); + thread( &X::f4, ref(x), 1, 2, 3, 4 ).join(); + + thread( &X::g4, &x, 1, 2, 3, 4 ).join(); + thread( &X::g4, x, 1, 2, 3, 4 ).join(); + thread( &X::g4, ref(x), 1, 2, 3, 4 ).join(); + + // 5 + + thread( &X::f5, &x, 1, 2, 3, 4, 5 ).join(); + thread( &X::f5, ref(x), 1, 2, 3, 4, 5 ).join(); + + thread( &X::g5, &x, 1, 2, 3, 4, 5 ).join(); + thread( &X::g5, x, 1, 2, 3, 4, 5 ).join(); + thread( &X::g5, ref(x), 1, 2, 3, 4, 5 ).join(); + + // 6 + + thread( &X::f6, &x, 1, 2, 3, 4, 5, 6 ).join(); + thread( &X::f6, ref(x), 1, 2, 3, 4, 5, 6 ).join(); + + thread( &X::g6, &x, 1, 2, 3, 4, 5, 6 ).join(); + thread( &X::g6, x, 1, 2, 3, 4, 5, 6 ).join(); + thread( &X::g6, ref(x), 1, 2, 3, 4, 5, 6 ).join(); + + // 7 + + thread( &X::f7, &x, 1, 2, 3, 4, 5, 6, 7).join(); + thread( &X::f7, ref(x), 1, 2, 3, 4, 5, 6, 7).join(); + + thread( &X::g7, &x, 1, 2, 3, 4, 5, 6, 7).join(); + thread( &X::g7, x, 1, 2, 3, 4, 5, 6, 7).join(); + thread( &X::g7, ref(x), 1, 2, 3, 4, 5, 6, 7).join(); + + // 8 + + thread( &X::f8, &x, 1, 2, 3, 4, 5, 6, 7, 8 ).join(); + thread( &X::f8, ref(x), 1, 2, 3, 4, 5, 6, 7, 8 ).join(); + + thread( &X::g8, &x, 1, 2, 3, 4, 5, 6, 7, 8 ).join(); + thread( &X::g8, x, 1, 2, 3, 4, 5, 6, 7, 8 ).join(); + thread( &X::g8, ref(x), 1, 2, 3, 4, 5, 6, 7, 8 ).join(); + + BOOST_TEST( x.hash == 23558 ); + + return report_errors(); +} From 8831b13efca409810e831e5f3db257f5eea7317f Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 21 May 2008 21:10:45 +0000 Subject: [PATCH 136/214] Use lock_guard<> instead of unique_lock<> internally. Clear out generations after notify_all, as they're all notified [SVN r45625] --- include/boost/thread/win32/condition_variable.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index c74d3cc4..c9121551 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -206,7 +206,7 @@ namespace boost { if(detail::interlocked_read_acquire(&total_count)) { - boost::mutex::scoped_lock internal_lock(internal_mutex); + boost::lock_guard internal_lock(internal_mutex); if(!total_count) { return; @@ -227,7 +227,7 @@ namespace boost { if(detail::interlocked_read_acquire(&total_count)) { - boost::mutex::scoped_lock internal_lock(internal_mutex); + boost::lock_guard internal_lock(internal_mutex); if(!total_count) { return; @@ -239,6 +239,7 @@ namespace boost { (*it)->release(detail::interlocked_read_acquire(&(*it)->waiters)); } + generations.clear(); wake_sem=detail::win32::handle(0); } } From 94d89aac5fdf86ff98a8dbf8d96917e1f3fc28de Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 21 May 2008 21:11:30 +0000 Subject: [PATCH 137/214] more rvalue reference stuff [SVN r45626] --- include/boost/thread/win32/thread.hpp | 61 +++++++++++++++++++++++---- src/win32/thread.cpp | 3 +- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index e9aef652..c9e489eb 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -3,8 +3,8 @@ // 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) -// (C) Copyright 2007 Anthony Williams - +// (C) Copyright 2007-8 Anthony Williams + #include #include #include @@ -38,6 +38,10 @@ namespace boost { struct thread_exit_callback_node; struct tss_data_node; + + struct thread_data_base; + void intrusive_ptr_add_ref(thread_data_base * p); + void intrusive_ptr_release(thread_data_base * p); struct thread_data_base { @@ -235,6 +239,24 @@ namespace boost { start_thread(); } + + thread(thread&& other) + { + thread_info.swap(other.thread_info); + } + + thread& operator=(thread&& other) + { + thread_info=other.thread_info; + other.thread_info.reset(); + return *this; + } + + thread&& move() + { + return static_cast(*this); + } + #else template explicit thread(F f): @@ -249,6 +271,12 @@ namespace boost { start_thread(); } + + thread(detail::thread_move_t x); + thread& operator=(detail::thread_move_t x); + operator detail::thread_move_t(); + detail::thread_move_t move(); + #endif template @@ -257,7 +285,6 @@ namespace boost { start_thread(); } - template thread(F f,A1 a1,A2 a2): thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2))) @@ -314,11 +341,6 @@ namespace boost start_thread(); } - thread(detail::thread_move_t x); - thread& operator=(detail::thread_move_t x); - operator detail::thread_move_t(); - detail::thread_move_t move(); - void swap(thread& x); class id; @@ -353,10 +375,17 @@ namespace boost bool interruption_requested() const; }; +#ifdef BOOST_HAS_RVALUE_REFS + inline thread&& move(thread&& t) + { + return t; + } +#else inline detail::thread_move_t move(detail::thread_move_t t) { return t; } +#endif template struct thread::thread_data >: @@ -373,6 +402,22 @@ namespace boost f(); } }; + + template + struct thread::thread_data >: + detail::thread_data_base + { + F& f; + + thread_data(const boost::reference_wrapper f_): + f(f_) + {} + + void run() + { + f(); + } + }; namespace this_thread diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index 40c3588b..dda8b4ab 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -247,9 +247,9 @@ namespace boost detach(); } +#ifndef BOOST_HAS_RVALUE_REFS thread::thread(detail::thread_move_t x) { - lock_guard lock(x->thread_info_mutex); thread_info=x->thread_info; x->thread_info=0; } @@ -271,6 +271,7 @@ namespace boost detail::thread_move_t x(*this); return x; } +#endif void thread::swap(thread& x) { From afecfd7c2dd86db4c59b36e3bee53dc6254dd0e8 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 22 May 2008 11:49:48 +0000 Subject: [PATCH 138/214] Refactored boost::thread code to try and remove duplication [SVN r45647] --- .../boost/thread/{win32 => detail}/thread.hpp | 188 +-------- .../boost/thread/detail/thread_heap_alloc.hpp | 23 + include/boost/thread/exceptions.hpp | 6 +- include/boost/thread/pthread/thread.hpp | 396 ------------------ include/boost/thread/pthread/thread_data.hpp | 26 +- .../thread/pthread/thread_heap_alloc.hpp | 239 +++++++++++ include/boost/thread/thread.hpp | 9 +- include/boost/thread/win32/thread_data.hpp | 175 ++++++++ .../boost/thread/win32/thread_heap_alloc.hpp | 224 +++++++++- src/pthread/thread.cpp | 232 +++++----- src/win32/thread.cpp | 53 +-- 11 files changed, 835 insertions(+), 736 deletions(-) rename include/boost/thread/{win32 => detail}/thread.hpp (70%) create mode 100644 include/boost/thread/detail/thread_heap_alloc.hpp delete mode 100644 include/boost/thread/pthread/thread.hpp create mode 100644 include/boost/thread/pthread/thread_heap_alloc.hpp create mode 100644 include/boost/thread/win32/thread_data.hpp diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/detail/thread.hpp similarity index 70% rename from include/boost/thread/win32/thread.hpp rename to include/boost/thread/detail/thread.hpp index c9e489eb..9c13e607 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/detail/thread.hpp @@ -1,19 +1,16 @@ -#ifndef BOOST_THREAD_THREAD_WIN32_HPP -#define BOOST_THREAD_THREAD_WIN32_HPP +#ifndef BOOST_THREAD_THREAD_COMMON_HPP +#define BOOST_THREAD_THREAD_COMMON_HPP // 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) // (C) Copyright 2007-8 Anthony Williams -#include #include #include #include -#include #include -#include -#include "thread_primitives.hpp" -#include "thread_heap_alloc.hpp" +#include +#include #include #include #include @@ -31,139 +28,6 @@ namespace boost { - class thread_interrupted - {}; - - namespace detail - { - struct thread_exit_callback_node; - struct tss_data_node; - - struct thread_data_base; - void intrusive_ptr_add_ref(thread_data_base * p); - void intrusive_ptr_release(thread_data_base * p); - - struct thread_data_base - { - long count; - detail::win32::handle_manager thread_handle; - detail::win32::handle_manager interruption_handle; - boost::detail::thread_exit_callback_node* thread_exit_callbacks; - boost::detail::tss_data_node* tss_data; - bool interruption_enabled; - unsigned id; - - thread_data_base(): - count(0),thread_handle(detail::win32::invalid_handle_value), - interruption_handle(create_anonymous_event(detail::win32::manual_reset_event,detail::win32::event_initially_reset)), - thread_exit_callbacks(0),tss_data(0), - interruption_enabled(true), - id(0) - {} - virtual ~thread_data_base() - {} - - friend void intrusive_ptr_add_ref(thread_data_base * p) - { - BOOST_INTERLOCKED_INCREMENT(&p->count); - } - - friend void intrusive_ptr_release(thread_data_base * p) - { - if(!BOOST_INTERLOCKED_DECREMENT(&p->count)) - { - detail::heap_delete(p); - } - } - - void interrupt() - { - BOOST_VERIFY(detail::win32::SetEvent(interruption_handle)!=0); - } - - - virtual void run()=0; - }; - - typedef boost::intrusive_ptr thread_data_ptr; - - struct timeout - { - unsigned long start; - uintmax_t milliseconds; - bool relative; - boost::system_time abs_time; - - static unsigned long const max_non_infinite_wait=0xfffffffe; - - timeout(uintmax_t milliseconds_): - start(win32::GetTickCount()), - milliseconds(milliseconds_), - relative(true), - abs_time(boost::get_system_time()) - {} - - timeout(boost::system_time const& abs_time_): - start(win32::GetTickCount()), - milliseconds(0), - relative(false), - abs_time(abs_time_) - {} - - struct remaining_time - { - bool more; - unsigned long milliseconds; - - remaining_time(uintmax_t remaining): - more(remaining>max_non_infinite_wait), - milliseconds(more?max_non_infinite_wait:(unsigned long)remaining) - {} - }; - - remaining_time remaining_milliseconds() const - { - if(is_sentinel()) - { - return remaining_time(win32::infinite); - } - else if(relative) - { - unsigned long const now=win32::GetTickCount(); - unsigned long const elapsed=now-start; - return remaining_time((elapsed static inline detail::thread_data_ptr make_thread_info(F&& f) { - return detail::heap_new >(static_cast(f)); + return detail::thread_data_ptr(detail::heap_new >(static_cast(f))); } #else template static inline detail::thread_data_ptr make_thread_info(F f) { - return detail::heap_new >(f); + return detail::thread_data_ptr(detail::heap_new >(f)); } template static inline detail::thread_data_ptr make_thread_info(boost::detail::thread_move_t f) { - return detail::heap_new >(f); + return detail::thread_data_ptr(detail::heap_new >(f)); } #endif public: @@ -360,15 +222,22 @@ namespace boost static unsigned hardware_concurrency(); - typedef detail::win32::handle native_handle_type; + typedef detail::thread_data_base::native_handle_type native_handle_type; native_handle_type native_handle(); // backwards compatibility bool operator==(const thread& other) const; bool operator!=(const thread& other) const; - static void yield(); - static void sleep(const system_time& xt); + static inline void yield() + { + this_thread::yield(); + } + + static inline void sleep(const system_time& xt) + { + this_thread::sleep(xt); + } // extensions void interrupt(); @@ -445,21 +314,13 @@ namespace boost thread::id BOOST_THREAD_DECL get_id(); - bool BOOST_THREAD_DECL interruptible_wait(detail::win32::handle handle_to_wait_for,detail::timeout target_time); - inline bool interruptible_wait(unsigned long milliseconds) - { - return interruptible_wait(detail::win32::invalid_handle_value,milliseconds); - } - void BOOST_THREAD_DECL interruption_point(); bool BOOST_THREAD_DECL interruption_enabled(); bool BOOST_THREAD_DECL interruption_requested(); - void BOOST_THREAD_DECL yield(); - template - void sleep(TimeDuration const& rel_time) + inline void sleep(xtime const& abs_time) { - interruptible_wait(static_cast(rel_time.total_milliseconds())); + sleep(system_time(abs_time)); } } @@ -475,7 +336,7 @@ namespace boost friend id this_thread::get_id(); public: id(): - thread_data(0) + thread_data() {} bool operator==(const id& y) const @@ -521,15 +382,6 @@ namespace boost return os<<"{Not-any-thread}"; } } - - void interrupt() - { - if(thread_data) - { - thread_data->interrupt(); - } - } - }; inline bool thread::operator==(const thread& other) const diff --git a/include/boost/thread/detail/thread_heap_alloc.hpp b/include/boost/thread/detail/thread_heap_alloc.hpp new file mode 100644 index 00000000..2f9bfd5c --- /dev/null +++ b/include/boost/thread/detail/thread_heap_alloc.hpp @@ -0,0 +1,23 @@ +#ifndef BOOST_THREAD_THREAD_HEAP_ALLOC_HPP +#define BOOST_THREAD_THREAD_HEAP_ALLOC_HPP + +// thread_heap_alloc.hpp +// +// (C) Copyright 2008 Anthony Williams +// +// 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) + +#include + +#if defined(BOOST_THREAD_PLATFORM_WIN32) +#include +#elif defined(BOOST_THREAD_PLATFORM_PTHREAD) +#include +#else +#error "Boost threads unavailable on this platform" +#endif + + +#endif diff --git a/include/boost/thread/exceptions.hpp b/include/boost/thread/exceptions.hpp index e3ff59a5..e46b30d2 100644 --- a/include/boost/thread/exceptions.hpp +++ b/include/boost/thread/exceptions.hpp @@ -19,7 +19,11 @@ #include #include -namespace boost { +namespace boost +{ + + class BOOST_THREAD_DECL thread_interrupted + {}; class BOOST_THREAD_DECL thread_exception : public std::exception { diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp deleted file mode 100644 index 74500c4f..00000000 --- a/include/boost/thread/pthread/thread.hpp +++ /dev/null @@ -1,396 +0,0 @@ -#ifndef BOOST_THREAD_THREAD_PTHREAD_HPP -#define BOOST_THREAD_THREAD_PTHREAD_HPP -// Copyright (C) 2001-2003 -// William E. Kempf -// Copyright (C) 2007 Anthony Williams -// -// 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) - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include "thread_data.hpp" -#include -#include - -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4251) -#endif - -namespace boost -{ - class thread; - - namespace detail - { - class thread_id; - } - - namespace this_thread - { - BOOST_THREAD_DECL detail::thread_id get_id(); - } - - namespace detail - { - class thread_id - { - private: - detail::thread_data_ptr thread_data; - - thread_id(detail::thread_data_ptr thread_data_): - thread_data(thread_data_) - {} - friend class boost::thread; - friend thread_id this_thread::get_id(); - public: - thread_id(): - thread_data() - {} - - bool operator==(const thread_id& y) const - { - return thread_data==y.thread_data; - } - - bool operator!=(const thread_id& y) const - { - return thread_data!=y.thread_data; - } - - bool operator<(const thread_id& y) const - { - return thread_data(const thread_id& y) const - { - return y.thread_data=(const thread_id& y) const - { - return !(thread_data - friend std::basic_ostream& - operator<<(std::basic_ostream& os, const thread_id& x) - { - if(x.thread_data) - { - return os< - struct thread_data: - detail::thread_data_base - { - F f; - - thread_data(F f_): - f(f_) - {} - thread_data(detail::thread_move_t f_): - f(f_) - {} - - void run() - { - f(); - } - }; - - mutable boost::mutex thread_info_mutex; - detail::thread_data_ptr thread_info; - - void start_thread(); - - explicit thread(detail::thread_data_ptr data); - - detail::thread_data_ptr get_thread_info() const; - - template - static inline detail::thread_data_ptr make_thread_info(F f) - { - return detail::thread_data_ptr(new thread_data(f)); - } - - public: - thread(); - ~thread(); - - template - explicit thread(F f): - thread_info(make_thread_info(f)) - { - start_thread(); - } - template - thread(detail::thread_move_t f): - thread_info(make_thread_info(f)) - { - start_thread(); - } - - template - thread(F f,A1 a1): - thread_info(make_thread_info(boost::bind(boost::type(),f,a1))) - { - start_thread(); - } - - template - thread(F f,A1 a1,A2 a2): - thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2))) - { - start_thread(); - } - - template - thread(F f,A1 a1,A2 a2,A3 a3): - thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3))) - { - start_thread(); - } - - template - thread(F f,A1 a1,A2 a2,A3 a3,A4 a4): - thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4))) - { - start_thread(); - } - - template - thread(F f,A1 a1,A2 a2,A3 a3,A4 a4,A5 a5): - thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4,a5))) - { - start_thread(); - } - - template - thread(F f,A1 a1,A2 a2,A3 a3,A4 a4,A5 a5,A6 a6): - thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4,a5,a6))) - { - start_thread(); - } - - template - thread(F f,A1 a1,A2 a2,A3 a3,A4 a4,A5 a5,A6 a6,A7 a7): - thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4,a5,a6,a7))) - { - start_thread(); - } - - template - thread(F f,A1 a1,A2 a2,A3 a3,A4 a4,A5 a5,A6 a6,A7 a7,A8 a8): - thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4,a5,a6,a7,a8))) - { - start_thread(); - } - - template - thread(F f,A1 a1,A2 a2,A3 a3,A4 a4,A5 a5,A6 a6,A7 a7,A8 a8,A9 a9): - thread_info(make_thread_info(boost::bind(boost::type(),f,a1,a2,a3,a4,a5,a6,a7,a8,a9))) - { - start_thread(); - } - - thread(detail::thread_move_t x); - thread& operator=(detail::thread_move_t x); - operator detail::thread_move_t(); - detail::thread_move_t move(); - - void swap(thread& x); - - typedef detail::thread_id id; - - id get_id() const; - - bool joinable() const; - void join(); - bool timed_join(const system_time& wait_until); - - template - inline bool timed_join(TimeDuration const& rel_time) - { - return timed_join(get_system_time()+rel_time); - } - void detach(); - - static unsigned hardware_concurrency(); - - // backwards compatibility - bool operator==(const thread& other) const; - bool operator!=(const thread& other) const; - - static void sleep(const system_time& xt); - static void yield(); - - typedef pthread_t native_handle_type; - native_handle_type native_handle(); - - // extensions - void interrupt(); - bool interruption_requested() const; - }; - - inline detail::thread_move_t move(detail::thread_move_t t) - { - return t; - } - - template - struct thread::thread_data >: - detail::thread_data_base - { - F& f; - - thread_data(boost::reference_wrapper f_): - f(f_) - {} - - void run() - { - f(); - } - }; - - namespace this_thread - { - class BOOST_THREAD_DECL disable_interruption - { - disable_interruption(const disable_interruption&); - disable_interruption& operator=(const disable_interruption&); - - bool interruption_was_enabled; - friend class restore_interruption; - public: - disable_interruption(); - ~disable_interruption(); - }; - - class BOOST_THREAD_DECL restore_interruption - { - restore_interruption(const restore_interruption&); - restore_interruption& operator=(const restore_interruption&); - public: - explicit restore_interruption(disable_interruption& d); - ~restore_interruption(); - }; - - BOOST_THREAD_DECL thread::id get_id(); - - BOOST_THREAD_DECL void interruption_point(); - BOOST_THREAD_DECL bool interruption_enabled(); - BOOST_THREAD_DECL bool interruption_requested(); - - inline void yield() - { - thread::yield(); - } - - template - inline void sleep(TimeDuration const& rel_time) - { - thread::sleep(get_system_time()+rel_time); - } - } - - namespace detail - { - struct thread_exit_function_base - { - virtual ~thread_exit_function_base() - {} - virtual void operator()() const=0; - }; - - template - struct thread_exit_function: - thread_exit_function_base - { - F f; - - thread_exit_function(F f_): - f(f_) - {} - - void operator()() const - { - f(); - } - }; - - BOOST_THREAD_DECL void add_thread_exit_function(thread_exit_function_base*); - } - - namespace this_thread - { - template - inline void at_thread_exit(F f) - { - detail::thread_exit_function_base* const thread_exit_func=new detail::thread_exit_function(f); - detail::add_thread_exit_function(thread_exit_func); - } - } - - class BOOST_THREAD_DECL thread_group - { - public: - thread_group(); - ~thread_group(); - - thread* create_thread(const function0& threadfunc); - void add_thread(thread* thrd); - void remove_thread(thread* thrd); - void join_all(); - void interrupt_all(); - size_t size() const; - - private: - thread_group(thread_group&); - void operator=(thread_group&); - - std::list m_threads; - mutex m_mutex; - }; -} // namespace boost - -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif - - -#endif diff --git a/include/boost/thread/pthread/thread_data.hpp b/include/boost/thread/pthread/thread_data.hpp index 52ca40e5..2532b854 100644 --- a/include/boost/thread/pthread/thread_data.hpp +++ b/include/boost/thread/pthread/thread_data.hpp @@ -6,6 +6,7 @@ // (C) Copyright 2007 Anthony Williams #include +#include #include #include #include @@ -15,9 +16,8 @@ namespace boost { - class thread_interrupted - {}; - + class thread; + namespace detail { struct thread_exit_callback_node; @@ -26,7 +26,7 @@ namespace boost struct thread_data_base; typedef boost::shared_ptr thread_data_ptr; - struct thread_data_base: + struct BOOST_THREAD_DECL thread_data_base: enable_shared_from_this { thread_data_ptr self; @@ -51,8 +51,9 @@ namespace boost interrupt_requested(false), current_cond(0) {} - virtual ~thread_data_base() - {} + virtual ~thread_data_base(); + + typedef pthread_t native_handle_type; virtual void run()=0; }; @@ -95,6 +96,19 @@ namespace boost } }; } + + namespace this_thread + { + void BOOST_THREAD_DECL yield(); + + void BOOST_THREAD_DECL sleep(system_time const& abs_time); + + template + inline void sleep(TimeDuration const& rel_time) + { + this_thread::sleep(get_system_time()+rel_time); + } + } } diff --git a/include/boost/thread/pthread/thread_heap_alloc.hpp b/include/boost/thread/pthread/thread_heap_alloc.hpp new file mode 100644 index 00000000..338092da --- /dev/null +++ b/include/boost/thread/pthread/thread_heap_alloc.hpp @@ -0,0 +1,239 @@ +// 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) +// (C) Copyright 2008 Anthony Williams +#ifndef THREAD_HEAP_ALLOC_PTHREAD_HPP +#define THREAD_HEAP_ALLOC_PTHREAD_HPP + +namespace boost +{ + namespace detail + { + template + inline T* heap_new() + { + return new T(); + } + +#ifdef BOOST_HAS_RVALUE_REFS + template + inline T* heap_new(A1&& a1) + { + return new T(static_cast(a1)); + } + template + inline T* heap_new(A1&& a1,A2&& a2) + { + return new T(static_cast(a1),static_cast(a2)); + } + template + inline T* heap_new(A1&& a1,A2&& a2,A3&& a3) + { + return new T(static_cast(a1),static_cast(a2), + static_cast(a3)); + } + template + inline T* heap_new(A1&& a1,A2&& a2,A3&& a3,A4&& a4) + { + return new T(static_cast(a1),static_cast(a2), + static_cast(a3),static_cast(a4)); + } +#else + template + inline T* heap_new_impl(A1 a1) + { + return new T(a1); + } + template + inline T* heap_new_impl(A1 a1,A2 a2) + { + return new T(a1,a2); + } + template + inline T* heap_new_impl(A1 a1,A2 a2,A3 a3) + { + return new T(a1,a2,a3); + } + template + inline T* heap_new_impl(A1 a1,A2 a2,A3 a3,A4 a4) + { + return new T(a1,a2,a3,a4); + } + + template + inline T* heap_new(A1 const& a1) + { + return heap_new_impl(a1); + } + template + inline T* heap_new(A1& a1) + { + return heap_new_impl(a1); + } + + template + inline T* heap_new(A1 const& a1,A2 const& a2) + { + return heap_new_impl(a1,a2); + } + template + inline T* heap_new(A1& a1,A2 const& a2) + { + return heap_new_impl(a1,a2); + } + template + inline T* heap_new(A1 const& a1,A2& a2) + { + return heap_new_impl(a1,a2); + } + template + inline T* heap_new(A1& a1,A2& a2) + { + return heap_new_impl(a1,a2); + } + + template + inline T* heap_new(A1 const& a1,A2 const& a2,A3 const& a3) + { + return heap_new_impl(a1,a2,a3); + } + template + inline T* heap_new(A1& a1,A2 const& a2,A3 const& a3) + { + return heap_new_impl(a1,a2,a3); + } + template + inline T* heap_new(A1 const& a1,A2& a2,A3 const& a3) + { + return heap_new_impl(a1,a2,a3); + } + template + inline T* heap_new(A1& a1,A2& a2,A3 const& a3) + { + return heap_new_impl(a1,a2,a3); + } + + template + inline T* heap_new(A1 const& a1,A2 const& a2,A3& a3) + { + return heap_new_impl(a1,a2,a3); + } + template + inline T* heap_new(A1& a1,A2 const& a2,A3& a3) + { + return heap_new_impl(a1,a2,a3); + } + template + inline T* heap_new(A1 const& a1,A2& a2,A3& a3) + { + return heap_new_impl(a1,a2,a3); + } + template + inline T* heap_new(A1& a1,A2& a2,A3& a3) + { + return heap_new_impl(a1,a2,a3); + } + + template + inline T* heap_new(A1 const& a1,A2 const& a2,A3 const& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2 const& a2,A3 const& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1 const& a1,A2& a2,A3 const& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2& a2,A3 const& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + + template + inline T* heap_new(A1 const& a1,A2 const& a2,A3& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2 const& a2,A3& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1 const& a1,A2& a2,A3& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2& a2,A3& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1 const& a1,A2 const& a2,A3 const& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2 const& a2,A3 const& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1 const& a1,A2& a2,A3 const& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2& a2,A3 const& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + + template + inline T* heap_new(A1 const& a1,A2 const& a2,A3& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2 const& a2,A3& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1 const& a1,A2& a2,A3& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2& a2,A3& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + +#endif + template + inline void heap_delete(T* data) + { + delete data; + } + + template + struct do_heap_delete + { + void operator()(T* data) const + { + detail::heap_delete(data); + } + }; + } +} + + +#endif diff --git a/include/boost/thread/thread.hpp b/include/boost/thread/thread.hpp index a1714981..6146132b 100644 --- a/include/boost/thread/thread.hpp +++ b/include/boost/thread/thread.hpp @@ -3,7 +3,7 @@ // thread.hpp // -// (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007-8 Anthony Williams // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -12,11 +12,14 @@ #include #if defined(BOOST_THREAD_PLATFORM_WIN32) -#include +#include #elif defined(BOOST_THREAD_PLATFORM_PTHREAD) -#include +#include #else #error "Boost threads unavailable on this platform" #endif +#include + + #endif diff --git a/include/boost/thread/win32/thread_data.hpp b/include/boost/thread/win32/thread_data.hpp new file mode 100644 index 00000000..604569a5 --- /dev/null +++ b/include/boost/thread/win32/thread_data.hpp @@ -0,0 +1,175 @@ +#ifndef BOOST_THREAD_PTHREAD_THREAD_DATA_HPP +#define BOOST_THREAD_PTHREAD_THREAD_DATA_HPP +// 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) +// (C) Copyright 2008 Anthony Williams + +#include +#include +#include +#include "thread_primitives.hpp" +#include "thread_heap_alloc.hpp" + +namespace boost +{ + namespace detail + { + struct thread_exit_callback_node; + struct tss_data_node; + + struct thread_data_base; + void intrusive_ptr_add_ref(thread_data_base * p); + void intrusive_ptr_release(thread_data_base * p); + + struct thread_data_base + { + long count; + detail::win32::handle_manager thread_handle; + detail::win32::handle_manager interruption_handle; + boost::detail::thread_exit_callback_node* thread_exit_callbacks; + boost::detail::tss_data_node* tss_data; + bool interruption_enabled; + unsigned id; + + thread_data_base(): + count(0),thread_handle(detail::win32::invalid_handle_value), + interruption_handle(create_anonymous_event(detail::win32::manual_reset_event,detail::win32::event_initially_reset)), + thread_exit_callbacks(0),tss_data(0), + interruption_enabled(true), + id(0) + {} + virtual ~thread_data_base() + {} + + friend void intrusive_ptr_add_ref(thread_data_base * p) + { + BOOST_INTERLOCKED_INCREMENT(&p->count); + } + + friend void intrusive_ptr_release(thread_data_base * p) + { + if(!BOOST_INTERLOCKED_DECREMENT(&p->count)) + { + detail::heap_delete(p); + } + } + + void interrupt() + { + BOOST_VERIFY(detail::win32::SetEvent(interruption_handle)!=0); + } + + typedef detail::win32::handle native_handle_type; + + virtual void run()=0; + }; + + typedef boost::intrusive_ptr thread_data_ptr; + + struct timeout + { + unsigned long start; + uintmax_t milliseconds; + bool relative; + boost::system_time abs_time; + + static unsigned long const max_non_infinite_wait=0xfffffffe; + + timeout(uintmax_t milliseconds_): + start(win32::GetTickCount()), + milliseconds(milliseconds_), + relative(true), + abs_time(boost::get_system_time()) + {} + + timeout(boost::system_time const& abs_time_): + start(win32::GetTickCount()), + milliseconds(0), + relative(false), + abs_time(abs_time_) + {} + + struct remaining_time + { + bool more; + unsigned long milliseconds; + + remaining_time(uintmax_t remaining): + more(remaining>max_non_infinite_wait), + milliseconds(more?max_non_infinite_wait:(unsigned long)remaining) + {} + }; + + remaining_time remaining_milliseconds() const + { + if(is_sentinel()) + { + return remaining_time(win32::infinite); + } + else if(relative) + { + unsigned long const now=win32::GetTickCount(); + unsigned long const elapsed=now-start; + return remaining_time((elapsed + inline void sleep(TimeDuration const& rel_time) + { + interruptible_wait(static_cast(rel_time.total_milliseconds())); + } + inline void sleep(system_time const& abs_time) + { + interruptible_wait(abs_time); + } + } + +} + + +#endif diff --git a/include/boost/thread/win32/thread_heap_alloc.hpp b/include/boost/thread/win32/thread_heap_alloc.hpp index b419f57e..a669654d 100644 --- a/include/boost/thread/win32/thread_heap_alloc.hpp +++ b/include/boost/thread/win32/thread_heap_alloc.hpp @@ -69,7 +69,7 @@ namespace boost } template - T* heap_new() + inline T* heap_new() { void* const heap_memory=allocate_raw_heap_memory(sizeof(T)); try @@ -86,7 +86,7 @@ namespace boost #ifdef BOOST_HAS_RVALUE_REFS template - T* heap_new(A1&& a1) + inline T* heap_new(A1&& a1) { void* const heap_memory=allocate_raw_heap_memory(sizeof(T)); try @@ -100,9 +100,56 @@ namespace boost throw; } } + template + inline T* heap_new(A1&& a1,A2&& a2) + { + void* const heap_memory=allocate_raw_heap_memory(sizeof(T)); + try + { + T* const data=new (heap_memory) T(static_cast(a1),static_cast(a2)); + return data; + } + catch(...) + { + free_raw_heap_memory(heap_memory); + throw; + } + } + template + inline T* heap_new(A1&& a1,A2&& a2,A3&& a3) + { + void* const heap_memory=allocate_raw_heap_memory(sizeof(T)); + try + { + T* const data=new (heap_memory) T(static_cast(a1),static_cast(a2), + static_cast(a3)); + return data; + } + catch(...) + { + free_raw_heap_memory(heap_memory); + throw; + } + } + template + inline T* heap_new(A1&& a1,A2&& a2,A3&& a3,A4&& a4) + { + void* const heap_memory=allocate_raw_heap_memory(sizeof(T)); + try + { + T* const data=new (heap_memory) T(static_cast(a1),static_cast(a2), + static_cast(a3),static_cast(a4)); + return data; + } + catch(...) + { + free_raw_heap_memory(heap_memory); + throw; + } + } #else template - T* heap_new(A1 a1) + inline T* heap_new_impl(A1 a1) { void* const heap_memory=allocate_raw_heap_memory(sizeof(T)); try @@ -116,9 +163,9 @@ namespace boost throw; } } -#endif + template - T* heap_new(A1 a1,A2 a2) + inline T* heap_new_impl(A1 a1,A2 a2) { void* const heap_memory=allocate_raw_heap_memory(sizeof(T)); try @@ -134,7 +181,7 @@ namespace boost } template - T* heap_new(A1 a1,A2 a2,A3 a3) + inline T* heap_new_impl(A1 a1,A2 a2,A3 a3) { void* const heap_memory=allocate_raw_heap_memory(sizeof(T)); try @@ -148,9 +195,9 @@ namespace boost throw; } } - + template - T* heap_new(A1 a1,A2 a2,A3 a3,A4 a4) + inline T* heap_new_impl(A1 a1,A2 a2,A3 a3,A4 a4) { void* const heap_memory=allocate_raw_heap_memory(sizeof(T)); try @@ -164,9 +211,168 @@ namespace boost throw; } } + + + template + inline T* heap_new(A1 const& a1) + { + return heap_new_impl(a1); + } + template + inline T* heap_new(A1& a1) + { + return heap_new_impl(a1); + } + template + inline T* heap_new(A1 const& a1,A2 const& a2) + { + return heap_new_impl(a1,a2); + } + template + inline T* heap_new(A1& a1,A2 const& a2) + { + return heap_new_impl(a1,a2); + } + template + inline T* heap_new(A1 const& a1,A2& a2) + { + return heap_new_impl(a1,a2); + } + template + inline T* heap_new(A1& a1,A2& a2) + { + return heap_new_impl(a1,a2); + } + + template + inline T* heap_new(A1 const& a1,A2 const& a2,A3 const& a3) + { + return heap_new_impl(a1,a2,a3); + } + template + inline T* heap_new(A1& a1,A2 const& a2,A3 const& a3) + { + return heap_new_impl(a1,a2,a3); + } + template + inline T* heap_new(A1 const& a1,A2& a2,A3 const& a3) + { + return heap_new_impl(a1,a2,a3); + } + template + inline T* heap_new(A1& a1,A2& a2,A3 const& a3) + { + return heap_new_impl(a1,a2,a3); + } + + template + inline T* heap_new(A1 const& a1,A2 const& a2,A3& a3) + { + return heap_new_impl(a1,a2,a3); + } + template + inline T* heap_new(A1& a1,A2 const& a2,A3& a3) + { + return heap_new_impl(a1,a2,a3); + } + template + inline T* heap_new(A1 const& a1,A2& a2,A3& a3) + { + return heap_new_impl(a1,a2,a3); + } + template + inline T* heap_new(A1& a1,A2& a2,A3& a3) + { + return heap_new_impl(a1,a2,a3); + } + + template + inline T* heap_new(A1 const& a1,A2 const& a2,A3 const& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2 const& a2,A3 const& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1 const& a1,A2& a2,A3 const& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2& a2,A3 const& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + + template + inline T* heap_new(A1 const& a1,A2 const& a2,A3& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2 const& a2,A3& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1 const& a1,A2& a2,A3& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2& a2,A3& a3,A4 const& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1 const& a1,A2 const& a2,A3 const& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2 const& a2,A3 const& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1 const& a1,A2& a2,A3 const& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2& a2,A3 const& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + + template + inline T* heap_new(A1 const& a1,A2 const& a2,A3& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2 const& a2,A3& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1 const& a1,A2& a2,A3& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + template + inline T* heap_new(A1& a1,A2& a2,A3& a3,A4& a4) + { + return heap_new_impl(a1,a2,a3,a4); + } + +#endif template - void heap_delete(T* data) + inline void heap_delete(T* data) { data->~T(); free_raw_heap_memory(data); diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 3cd6a86a..6dfcbc7a 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -28,6 +28,9 @@ namespace boost { namespace detail { + thread_data_base::~thread_data_base() + {} + struct thread_exit_callback_node { boost::detail::thread_exit_function_base* func; @@ -119,7 +122,7 @@ namespace boost { void* thread_proxy(void* param) { - boost::shared_ptr thread_info = static_cast(param)->self; + boost::detail::thread_data_ptr thread_info = static_cast(param)->self; thread_info->self.reset(); detail::set_current_thread_data(thread_info.get()); try @@ -153,6 +156,10 @@ namespace boost void run() {} + + private: + externally_launched_thread(externally_launched_thread&); + void operator=(externally_launched_thread&); }; detail::thread_data_base* make_external_thread_data() @@ -226,17 +233,6 @@ namespace boost thread_info.swap(x.thread_info); } - - bool thread::operator==(const thread& other) const - { - return get_id()==other.get_id(); - } - - bool thread::operator!=(const thread& other) const - { - return !operator==(other); - } - detail::thread_data_ptr thread::get_thread_info() const { lock_guard l(thread_info_mutex); @@ -361,57 +357,61 @@ namespace boost } } - void thread::sleep(const system_time& st) + namespace this_thread { - detail::thread_data_base* const thread_info=detail::get_current_thread_data(); - if(thread_info) + void sleep(const system_time& st) { - unique_lock lk(thread_info->sleep_mutex); - while(thread_info->sleep_condition.timed_wait(lk,st)); - } - else - { - xtime const xt=get_xtime(st); - - for (int foo=0; foo < 5; ++foo) + detail::thread_data_base* const thread_info=detail::get_current_thread_data(); + + if(thread_info) { + unique_lock lk(thread_info->sleep_mutex); + while(thread_info->sleep_condition.timed_wait(lk,st)); + } + else + { + xtime const xt=get_xtime(st); + + for (int foo=0; foo < 5; ++foo) + { # if defined(BOOST_HAS_PTHREAD_DELAY_NP) - timespec ts; - to_timespec_duration(xt, ts); - BOOST_VERIFY(!pthread_delay_np(&ts)); + timespec ts; + to_timespec_duration(xt, ts); + BOOST_VERIFY(!pthread_delay_np(&ts)); # elif defined(BOOST_HAS_NANOSLEEP) - timespec ts; - to_timespec_duration(xt, ts); + timespec ts; + to_timespec_duration(xt, ts); - // nanosleep takes a timespec that is an offset, not - // an absolute time. - nanosleep(&ts, 0); + // nanosleep takes a timespec that is an offset, not + // an absolute time. + nanosleep(&ts, 0); # else - mutex mx; - mutex::scoped_lock lock(mx); - condition cond; - cond.timed_wait(lock, xt); + mutex mx; + mutex::scoped_lock lock(mx); + condition cond; + cond.timed_wait(lock, xt); # endif - xtime cur; - xtime_get(&cur, TIME_UTC); - if (xtime_cmp(xt, cur) <= 0) - return; + xtime cur; + xtime_get(&cur, TIME_UTC); + if (xtime_cmp(xt, cur) <= 0) + return; + } } } - } - void thread::yield() - { + void yield() + { # if defined(BOOST_HAS_SCHED_YIELD) - BOOST_VERIFY(!sched_yield()); + BOOST_VERIFY(!sched_yield()); # elif defined(BOOST_HAS_PTHREAD_YIELD) - BOOST_VERIFY(!pthread_yield()); + BOOST_VERIFY(!pthread_yield()); # else - xtime xt; - xtime_get(&xt, TIME_UTC); - sleep(xt); + xtime xt; + xtime_get(&xt, TIME_UTC); + sleep(xt); # endif + } } unsigned thread::hardware_concurrency() @@ -622,85 +622,85 @@ namespace boost } } - thread_group::thread_group() - { - } +// thread_group::thread_group() +// { +// } - thread_group::~thread_group() - { - // We shouldn't have to scoped_lock here, since referencing this object - // from another thread while we're deleting it in the current thread is - // going to lead to undefined behavior any way. - for (std::list::iterator it = m_threads.begin(); - it != m_threads.end(); ++it) - { - delete (*it); - } - } +// thread_group::~thread_group() +// { +// // We shouldn't have to scoped_lock here, since referencing this object +// // from another thread while we're deleting it in the current thread is +// // going to lead to undefined behavior any way. +// for (std::list::iterator it = m_threads.begin(); +// it != m_threads.end(); ++it) +// { +// delete (*it); +// } +// } - thread* thread_group::create_thread(const function0& threadfunc) - { - // No scoped_lock required here since the only "shared data" that's - // modified here occurs inside add_thread which does scoped_lock. - std::auto_ptr thrd(new thread(threadfunc)); - add_thread(thrd.get()); - return thrd.release(); - } +// thread* thread_group::create_thread(const function0& threadfunc) +// { +// // No scoped_lock required here since the only "shared data" that's +// // modified here occurs inside add_thread which does scoped_lock. +// std::auto_ptr thrd(new thread(threadfunc)); +// add_thread(thrd.get()); +// return thrd.release(); +// } - void thread_group::add_thread(thread* thrd) - { - mutex::scoped_lock scoped_lock(m_mutex); +// void thread_group::add_thread(thread* thrd) +// { +// mutex::scoped_lock scoped_lock(m_mutex); - // For now we'll simply ignore requests to add a thread object multiple - // times. Should we consider this an error and either throw or return an - // error value? - std::list::iterator it = std::find(m_threads.begin(), - m_threads.end(), thrd); - BOOST_ASSERT(it == m_threads.end()); - if (it == m_threads.end()) - m_threads.push_back(thrd); - } +// // For now we'll simply ignore requests to add a thread object multiple +// // times. Should we consider this an error and either throw or return an +// // error value? +// std::list::iterator it = std::find(m_threads.begin(), +// m_threads.end(), thrd); +// BOOST_ASSERT(it == m_threads.end()); +// if (it == m_threads.end()) +// m_threads.push_back(thrd); +// } - void thread_group::remove_thread(thread* thrd) - { - mutex::scoped_lock scoped_lock(m_mutex); +// void thread_group::remove_thread(thread* thrd) +// { +// mutex::scoped_lock scoped_lock(m_mutex); - // For now we'll simply ignore requests to remove a thread object that's - // not in the group. Should we consider this an error and either throw or - // return an error value? - std::list::iterator it = std::find(m_threads.begin(), - m_threads.end(), thrd); - BOOST_ASSERT(it != m_threads.end()); - if (it != m_threads.end()) - m_threads.erase(it); - } +// // For now we'll simply ignore requests to remove a thread object that's +// // not in the group. Should we consider this an error and either throw or +// // return an error value? +// std::list::iterator it = std::find(m_threads.begin(), +// m_threads.end(), thrd); +// BOOST_ASSERT(it != m_threads.end()); +// if (it != m_threads.end()) +// m_threads.erase(it); +// } - void thread_group::join_all() - { - mutex::scoped_lock scoped_lock(m_mutex); - for (std::list::iterator it = m_threads.begin(); - it != m_threads.end(); ++it) - { - (*it)->join(); - } - } +// void thread_group::join_all() +// { +// mutex::scoped_lock scoped_lock(m_mutex); +// for (std::list::iterator it = m_threads.begin(); +// it != m_threads.end(); ++it) +// { +// (*it)->join(); +// } +// } - void thread_group::interrupt_all() - { - boost::lock_guard guard(m_mutex); +// void thread_group::interrupt_all() +// { +// boost::lock_guard guard(m_mutex); - for(std::list::iterator it=m_threads.begin(),end=m_threads.end(); - it!=end; - ++it) - { - (*it)->interrupt(); - } - } +// for(std::list::iterator it=m_threads.begin(),end=m_threads.end(); +// it!=end; +// ++it) +// { +// (*it)->interrupt(); +// } +// } - size_t thread_group::size() const - { - return m_threads.size(); - } +// size_t thread_group::size() const +// { +// return m_threads.size(); +// } } diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index dda8b4ab..a6838f5e 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -81,25 +81,6 @@ namespace boost } - void thread::yield() - { - this_thread::yield(); - } - - void thread::sleep(const system_time& target) - { - system_time const now(get_system_time()); - - if(target<=now) - { - this_thread::yield(); - } - else - { - this_thread::sleep(target-now); - } - } - namespace detail { struct thread_exit_callback_node @@ -164,26 +145,24 @@ namespace boost set_current_thread_data(0); } - } - - - unsigned __stdcall thread::thread_start_function(void* param) - { - detail::thread_data_base* const thread_info(reinterpret_cast(param)); - set_current_thread_data(thread_info); - try + unsigned __stdcall thread_start_function(void* param) { - thread_info->run(); + detail::thread_data_base* const thread_info(reinterpret_cast(param)); + set_current_thread_data(thread_info); + try + { + thread_info->run(); + } + catch(thread_interrupted const&) + { + } + catch(...) + { + std::terminate(); + } + run_thread_exit_callbacks(); + return 0; } - catch(thread_interrupted const&) - { - } - catch(...) - { - std::terminate(); - } - run_thread_exit_callbacks(); - return 0; } thread::thread() From 113288e3b05545158cf00271482b2eb0bdcf8598 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 22 May 2008 16:33:34 +0000 Subject: [PATCH 139/214] tidying up move [SVN r45661] --- include/boost/thread/detail/thread.hpp | 32 ++++++++++++++++++++++---- src/pthread/thread.cpp | 30 ------------------------ src/win32/thread.cpp | 31 ------------------------- 3 files changed, 27 insertions(+), 66 deletions(-) diff --git a/include/boost/thread/detail/thread.hpp b/include/boost/thread/detail/thread.hpp index 9c13e607..35b4bb5c 100644 --- a/include/boost/thread/detail/thread.hpp +++ b/include/boost/thread/detail/thread.hpp @@ -134,10 +134,29 @@ namespace boost start_thread(); } - thread(detail::thread_move_t x); - thread& operator=(detail::thread_move_t x); - operator detail::thread_move_t(); - detail::thread_move_t move(); + thread(detail::thread_move_t x) + { + thread_info=x->thread_info; + x->thread_info=0; + } + + thread& operator=(detail::thread_move_t x) + { + thread new_thread(x); + swap(new_thread); + return *this; + } + + operator detail::thread_move_t() + { + return move(); + } + + detail::thread_move_t move() + { + detail::thread_move_t x(*this); + return x; + } #endif @@ -203,7 +222,10 @@ namespace boost start_thread(); } - void swap(thread& x); + void swap(thread& x) + { + thread_info.swap(x.thread_info); + } class id; id get_id() const; diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 6dfcbc7a..89fbf928 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -203,36 +203,6 @@ namespace boost detach(); } - thread::thread(detail::thread_move_t x) - { - lock_guard lock(x->thread_info_mutex); - thread_info=x->thread_info; - x->thread_info.reset(); - } - - thread& thread::operator=(detail::thread_move_t x) - { - thread new_thread(x); - swap(new_thread); - return *this; - } - - thread::operator detail::thread_move_t() - { - return move(); - } - - detail::thread_move_t thread::move() - { - detail::thread_move_t x(*this); - return x; - } - - void thread::swap(thread& x) - { - thread_info.swap(x.thread_info); - } - detail::thread_data_ptr thread::get_thread_info() const { lock_guard l(thread_info_mutex); diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index a6838f5e..e54b847d 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -226,37 +226,6 @@ namespace boost detach(); } -#ifndef BOOST_HAS_RVALUE_REFS - thread::thread(detail::thread_move_t x) - { - thread_info=x->thread_info; - x->thread_info=0; - } - - thread& thread::operator=(detail::thread_move_t x) - { - thread new_thread(x); - swap(new_thread); - return *this; - } - - thread::operator detail::thread_move_t() - { - return move(); - } - - detail::thread_move_t thread::move() - { - detail::thread_move_t x(*this); - return x; - } -#endif - - void thread::swap(thread& x) - { - thread_info.swap(x.thread_info); - } - thread::id thread::get_id() const { return thread::id(get_thread_info()); From d8af0d0b4e92e117a8c3f74dbce7322fe2e8c5ec Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 23 May 2008 10:48:07 +0000 Subject: [PATCH 140/214] Reset thread_info on move rather than assigning 0 [SVN r45672] --- include/boost/thread/detail/thread.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/boost/thread/detail/thread.hpp b/include/boost/thread/detail/thread.hpp index 35b4bb5c..10810ef0 100644 --- a/include/boost/thread/detail/thread.hpp +++ b/include/boost/thread/detail/thread.hpp @@ -137,15 +137,15 @@ namespace boost thread(detail::thread_move_t x) { thread_info=x->thread_info; - x->thread_info=0; + x->thread_info.reset(); } thread& operator=(detail::thread_move_t x) - { - thread new_thread(x); - swap(new_thread); - return *this; - } + { + thread new_thread(x); + swap(new_thread); + return *this; + } operator detail::thread_move_t() { From a0a0e57527ede37bb689ab135c35286cc45496ff Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 23 May 2008 13:16:01 +0000 Subject: [PATCH 141/214] Fixed #ifdef to actually use BTS primitives on MSVC 9 [SVN r45676] --- include/boost/thread/win32/thread_primitives.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/boost/thread/win32/thread_primitives.hpp b/include/boost/thread/win32/thread_primitives.hpp index 3109f142..bfeadd0c 100644 --- a/include/boost/thread/win32/thread_primitives.hpp +++ b/include/boost/thread/win32/thread_primitives.hpp @@ -279,9 +279,9 @@ namespace boost } #if defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN) -#if MSC_VER>=1400 -extern "C" unsigned char _interlockedbittestandset(long *a,long b); -extern "C" unsigned char _interlockedbittestandreset(long *a,long b); +#if _MSC_VER>=1400 +extern "C" unsigned char _interlockedbittestandset(volatile long *a,long b); +extern "C" unsigned char _interlockedbittestandreset(volatile long *a,long b); #pragma intrinsic(_interlockedbittestandset) #pragma intrinsic(_interlockedbittestandreset) @@ -294,12 +294,12 @@ namespace boost { inline bool interlocked_bit_test_and_set(long* x,long bit) { - return _interlockedbittestandset(x,bit); + return _interlockedbittestandset(x,bit)!=0; } inline bool interlocked_bit_test_and_reset(long* x,long bit) { - return _interlockedbittestandreset(x,bit); + return _interlockedbittestandreset(x,bit)!=0; } } From 4d21dd1f471789824842a8cb71d72dd56f3c3c18 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 23 May 2008 15:17:14 +0000 Subject: [PATCH 142/214] try_lock_wrapper implements operator! in order to try and avoid compiler problems [SVN r45681] --- include/boost/thread/locks.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 0fe66bb6..dee1bdf7 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -625,9 +625,14 @@ namespace boost using base::try_lock; using base::unlock; using base::owns_lock; - using base::operator!; using base::mutex; using base::release; + + bool operator!() const + { + return !this->owns_lock(); + } + typedef typename base::bool_type bool_type; operator bool_type() const { From c2661d7eb518116f70836528dd2e4153c0f4122c Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 23 May 2008 15:18:19 +0000 Subject: [PATCH 143/214] define intrusive_ptr_add_ref and intrusive_ptr_release at namespace scope rather than inline as friends in order to try and avoid compiler problems [SVN r45682] --- .../boost/thread/win32/condition_variable.hpp | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index c9121551..5efaf6c8 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -46,21 +46,11 @@ namespace boost notified=true; detail::win32::ReleaseSemaphore(semaphore,count_to_release,0); } - - friend void intrusive_ptr_add_ref(list_entry * p) - { - BOOST_INTERLOCKED_INCREMENT(&p->references); - } - - friend void intrusive_ptr_release(list_entry * p) - { - if(!BOOST_INTERLOCKED_DECREMENT(&p->references)) - { - delete p; - } - } }; + friend void intrusive_ptr_add_ref(list_entry * p); + friend void intrusive_ptr_release(list_entry * p); + typedef boost::intrusive_ptr entry_ptr; typedef std::vector generation_list; @@ -245,6 +235,20 @@ namespace boost } }; + void intrusive_ptr_add_ref(basic_condition_variable::list_entry * p) + { + BOOST_INTERLOCKED_INCREMENT(&p->references); + } + + void intrusive_ptr_release(basic_condition_variable::list_entry * p) + { + if(!BOOST_INTERLOCKED_DECREMENT(&p->references)) + { + delete p; + } + } + + } class condition_variable: From 999613c686ff6b6a39fd99ca20f61b29bcbaa8f1 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 23 May 2008 19:33:18 +0000 Subject: [PATCH 144/214] Added note about mutex not being recursive [SVN r45688] --- doc/changes.qbk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/changes.qbk b/doc/changes.qbk index 30c4a2c2..62a6630f 100644 --- a/doc/changes.qbk +++ b/doc/changes.qbk @@ -52,4 +52,6 @@ been moved to __thread_id__. * The broken `boost::read_write_mutex` has been replaced with __shared_mutex__. +* __mutex__ is now never recursive. For Boost releases prior to 1.35 __mutex__ was recursive on Windows and not on POSIX platforms. + [endsect] From e00b764454623e9958783bb0353d0d8b28ae042d Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 23 May 2008 19:53:06 +0000 Subject: [PATCH 145/214] The signature of _interlockedbittestandset changes between MSVC 2005 and MSVC 2008 [SVN r45689] --- include/boost/thread/win32/thread_primitives.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/boost/thread/win32/thread_primitives.hpp b/include/boost/thread/win32/thread_primitives.hpp index bfeadd0c..372f262d 100644 --- a/include/boost/thread/win32/thread_primitives.hpp +++ b/include/boost/thread/win32/thread_primitives.hpp @@ -280,8 +280,13 @@ namespace boost #if defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN) #if _MSC_VER>=1400 +#if _MSC_VER==1400 +extern "C" unsigned char _interlockedbittestandset(long *a,long b); +extern "C" unsigned char _interlockedbittestandreset(long *a,long b); +#else extern "C" unsigned char _interlockedbittestandset(volatile long *a,long b); extern "C" unsigned char _interlockedbittestandreset(volatile long *a,long b); +#endif #pragma intrinsic(_interlockedbittestandset) #pragma intrinsic(_interlockedbittestandreset) From 68682804096e21bb48bab2868f1a6782ce34cc77 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 26 May 2008 07:36:16 +0000 Subject: [PATCH 146/214] Try and avoid compile errors in test_thread_callable_object_one_argument [SVN r45764] --- test/test_thread_launching.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_thread_launching.cpp b/test/test_thread_launching.cpp index 7a4fd38c..24a646f0 100644 --- a/test/test_thread_launching.cpp +++ b/test/test_thread_launching.cpp @@ -96,7 +96,8 @@ int callable_one_arg::called_arg=0; void test_thread_callable_object_one_argument() { - boost::thread callable(callable_one_arg(),42); + callable_one_arg func; + boost::thread callable(func,42); callable.join(); BOOST_CHECK(callable_one_arg::called); BOOST_CHECK_EQUAL(callable_one_arg::called_arg,42); From 9ea179b0522acb256572f65e0eda718d55b4cf26 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 26 May 2008 08:40:13 +0000 Subject: [PATCH 147/214] Initial test for generic lock functions [SVN r45765] --- test/test_generic_locks.cpp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 test/test_generic_locks.cpp diff --git a/test/test_generic_locks.cpp b/test/test_generic_locks.cpp new file mode 100644 index 00000000..a74b5399 --- /dev/null +++ b/test/test_generic_locks.cpp @@ -0,0 +1,36 @@ +// (C) Copyright 2008 Anthony Williams +// 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) + +#include +#include +#include +#include +#include + +void test_lock_two_uncontended() +{ + boost::mutex m1,m2; + + boost::mutex::scoped_lock l1(m1,boost::defer_lock), + l2(m2,boost::defer_lock); + + BOOST_CHECK(!l1.owns_lock()); + BOOST_CHECK(!l2.owns_lock()); + + boost::lock(l1,l2); + + BOOST_CHECK(l1.owns_lock()); + BOOST_CHECK(l2.owns_lock()); +} + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: generic locks test suite"); + + test->add(BOOST_TEST_CASE(&test_lock_two_uncontended)); + + return test; +} From eee95fef57c660aac80ba1a107afaa18c2b8e327 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 26 May 2008 08:40:21 +0000 Subject: [PATCH 148/214] Initial test for generic lock functions [SVN r45766] --- test/Jamfile.v2 | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 7811c740..a6a769cb 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -54,6 +54,7 @@ rule thread-run ( sources ) [ thread-run test_shared_mutex_part_2.cpp ] [ thread-run test_shared_mutex_timed_locks.cpp ] [ thread-run test_lock_concept.cpp ] + [ thread-run test_generic_locks.cpp ] [ compile-fail no_implicit_move_from_lvalue_thread.cpp ] [ compile-fail no_implicit_assign_from_lvalue_thread.cpp ] ; From 8be168fd87ee32fedec0b7b93652315a5340b485 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 26 May 2008 08:59:48 +0000 Subject: [PATCH 149/214] Basic tests for lock() when other thread is acquiring locks in same or opposite order [SVN r45767] --- test/test_generic_locks.cpp | 103 +++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/test/test_generic_locks.cpp b/test/test_generic_locks.cpp index a74b5399..b0023bab 100644 --- a/test/test_generic_locks.cpp +++ b/test/test_generic_locks.cpp @@ -4,10 +4,10 @@ // http://www.boost.org/LICENSE_1_0.txt) #include -#include #include #include #include +#include void test_lock_two_uncontended() { @@ -25,12 +25,113 @@ void test_lock_two_uncontended() BOOST_CHECK(l2.owns_lock()); } +struct wait_data +{ + boost::mutex m; + bool flag; + boost::condition_variable cond; + + wait_data(): + flag(false) + {} + + void wait() + { + boost::mutex::scoped_lock l(m); + while(!flag) + { + cond.wait(l); + } + } + + template + bool timed_wait(Duration d) + { + boost::system_time const target=boost::get_system_time()+d; + + boost::mutex::scoped_lock l(m); + while(!flag) + { + if(!cond.timed_wait(l,target)) + { + return flag; + } + } + return true; + } + + void signal() + { + boost::mutex::scoped_lock l(m); + flag=true; + cond.notify_all(); + } +}; + + +void lock_mutexes_slowly(boost::mutex* m1,boost::mutex* m2,wait_data* locked,wait_data* quit) +{ + boost::lock_guard l1(*m1); + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + boost::lock_guard l2(*m2); + locked->signal(); + quit->wait(); +} + +void lock_pair(boost::mutex* m1,boost::mutex* m2) +{ + boost::lock(*m1,*m2); + boost::mutex::scoped_lock l1(*m1,boost::adopt_lock), + l2(*m2,boost::adopt_lock); +} + +void test_lock_two_other_thread_locks_in_order() +{ + boost::mutex m1,m2; + wait_data locked; + wait_data release; + + boost::thread t(lock_mutexes_slowly,&m1,&m2,&locked,&release); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + + boost::thread t2(lock_pair,&m1,&m2); + BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(1))); + + release.signal(); + + BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(1))); + + t.join(); +} + +void test_lock_two_other_thread_locks_in_opposite_order() +{ + boost::mutex m1,m2; + wait_data locked; + wait_data release; + + boost::thread t(lock_mutexes_slowly,&m1,&m2,&locked,&release); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + + boost::thread t2(lock_pair,&m2,&m1); + BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(1))); + + release.signal(); + + BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(1))); + + t.join(); +} + + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = BOOST_TEST_SUITE("Boost.Threads: generic locks test suite"); test->add(BOOST_TEST_CASE(&test_lock_two_uncontended)); + test->add(BOOST_TEST_CASE(&test_lock_two_other_thread_locks_in_order)); + test->add(BOOST_TEST_CASE(&test_lock_two_other_thread_locks_in_opposite_order)); return test; } From 28be2cfeefc7a0c18c62f69bd97158dfa564b83c Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 27 May 2008 06:32:05 +0000 Subject: [PATCH 150/214] intrusive_ptr_add_ref and intrusive_ptr_release need to be inline if defined in the header [SVN r45809] --- include/boost/thread/win32/condition_variable.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index 5efaf6c8..0581f459 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -235,12 +235,12 @@ namespace boost } }; - void intrusive_ptr_add_ref(basic_condition_variable::list_entry * p) + inline void intrusive_ptr_add_ref(basic_condition_variable::list_entry * p) { BOOST_INTERLOCKED_INCREMENT(&p->references); } - void intrusive_ptr_release(basic_condition_variable::list_entry * p) + inline void intrusive_ptr_release(basic_condition_variable::list_entry * p) { if(!BOOST_INTERLOCKED_DECREMENT(&p->references)) { From 6e42a04e43b60f80a0ecef7be14d22ef58d66efd Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 28 May 2008 08:09:07 +0000 Subject: [PATCH 151/214] Added note about move support [SVN r45856] --- doc/thread_ref.qbk | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk index 849bc3c7..507fd46d 100644 --- a/doc/thread_ref.qbk +++ b/doc/thread_ref.qbk @@ -17,6 +17,13 @@ allows the details of thread creation to be wrapped in a function. some_thread.join(); } +[Note: On compilers that support rvalue references, __thread__ provides a proper move constructor and move-assignment operator, and +therefore meets the C++0x ['MoveConstructible] and ['MoveAssignable] concepts. With such compilers, __thread__ can therefore be used +with containers that support those concepts. + +For other compilers, move support is provided with a move emulation layer, so containers must explicitly detect that move emulation +layer. See for details.] + [heading Launching threads] A new thread is launched by passing an object of a callable type that can be invoked with no parameters to the constructor. The From 1a5c911e365bf269414575bd8a01cfe3ab1785c1 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 28 May 2008 09:00:23 +0000 Subject: [PATCH 152/214] Added documentation for time support in the thread library [SVN r45858] --- doc/time.qbk | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 doc/time.qbk diff --git a/doc/time.qbk b/doc/time.qbk new file mode 100644 index 00000000..f817206c --- /dev/null +++ b/doc/time.qbk @@ -0,0 +1,64 @@ +[section:time Date and Time Requirements] + +As of Boost 1.35.0, the __boost_thread__ library uses the [link date_time Boost.Date_Time] library for all operations that require a +time out. These include (but are not limited to): + +* __sleep__ +* __timed_join__ +* __cond_timed_wait__ +* __timed_lock_ref__ + +For the overloads that accept an absolute time parameter, an object of type [link thread.time.system_time `boost::system_time`] is +required. Typically, this will be obtained by adding a duration to the current time, obtained with a call to [link +thread.time.get_system_time `boost::get_system_time()`]. e.g. + + boost::system_time const timeout=boost::get_system_time() + boost::posix_time::milliseconds(500); + + extern bool done; + extern boost::mutex m; + extern boost::condition_variable cond; + + boost::unique_lock lk(m); + while(!done) + { + if(!cond.timed_wait(lk,timeout)) + { + throw "timed out"; + } + } + +For the overloads that accept a ['TimeDuration] parameter, an object of any type that meets the [link +date_time.posix_time.time_duration Boost.Date_Time Time Duration requirements] can be used, e.g. + + boost::this_thread::sleep(boost::posix_time::milliseconds(25)); + + boost::mutex m; + if(m.timed_lock(boost::posix_time::nanoseconds(100))) + { + // ... + } + +[section:system_time Typedef `system_time`] + + typedef boost::posix_time::ptime system_time; + +See the documentation for [link date_time.posix_time.ptime_class `boost::posix_time::ptime`] in the Boost.Date_Time library. + +[endsect] + +[section:get_system_time Non-member function `get_system_time()`] + + system_time get_system_time(); + +[variablelist + +[[Returns:] [The current time.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + + +[endsect] \ No newline at end of file From 767d14ae4fe38900e45c2176e20a356425da2f62 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 28 May 2008 09:00:32 +0000 Subject: [PATCH 153/214] Added documentation for time support in the thread library [SVN r45859] --- doc/thread.qbk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/thread.qbk b/doc/thread.qbk index aaf975fd..b2ea6e6b 100644 --- a/doc/thread.qbk +++ b/doc/thread.qbk @@ -147,4 +147,6 @@ [include tss.qbk] +[include time.qbk] + [include acknowledgements.qbk] From 52bace18b2bc4f7acfb6db4de16292c6bf990854 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 28 May 2008 09:38:14 +0000 Subject: [PATCH 154/214] hardware_concurrency works for CYGWIN [SVN r45860] --- src/pthread/thread.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 89fbf928..2ceb68af 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -1,6 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf -// Copyright (C) 2007 Anthony Williams +// Copyright (C) 2007-8 Anthony Williams // // 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) @@ -18,7 +18,7 @@ #elif defined(__APPLE__) || defined(__FreeBSD__) #include #include -#elif defined(__sun) +#elif defined(__sun) || defined(__CYGWIN__) #include #endif @@ -394,7 +394,7 @@ namespace boost int count; size_t size=sizeof(count); return sysctlbyname("hw.ncpu",&count,&size,NULL,0)?0:count; -#elif defined(__sun) +#elif defined(__sun) || defined(__CYGWIN__) int const count=sysconf(_SC_NPROCESSORS_ONLN); return (count>0)?count:0; #else From 2991ca6c6f6e3dbbe0a16c46cfab779212b719cc Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 28 May 2008 11:02:06 +0000 Subject: [PATCH 155/214] Added abi prefix and suffix headers [SVN r45865] --- include/boost/thread/barrier.hpp | 6 +++++- include/boost/thread/detail/move.hpp | 5 ++++- include/boost/thread/detail/thread.hpp | 4 ++++ include/boost/thread/detail/tss_hooks.hpp | 4 ++++ include/boost/thread/exceptions.hpp | 6 +++++- include/boost/thread/locks.hpp | 4 ++++ include/boost/thread/once.hpp | 4 ++++ include/boost/thread/pthread/condition_variable.hpp | 6 +++++- include/boost/thread/pthread/condition_variable_fwd.hpp | 6 +++++- include/boost/thread/pthread/mutex.hpp | 6 +++++- include/boost/thread/pthread/once.hpp | 9 +++++++-- .../boost/thread/pthread/pthread_mutex_scoped_lock.hpp | 6 +++++- include/boost/thread/pthread/recursive_mutex.hpp | 5 ++++- include/boost/thread/pthread/shared_mutex.hpp | 5 ++++- include/boost/thread/pthread/thread_data.hpp | 3 +++ include/boost/thread/pthread/thread_heap_alloc.hpp | 3 +++ include/boost/thread/pthread/timespec.hpp | 6 +++++- include/boost/thread/pthread/tss.hpp | 6 +++++- include/boost/thread/thread_time.hpp | 4 ++++ include/boost/thread/win32/basic_recursive_mutex.hpp | 6 +++++- include/boost/thread/win32/basic_timed_mutex.hpp | 6 +++++- include/boost/thread/win32/condition_variable.hpp | 6 +++++- include/boost/thread/win32/interlocked_read.hpp | 9 ++++++--- include/boost/thread/win32/mutex.hpp | 4 ++++ include/boost/thread/win32/once.hpp | 4 ++++ include/boost/thread/win32/recursive_mutex.hpp | 3 +++ include/boost/thread/win32/shared_mutex.hpp | 3 +++ include/boost/thread/win32/thread_data.hpp | 3 +++ include/boost/thread/win32/thread_heap_alloc.hpp | 4 ++++ include/boost/thread/win32/thread_primitives.hpp | 4 ++++ include/boost/thread/win32/tss.hpp | 5 ++++- include/boost/thread/xtime.hpp | 6 +++++- 32 files changed, 140 insertions(+), 21 deletions(-) diff --git a/include/boost/thread/barrier.hpp b/include/boost/thread/barrier.hpp index 665e3b03..546f5e97 100644 --- a/include/boost/thread/barrier.hpp +++ b/include/boost/thread/barrier.hpp @@ -1,6 +1,6 @@ // Copyright (C) 2002-2003 // David Moore, William E. Kempf -// Copyright (C) 2007 Anthony Williams +// Copyright (C) 2007-8 Anthony Williams // // 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) @@ -15,6 +15,8 @@ #include #include +#include + namespace boost { @@ -56,4 +58,6 @@ namespace boost } // namespace boost +#include + #endif diff --git a/include/boost/thread/detail/move.hpp b/include/boost/thread/detail/move.hpp index 48744932..f8917758 100644 --- a/include/boost/thread/detail/move.hpp +++ b/include/boost/thread/detail/move.hpp @@ -1,7 +1,7 @@ // 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) -// (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007-8 Anthony Williams #ifndef BOOST_THREAD_MOVE_HPP #define BOOST_THREAD_MOVE_HPP @@ -9,6 +9,8 @@ #include #include +#include + namespace boost { namespace detail @@ -49,5 +51,6 @@ namespace boost } +#include #endif diff --git a/include/boost/thread/detail/thread.hpp b/include/boost/thread/detail/thread.hpp index 10810ef0..67fd6391 100644 --- a/include/boost/thread/detail/thread.hpp +++ b/include/boost/thread/detail/thread.hpp @@ -21,6 +21,8 @@ #include #include +#include + #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4251) @@ -536,4 +538,6 @@ namespace boost #pragma warning(pop) #endif +#include + #endif diff --git a/include/boost/thread/detail/tss_hooks.hpp b/include/boost/thread/detail/tss_hooks.hpp index 99a976f9..c4968442 100644 --- a/include/boost/thread/detail/tss_hooks.hpp +++ b/include/boost/thread/detail/tss_hooks.hpp @@ -8,6 +8,8 @@ #include +#include + #if defined(BOOST_HAS_WINTHREADS) typedef void (__cdecl *thread_exit_handler)(void); @@ -75,4 +77,6 @@ #endif //defined(BOOST_HAS_WINTHREADS) +#include + #endif //!defined(BOOST_TLS_HOOKS_HPP) diff --git a/include/boost/thread/exceptions.hpp b/include/boost/thread/exceptions.hpp index e46b30d2..49e244fe 100644 --- a/include/boost/thread/exceptions.hpp +++ b/include/boost/thread/exceptions.hpp @@ -1,6 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf -// Copyright (C) 2007 Anthony Williams +// Copyright (C) 2007-8 Anthony Williams // // 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) @@ -19,6 +19,8 @@ #include #include +#include + namespace boost { @@ -103,6 +105,8 @@ public: } // namespace boost +#include + #endif // BOOST_THREAD_CONFIG_PDM070801_H // Change log: diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index dee1bdf7..1739b52c 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -10,6 +10,8 @@ #include #include +#include + namespace boost { struct defer_lock_t @@ -900,4 +902,6 @@ namespace boost } } +#include + #endif diff --git a/include/boost/thread/once.hpp b/include/boost/thread/once.hpp index 608b77d6..975304e1 100644 --- a/include/boost/thread/once.hpp +++ b/include/boost/thread/once.hpp @@ -18,6 +18,8 @@ #error "Boost threads unavailable on this platform" #endif +#include + namespace boost { inline void call_once(void (*func)(),once_flag& flag) @@ -26,4 +28,6 @@ namespace boost } } +#include + #endif diff --git a/include/boost/thread/pthread/condition_variable.hpp b/include/boost/thread/pthread/condition_variable.hpp index 52c156bc..3e9bf1d9 100644 --- a/include/boost/thread/pthread/condition_variable.hpp +++ b/include/boost/thread/pthread/condition_variable.hpp @@ -3,7 +3,7 @@ // 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) -// (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007-8 Anthony Williams #include #include @@ -14,6 +14,8 @@ #include "thread_data.hpp" #include "condition_variable_fwd.hpp" +#include + namespace boost { inline condition_variable::condition_variable() @@ -175,4 +177,6 @@ namespace boost } +#include + #endif diff --git a/include/boost/thread/pthread/condition_variable_fwd.hpp b/include/boost/thread/pthread/condition_variable_fwd.hpp index 37884dd9..b1b531f3 100644 --- a/include/boost/thread/pthread/condition_variable_fwd.hpp +++ b/include/boost/thread/pthread/condition_variable_fwd.hpp @@ -3,7 +3,7 @@ // 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) -// (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007-8 Anthony Williams #include #include @@ -11,6 +11,8 @@ #include #include +#include + namespace boost { class condition_variable @@ -69,4 +71,6 @@ namespace boost }; } +#include + #endif diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index e1fa2593..4b33358e 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -1,6 +1,6 @@ #ifndef BOOST_THREAD_PTHREAD_MUTEX_HPP #define BOOST_THREAD_PTHREAD_MUTEX_HPP -// (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007-8 Anthony Williams // 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) @@ -24,6 +24,8 @@ #endif #endif +#include + namespace boost { class mutex: @@ -200,5 +202,7 @@ namespace boost } +#include + #endif diff --git a/include/boost/thread/pthread/once.hpp b/include/boost/thread/pthread/once.hpp index f342ed1d..f278a578 100644 --- a/include/boost/thread/pthread/once.hpp +++ b/include/boost/thread/pthread/once.hpp @@ -3,7 +3,7 @@ // once.hpp // -// (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007-8 Anthony Williams // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -17,7 +17,10 @@ #include #include -namespace boost { +#include + +namespace boost +{ struct once_flag { @@ -82,4 +85,6 @@ namespace boost { } } +#include + #endif diff --git a/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp b/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp index 6b474a95..2407f915 100644 --- a/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp +++ b/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp @@ -1,6 +1,6 @@ #ifndef BOOST_PTHREAD_MUTEX_SCOPED_LOCK_HPP #define BOOST_PTHREAD_MUTEX_SCOPED_LOCK_HPP -// (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007-8 Anthony Williams // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -9,6 +9,8 @@ #include #include +#include + namespace boost { namespace pthread @@ -47,4 +49,6 @@ namespace boost } } +#include + #endif diff --git a/include/boost/thread/pthread/recursive_mutex.hpp b/include/boost/thread/pthread/recursive_mutex.hpp index d0ddcef8..30689460 100644 --- a/include/boost/thread/pthread/recursive_mutex.hpp +++ b/include/boost/thread/pthread/recursive_mutex.hpp @@ -1,6 +1,6 @@ #ifndef BOOST_THREAD_PTHREAD_RECURSIVE_MUTEX_HPP #define BOOST_THREAD_PTHREAD_RECURSIVE_MUTEX_HPP -// (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007-8 Anthony Williams // 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) @@ -25,6 +25,8 @@ #endif #endif +#include + namespace boost { class recursive_mutex: @@ -259,5 +261,6 @@ namespace boost } +#include #endif diff --git a/include/boost/thread/pthread/shared_mutex.hpp b/include/boost/thread/pthread/shared_mutex.hpp index d44b101c..74345d88 100644 --- a/include/boost/thread/pthread/shared_mutex.hpp +++ b/include/boost/thread/pthread/shared_mutex.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_THREAD_PTHREAD_SHARED_MUTEX_HPP #define BOOST_THREAD_PTHREAD_SHARED_MUTEX_HPP -// (C) Copyright 2006-7 Anthony Williams +// (C) Copyright 2006-8 Anthony Williams // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -13,6 +13,8 @@ #include #include +#include + namespace boost { class shared_mutex @@ -296,5 +298,6 @@ namespace boost }; } +#include #endif diff --git a/include/boost/thread/pthread/thread_data.hpp b/include/boost/thread/pthread/thread_data.hpp index 2532b854..244035b4 100644 --- a/include/boost/thread/pthread/thread_data.hpp +++ b/include/boost/thread/pthread/thread_data.hpp @@ -14,6 +14,8 @@ #include #include "condition_variable_fwd.hpp" +#include + namespace boost { class thread; @@ -111,5 +113,6 @@ namespace boost } } +#include #endif diff --git a/include/boost/thread/pthread/thread_heap_alloc.hpp b/include/boost/thread/pthread/thread_heap_alloc.hpp index 338092da..7cc0aa04 100644 --- a/include/boost/thread/pthread/thread_heap_alloc.hpp +++ b/include/boost/thread/pthread/thread_heap_alloc.hpp @@ -5,6 +5,8 @@ #ifndef THREAD_HEAP_ALLOC_PTHREAD_HPP #define THREAD_HEAP_ALLOC_PTHREAD_HPP +#include + namespace boost { namespace detail @@ -235,5 +237,6 @@ namespace boost } } +#include #endif diff --git a/include/boost/thread/pthread/timespec.hpp b/include/boost/thread/pthread/timespec.hpp index 0837e97e..dc7e5f5d 100644 --- a/include/boost/thread/pthread/timespec.hpp +++ b/include/boost/thread/pthread/timespec.hpp @@ -1,6 +1,6 @@ #ifndef BOOST_THREAD_PTHREAD_TIMESPEC_HPP #define BOOST_THREAD_PTHREAD_TIMESPEC_HPP -// (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007-8 Anthony Williams // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -9,6 +9,8 @@ #include #include +#include + namespace boost { namespace detail @@ -25,4 +27,6 @@ namespace boost } } +#include + #endif diff --git a/include/boost/thread/pthread/tss.hpp b/include/boost/thread/pthread/tss.hpp index 8d3bfd36..1f3cb246 100644 --- a/include/boost/thread/pthread/tss.hpp +++ b/include/boost/thread/pthread/tss.hpp @@ -4,11 +4,13 @@ // 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) -// (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007-8 Anthony Williams #include #include +#include + namespace boost { namespace detail @@ -100,4 +102,6 @@ namespace boost }; } +#include + #endif diff --git a/include/boost/thread/thread_time.hpp b/include/boost/thread/thread_time.hpp index 5468961f..8b557d5c 100644 --- a/include/boost/thread/thread_time.hpp +++ b/include/boost/thread/thread_time.hpp @@ -9,6 +9,8 @@ #include #include +#include + namespace boost { typedef boost::posix_time::ptime system_time; @@ -43,4 +45,6 @@ namespace boost } +#include + #endif diff --git a/include/boost/thread/win32/basic_recursive_mutex.hpp b/include/boost/thread/win32/basic_recursive_mutex.hpp index d2b8e77d..89c5f1dc 100644 --- a/include/boost/thread/win32/basic_recursive_mutex.hpp +++ b/include/boost/thread/win32/basic_recursive_mutex.hpp @@ -3,7 +3,7 @@ // basic_recursive_mutex.hpp // -// (C) Copyright 2006-7 Anthony Williams +// (C) Copyright 2006-8 Anthony Williams // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -12,6 +12,8 @@ #include "thread_primitives.hpp" #include "basic_timed_mutex.hpp" +#include + namespace boost { namespace detail @@ -123,4 +125,6 @@ namespace boost #define BOOST_BASIC_RECURSIVE_MUTEX_INITIALIZER {0} +#include + #endif diff --git a/include/boost/thread/win32/basic_timed_mutex.hpp b/include/boost/thread/win32/basic_timed_mutex.hpp index 77a9332f..a218233c 100644 --- a/include/boost/thread/win32/basic_timed_mutex.hpp +++ b/include/boost/thread/win32/basic_timed_mutex.hpp @@ -3,7 +3,7 @@ // basic_timed_mutex_win32.hpp // -// (C) Copyright 2006 Anthony Williams +// (C) Copyright 2006-8 Anthony Williams // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -15,6 +15,8 @@ #include #include +#include + namespace boost { namespace detail @@ -175,4 +177,6 @@ namespace boost #define BOOST_BASIC_TIMED_MUTEX_INITIALIZER {0} +#include + #endif diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index 0581f459..b7a38665 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -3,7 +3,7 @@ // 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) -// (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007-8 Anthony Williams #include #include "thread_primitives.hpp" @@ -17,6 +17,8 @@ #include #include +#include + namespace boost { namespace detail @@ -372,4 +374,6 @@ namespace boost } +#include + #endif diff --git a/include/boost/thread/win32/interlocked_read.hpp b/include/boost/thread/win32/interlocked_read.hpp index 72315f60..133fb6f9 100644 --- a/include/boost/thread/win32/interlocked_read.hpp +++ b/include/boost/thread/win32/interlocked_read.hpp @@ -3,12 +3,16 @@ // interlocked_read_win32.hpp // -// (C) Copyright 2005-7 Anthony Williams +// (C) Copyright 2005-8 Anthony Williams // // 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) +#include + +#include + #ifdef BOOST_MSVC extern "C" void _ReadWriteBarrier(void); @@ -46,8 +50,6 @@ namespace boost #else -#include - namespace boost { namespace detail @@ -73,5 +75,6 @@ namespace boost #endif +#include #endif diff --git a/include/boost/thread/win32/mutex.hpp b/include/boost/thread/win32/mutex.hpp index 41787559..3fa11d9c 100644 --- a/include/boost/thread/win32/mutex.hpp +++ b/include/boost/thread/win32/mutex.hpp @@ -10,6 +10,8 @@ #include #include +#include + namespace boost { namespace detail @@ -58,4 +60,6 @@ namespace boost }; } +#include + #endif diff --git a/include/boost/thread/win32/once.hpp b/include/boost/thread/win32/once.hpp index 719eaea5..a6fcc94d 100644 --- a/include/boost/thread/win32/once.hpp +++ b/include/boost/thread/win32/once.hpp @@ -18,6 +18,8 @@ #include #include +#include + #ifdef BOOST_NO_STDC_NAMESPACE namespace std { @@ -129,4 +131,6 @@ namespace boost } } +#include + #endif diff --git a/include/boost/thread/win32/recursive_mutex.hpp b/include/boost/thread/win32/recursive_mutex.hpp index 7f7541a1..2360a92b 100644 --- a/include/boost/thread/win32/recursive_mutex.hpp +++ b/include/boost/thread/win32/recursive_mutex.hpp @@ -15,6 +15,8 @@ #include #include +#include + namespace boost { class recursive_mutex: @@ -57,5 +59,6 @@ namespace boost }; } +#include #endif diff --git a/include/boost/thread/win32/shared_mutex.hpp b/include/boost/thread/win32/shared_mutex.hpp index 0b7d6fae..9f3c489e 100644 --- a/include/boost/thread/win32/shared_mutex.hpp +++ b/include/boost/thread/win32/shared_mutex.hpp @@ -15,6 +15,8 @@ #include #include +#include + namespace boost { class shared_mutex: @@ -632,5 +634,6 @@ namespace boost }; } +#include #endif diff --git a/include/boost/thread/win32/thread_data.hpp b/include/boost/thread/win32/thread_data.hpp index 604569a5..1a6a1e03 100644 --- a/include/boost/thread/win32/thread_data.hpp +++ b/include/boost/thread/win32/thread_data.hpp @@ -11,6 +11,8 @@ #include "thread_primitives.hpp" #include "thread_heap_alloc.hpp" +#include + namespace boost { namespace detail @@ -171,5 +173,6 @@ namespace boost } +#include #endif diff --git a/include/boost/thread/win32/thread_heap_alloc.hpp b/include/boost/thread/win32/thread_heap_alloc.hpp index a669654d..9f8186f5 100644 --- a/include/boost/thread/win32/thread_heap_alloc.hpp +++ b/include/boost/thread/win32/thread_heap_alloc.hpp @@ -49,6 +49,8 @@ namespace boost #endif +#include + namespace boost { namespace detail @@ -389,5 +391,7 @@ namespace boost } } +#include + #endif diff --git a/include/boost/thread/win32/thread_primitives.hpp b/include/boost/thread/win32/thread_primitives.hpp index 372f262d..022dc9ee 100644 --- a/include/boost/thread/win32/thread_primitives.hpp +++ b/include/boost/thread/win32/thread_primitives.hpp @@ -18,6 +18,7 @@ #if defined( BOOST_USE_WINDOWS_H ) # include + namespace boost { namespace detail @@ -147,6 +148,8 @@ namespace boost # error "Win32 functions not available" #endif +#include + namespace boost { namespace detail @@ -391,5 +394,6 @@ namespace boost } #endif +#include #endif diff --git a/include/boost/thread/win32/tss.hpp b/include/boost/thread/win32/tss.hpp index c6ce5c12..a5f1c31c 100644 --- a/include/boost/thread/win32/tss.hpp +++ b/include/boost/thread/win32/tss.hpp @@ -3,11 +3,13 @@ // 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) -// (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007-8 Anthony Williams #include #include "thread_heap_alloc.hpp" +#include + namespace boost { namespace detail @@ -99,5 +101,6 @@ namespace boost }; } +#include #endif diff --git a/include/boost/thread/xtime.hpp b/include/boost/thread/xtime.hpp index e3970758..54f91720 100644 --- a/include/boost/thread/xtime.hpp +++ b/include/boost/thread/xtime.hpp @@ -1,6 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf -// Copyright (C) 2007 Anthony Williams +// Copyright (C) 2007-8 Anthony Williams // // 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) @@ -14,6 +14,8 @@ #include #include +#include + namespace boost { enum xtime_clock_types @@ -85,4 +87,6 @@ inline int xtime_cmp(const xtime& xt1, const xtime& xt2) } // namespace boost +#include + #endif //BOOST_XTIME_WEK070601_HPP From ef8c08ba993a8a0af1fb6c3a3fd3f92d57c39198 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 28 May 2008 12:55:30 +0000 Subject: [PATCH 156/214] Removed surplus "the" [SVN r45869] --- doc/thread_ref.qbk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk index 507fd46d..2f3caf37 100644 --- a/doc/thread_ref.qbk +++ b/doc/thread_ref.qbk @@ -506,7 +506,7 @@ otherwise.]] [variablelist -[[Returns:] [`true` if `*this` and `y` represent the different threads of execution, or one represents a thread of execution, and +[[Returns:] [`true` if `*this` and `y` represent different threads of execution, or one represents a thread of execution, and the other represent __not_a_thread__, `false` otherwise.]] [[Throws:] [Nothing]] From 31a34cd0b5bd43c8c6fa754eefeef8f1387779f4 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 28 May 2008 14:50:25 +0000 Subject: [PATCH 157/214] Added missing "no" [SVN r45870] --- doc/thread_ref.qbk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk index 2f3caf37..55ff306f 100644 --- a/doc/thread_ref.qbk +++ b/doc/thread_ref.qbk @@ -325,7 +325,7 @@ unchanged.]] [variablelist -[[Effects:] [If `*this` refers to a thread of execution, that thread of execution becomes detached, and longer has an associated __thread__ object.]] +[[Effects:] [If `*this` refers to a thread of execution, that thread of execution becomes detached, and no longer has an associated __thread__ object.]] [[Postconditions:] [`*this` no longer refers to any thread of execution.]] From 67cc49f333a6c5d1ff08675ed34bb4a719d7e354 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 29 May 2008 09:02:05 +0000 Subject: [PATCH 158/214] More tests for generic locks, and a new range version [SVN r45897] --- include/boost/thread/locks.hpp | 201 +++++++++++++++++- test/test_generic_locks.cpp | 376 +++++++++++++++++++++++++++++++++ 2 files changed, 576 insertions(+), 1 deletion(-) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 1739b52c..86ce8cf9 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -8,12 +8,77 @@ #include #include #include +#include #include +#include #include namespace boost { + namespace detail + { + template + struct has_member_lock + { + typedef char true_type; + struct false_type + { + true_type dummy[2]; + }; + + template + static true_type has_member(U*,void (U::*dummy)()=&U::lock); + static false_type has_member(void*); + + BOOST_STATIC_CONSTANT(bool, value=sizeof(has_member_lock::has_member((T*)NULL))==sizeof(true_type)); + }; + + template + struct has_member_unlock + { + typedef char true_type; + struct false_type + { + true_type dummy[2]; + }; + + template + static true_type has_member(U*,void (U::*dummy)()=&U::unlock); + static false_type has_member(void*); + + BOOST_STATIC_CONSTANT(bool, value=sizeof(has_member_unlock::has_member((T*)NULL))==sizeof(true_type)); + }; + + template + struct has_member_try_lock + { + typedef char true_type; + struct false_type + { + true_type dummy[2]; + }; + + template + static true_type has_member(U*,bool (U::*dummy)()=&U::try_lock); + static false_type has_member(void*); + + BOOST_STATIC_CONSTANT(bool, value=sizeof(has_member_try_lock::has_member((T*)NULL))==sizeof(true_type)); + }; + + } + + + template + struct is_mutex_type + { + BOOST_STATIC_CONSTANT(bool, value = detail::has_member_lock::value && + detail::has_member_unlock::value && + detail::has_member_try_lock::value); + + }; + + struct defer_lock_t {}; struct try_to_lock_t @@ -766,7 +831,7 @@ namespace boost } template - void lock(MutexType1& m1,MutexType2& m2) + typename enable_if, void>::type lock(MutexType1& m1,MutexType2& m2) { unsigned const lock_count=2; unsigned lock_first=0; @@ -900,6 +965,140 @@ namespace boost } } } + + template + typename enable_if, int>::type try_lock(MutexType1& m1,MutexType2& m2) + { + return ((int)detail::try_lock_internal(m1,m2))-1; + } + + template + int try_lock(MutexType1& m1,MutexType2& m2,MutexType3& m3) + { + return ((int)detail::try_lock_internal(m1,m2,m3))-1; + } + + template + int try_lock(MutexType1& m1,MutexType2& m2,MutexType3& m3,MutexType4& m4) + { + return ((int)detail::try_lock_internal(m1,m2,m3,m4))-1; + } + + template + int try_lock(MutexType1& m1,MutexType2& m2,MutexType3& m3,MutexType4& m4,MutexType5& m5) + { + return ((int)detail::try_lock_internal(m1,m2,m3,m4,m5))-1; + } + + + template + typename disable_if, void>::type lock(Iterator begin,Iterator end); + + namespace detail + { + template + struct range_lock_guard + { + Iterator begin; + Iterator end; + + range_lock_guard(Iterator begin_,Iterator end_): + begin(begin_),end(end_) + { + lock(begin,end); + } + + void release() + { + begin=end; + } + + ~range_lock_guard() + { + for(;begin!=end;++begin) + { + begin->unlock(); + } + } + }; + } + + template + typename disable_if, Iterator>::type try_lock(Iterator begin,Iterator end) + { + if(begin==end) + { + return end; + } + typedef typename std::iterator_traits::value_type lock_type; + unique_lock guard(*begin,try_to_lock); + + if(!guard.owns_lock()) + { + return begin; + } + Iterator const failed=try_lock(++begin,end); + if(failed==end) + { + guard.release(); + } + + return failed; + } + + template + typename disable_if, void>::type lock(Iterator begin,Iterator end) + { + typedef typename std::iterator_traits::value_type lock_type; + + if(begin==end) + { + return; + } + bool start_with_begin=true; + Iterator second=begin; + ++second; + Iterator next=second; + + for(;;) + { + unique_lock begin_lock(*begin,defer_lock); + if(start_with_begin) + { + begin_lock.lock(); + Iterator const failed_lock=try_lock(next,end); + if(failed_lock==end) + { + begin_lock.release(); + return; + } + start_with_begin=false; + next=failed_lock; + } + else + { + detail::range_lock_guard guard(next,end); + if(begin_lock.try_lock()) + { + Iterator const failed_lock=try_lock(second,next); + if(failed_lock==next) + { + begin_lock.release(); + guard.release(); + return; + } + start_with_begin=false; + next=failed_lock; + } + else + { + start_with_begin=true; + next=second; + } + } + } + } + } #include diff --git a/test/test_generic_locks.cpp b/test/test_generic_locks.cpp index b0023bab..a091976e 100644 --- a/test/test_generic_locks.cpp +++ b/test/test_generic_locks.cpp @@ -123,6 +123,370 @@ void test_lock_two_other_thread_locks_in_opposite_order() t.join(); } +void test_lock_five_uncontended() +{ + boost::mutex m1,m2,m3,m4,m5; + + boost::mutex::scoped_lock l1(m1,boost::defer_lock), + l2(m2,boost::defer_lock), + l3(m3,boost::defer_lock), + l4(m4,boost::defer_lock), + l5(m5,boost::defer_lock); + + BOOST_CHECK(!l1.owns_lock()); + BOOST_CHECK(!l2.owns_lock()); + BOOST_CHECK(!l3.owns_lock()); + BOOST_CHECK(!l4.owns_lock()); + BOOST_CHECK(!l5.owns_lock()); + + boost::lock(l1,l2,l3,l4,l5); + + BOOST_CHECK(l1.owns_lock()); + BOOST_CHECK(l2.owns_lock()); + BOOST_CHECK(l3.owns_lock()); + BOOST_CHECK(l4.owns_lock()); + BOOST_CHECK(l5.owns_lock()); +} + +void lock_five_mutexes_slowly(boost::mutex* m1,boost::mutex* m2,boost::mutex* m3,boost::mutex* m4,boost::mutex* m5, + wait_data* locked,wait_data* quit) +{ + boost::lock_guard l1(*m1); + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + boost::lock_guard l2(*m2); + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + boost::lock_guard l3(*m3); + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + boost::lock_guard l4(*m4); + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + boost::lock_guard l5(*m5); + locked->signal(); + quit->wait(); +} + +void lock_five(boost::mutex* m1,boost::mutex* m2,boost::mutex* m3,boost::mutex* m4,boost::mutex* m5) +{ + boost::lock(*m1,*m2,*m3,*m4,*m5); + m1->unlock(); + m2->unlock(); + m3->unlock(); + m4->unlock(); + m5->unlock(); +} + +void test_lock_five_other_thread_locks_in_order() +{ + boost::mutex m1,m2,m3,m4,m5; + wait_data locked; + wait_data release; + + boost::thread t(lock_five_mutexes_slowly,&m1,&m2,&m3,&m4,&m5,&locked,&release); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + + boost::thread t2(lock_five,&m1,&m2,&m3,&m4,&m5); + BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(3))); + + release.signal(); + + BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(3))); + + t.join(); +} + +void test_lock_five_other_thread_locks_in_different_order() +{ + boost::mutex m1,m2,m3,m4,m5; + wait_data locked; + wait_data release; + + boost::thread t(lock_five_mutexes_slowly,&m1,&m2,&m3,&m4,&m5,&locked,&release); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + + boost::thread t2(lock_five,&m5,&m1,&m4,&m2,&m3); + BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(3))); + + release.signal(); + + BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(3))); + + t.join(); +} + +void lock_n(boost::mutex* mutexes,unsigned count) +{ + boost::lock(mutexes,mutexes+count); + for(unsigned i=0;i l1(m1,boost::defer_lock), + l2(m2,boost::defer_lock); + + int const res=boost::try_lock(l1,l2); + + BOOST_CHECK(res==0); + BOOST_CHECK(m1.is_locked); + BOOST_CHECK(!m2.is_locked); + BOOST_CHECK(!l1.owns_lock()); + BOOST_CHECK(!l2.owns_lock()); +} +void test_try_lock_two_second_locked() +{ + dummy_mutex m1,m2; + m2.lock(); + + boost::unique_lock l1(m1,boost::defer_lock), + l2(m2,boost::defer_lock); + + int const res=boost::try_lock(l1,l2); + + BOOST_CHECK(res==1); + BOOST_CHECK(!m1.is_locked); + BOOST_CHECK(m2.is_locked); + BOOST_CHECK(!l1.owns_lock()); + BOOST_CHECK(!l2.owns_lock()); +} + +void test_try_lock_three() +{ + int const num_mutexes=3; + + for(int i=-1;i=0) + { + mutexes[i].lock(); + } + boost::unique_lock l1(mutexes[0],boost::defer_lock), + l2(mutexes[1],boost::defer_lock), + l3(mutexes[2],boost::defer_lock); + + int const res=boost::try_lock(l1,l2,l3); + + BOOST_CHECK(res==i); + for(int j=0;j=0) + { + mutexes[i].lock(); + } + boost::unique_lock l1(mutexes[0],boost::defer_lock), + l2(mutexes[1],boost::defer_lock), + l3(mutexes[2],boost::defer_lock), + l4(mutexes[3],boost::defer_lock); + + int const res=boost::try_lock(l1,l2,l3,l4); + + BOOST_CHECK(res==i); + for(int j=0;j=0) + { + mutexes[i].lock(); + } + boost::unique_lock l1(mutexes[0],boost::defer_lock), + l2(mutexes[1],boost::defer_lock), + l3(mutexes[2],boost::defer_lock), + l4(mutexes[3],boost::defer_lock), + l5(mutexes[4],boost::defer_lock); + + int const res=boost::try_lock(l1,l2,l3,l4,l5); + + BOOST_CHECK(res==i); + for(int j=0;jadd(BOOST_TEST_CASE(&test_lock_two_uncontended)); test->add(BOOST_TEST_CASE(&test_lock_two_other_thread_locks_in_order)); test->add(BOOST_TEST_CASE(&test_lock_two_other_thread_locks_in_opposite_order)); + test->add(BOOST_TEST_CASE(&test_lock_five_uncontended)); + test->add(BOOST_TEST_CASE(&test_lock_five_other_thread_locks_in_order)); + test->add(BOOST_TEST_CASE(&test_lock_five_other_thread_locks_in_different_order)); + test->add(BOOST_TEST_CASE(&test_lock_five_in_range)); + test->add(BOOST_TEST_CASE(&test_lock_ten_in_range)); + test->add(BOOST_TEST_CASE(&test_lock_ten_other_thread_locks_in_different_order)); + test->add(BOOST_TEST_CASE(&test_try_lock_two_uncontended)); + test->add(BOOST_TEST_CASE(&test_try_lock_two_first_locked)); + test->add(BOOST_TEST_CASE(&test_try_lock_two_second_locked)); + test->add(BOOST_TEST_CASE(&test_try_lock_three)); + test->add(BOOST_TEST_CASE(&test_try_lock_four)); + test->add(BOOST_TEST_CASE(&test_try_lock_five)); return test; } From fdd20a519e5c57c09a84267748c1e7cc1e48789c Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 29 May 2008 15:16:04 +0000 Subject: [PATCH 159/214] Use wrapper functions in try_lock_wrapper rather than using declarations, as the latter confuse some compilers [SVN r45908] --- include/boost/thread/locks.hpp | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 86ce8cf9..8b183d38 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -688,13 +688,30 @@ namespace boost base::swap(*other); } - using base::lock; - using base::try_lock; - using base::unlock; - using base::owns_lock; - using base::mutex; - using base::release; - + void lock() + { + base::lock(); + } + bool try_lock() + { + return base::try_lock(); + } + void unlock() + { + base::unlock(); + } + bool owns_lock() const + { + return base::owns_lock(); + } + Mutex* mutex() const + { + return base::mutex(); + } + Mutex* release() + { + return base::release(); + } bool operator!() const { return !this->owns_lock(); From 6abb53c9d3402cdda8682486120bf168775a6e67 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 29 May 2008 15:16:55 +0000 Subject: [PATCH 160/214] Move definition of constructor and destructor of condition_variable into condition_variable_fwd.hpp, so they are always available [SVN r45909] --- .../boost/thread/pthread/condition_variable.hpp | 17 ----------------- .../thread/pthread/condition_variable_fwd.hpp | 15 +++++++++++++-- include/boost/thread/pthread/timespec.hpp | 4 ++++ 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/include/boost/thread/pthread/condition_variable.hpp b/include/boost/thread/pthread/condition_variable.hpp index 3e9bf1d9..2d130a63 100644 --- a/include/boost/thread/pthread/condition_variable.hpp +++ b/include/boost/thread/pthread/condition_variable.hpp @@ -5,10 +5,6 @@ // http://www.boost.org/LICENSE_1_0.txt) // (C) Copyright 2007-8 Anthony Williams -#include -#include -#include -#include #include "timespec.hpp" #include "pthread_mutex_scoped_lock.hpp" #include "thread_data.hpp" @@ -18,19 +14,6 @@ namespace boost { - inline condition_variable::condition_variable() - { - int const res=pthread_cond_init(&cond,NULL); - if(res) - { - throw thread_resource_error(); - } - } - inline condition_variable::~condition_variable() - { - BOOST_VERIFY(!pthread_cond_destroy(&cond)); - } - inline void condition_variable::wait(unique_lock& m) { detail::interruption_checker check_for_interruption(&cond); diff --git a/include/boost/thread/pthread/condition_variable_fwd.hpp b/include/boost/thread/pthread/condition_variable_fwd.hpp index b1b531f3..c3941511 100644 --- a/include/boost/thread/pthread/condition_variable_fwd.hpp +++ b/include/boost/thread/pthread/condition_variable_fwd.hpp @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) // (C) Copyright 2007-8 Anthony Williams +#include #include #include #include @@ -24,8 +25,18 @@ namespace boost condition_variable& operator=(condition_variable&); public: - condition_variable(); - ~condition_variable(); + condition_variable() + { + int const res=pthread_cond_init(&cond,NULL); + if(res) + { + throw thread_resource_error(); + } + } + ~condition_variable() + { + BOOST_VERIFY(!pthread_cond_destroy(&cond)); + } void wait(unique_lock& m); diff --git a/include/boost/thread/pthread/timespec.hpp b/include/boost/thread/pthread/timespec.hpp index dc7e5f5d..105030ff 100644 --- a/include/boost/thread/pthread/timespec.hpp +++ b/include/boost/thread/pthread/timespec.hpp @@ -8,6 +8,10 @@ #include #include +#include +#ifndef _WIN32 +#include +#endif #include From 3f1334090395b9f6848b899ffbedcf43086c684c Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 29 May 2008 15:36:52 +0000 Subject: [PATCH 161/214] Don't construct function objects directly in boost::thread constructor as some compilers can't handle that. [SVN r45911] --- test/test_thread_launching.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/test_thread_launching.cpp b/test/test_thread_launching.cpp index 24a646f0..4582e657 100644 --- a/test/test_thread_launching.cpp +++ b/test/test_thread_launching.cpp @@ -143,8 +143,10 @@ void test_thread_callable_object_multiple_arguments() { x.push_back(i*i); } + + callable_multiple_arg func; - boost::thread callable3(callable_multiple_arg(),"hello",x,1.2); + boost::thread callable3(func,"hello",x,1.2); callable3.join(); BOOST_CHECK(callable_multiple_arg::called_three); BOOST_CHECK_EQUAL(callable_multiple_arg::called_three_arg1,"hello"); @@ -158,7 +160,7 @@ void test_thread_callable_object_multiple_arguments() double const dbl=1.234; - boost::thread callable2(callable_multiple_arg(),19,dbl); + boost::thread callable2(func,19,dbl); callable2.join(); BOOST_CHECK(callable_multiple_arg::called_two); BOOST_CHECK_EQUAL(callable_multiple_arg::called_two_arg1,19); From 4db57bcb10b16fbda81d2e745f278e2b1eb4b427 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 29 May 2008 15:38:08 +0000 Subject: [PATCH 162/214] Move thread_data to detail namespace rather than have it as a nested type of boost::thread, to try and help compilers that have problems with the partial specializations for reference_wrapper [SVN r45912] --- include/boost/thread/detail/thread.hpp | 104 ++++++++++++++----------- 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/include/boost/thread/detail/thread.hpp b/include/boost/thread/detail/thread.hpp index 67fd6391..1ada38f9 100644 --- a/include/boost/thread/detail/thread.hpp +++ b/include/boost/thread/detail/thread.hpp @@ -30,20 +30,13 @@ namespace boost { - class BOOST_THREAD_DECL thread + namespace detail { - private: - thread(thread&); - thread& operator=(thread&); - - void release_handle(); - template - struct thread_data: - detail::thread_data_base + class thread_data: + public detail::thread_data_base { - F f; - + public: #ifdef BOOST_HAS_RVALUE_REFS thread_data(F&& f_): f(static_cast(f_)) @@ -61,9 +54,59 @@ namespace boost f(); } private: + F f; + void operator=(thread_data&); thread_data(thread_data&); }; + + template + class thread_data >: + public detail::thread_data_base + { + private: + F& f; + + void operator=(thread_data&); + thread_data(thread_data&); + public: + thread_data(boost::reference_wrapper f_): + f(f_) + {} + + void run() + { + f(); + } + }; + + template + class thread_data >: + public detail::thread_data_base + { + private: + F& f; + void operator=(thread_data&); + thread_data(thread_data&); + public: + thread_data(const boost::reference_wrapper f_): + f(f_) + {} + + void run() + { + f(); + } + }; + } + + class BOOST_THREAD_DECL thread + { + private: + thread(thread&); + thread& operator=(thread&); + + void release_handle(); mutable boost::mutex thread_info_mutex; detail::thread_data_ptr thread_info; @@ -78,18 +121,18 @@ namespace boost template static inline detail::thread_data_ptr make_thread_info(F&& f) { - return detail::thread_data_ptr(detail::heap_new >(static_cast(f))); + return detail::thread_data_ptr(detail::heap_new >(static_cast(f))); } #else template static inline detail::thread_data_ptr make_thread_info(F f) { - return detail::thread_data_ptr(detail::heap_new >(f)); + return detail::thread_data_ptr(detail::heap_new >(f)); } template static inline detail::thread_data_ptr make_thread_info(boost::detail::thread_move_t f) { - return detail::thread_data_ptr(detail::heap_new >(f)); + return detail::thread_data_ptr(detail::heap_new >(f)); } #endif public: @@ -280,39 +323,6 @@ namespace boost } #endif - template - struct thread::thread_data >: - detail::thread_data_base - { - F& f; - - thread_data(boost::reference_wrapper f_): - f(f_) - {} - - void run() - { - f(); - } - }; - - template - struct thread::thread_data >: - detail::thread_data_base - { - F& f; - - thread_data(const boost::reference_wrapper f_): - f(f_) - {} - - void run() - { - f(); - } - }; - - namespace this_thread { class BOOST_THREAD_DECL disable_interruption From 5edfa273ff27a7023951b73683c3b20b68ce3f14 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 3 Jun 2008 20:55:40 +0000 Subject: [PATCH 163/214] removed unused header [SVN r46093] --- include/boost/thread/pthread/mutex.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index 4b33358e..128a9405 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -11,9 +11,6 @@ #include #include #include -#ifndef _WIN32 -#include -#endif #include #include "timespec.hpp" #include "pthread_mutex_scoped_lock.hpp" From c8e5ad564dab48917573863837d4815349eed46f Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 3 Jun 2008 20:56:39 +0000 Subject: [PATCH 164/214] basic_condition_variable::lock_entry extracted to basic_cv_lock_entry in order to try and eliminate problems on Borland compiler [SVN r46094] --- .../boost/thread/win32/condition_variable.hpp | 145 +++++++++++------- 1 file changed, 92 insertions(+), 53 deletions(-) diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index b7a38665..6e676b49 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -23,35 +23,96 @@ namespace boost { namespace detail { + class basic_cv_list_entry; + void intrusive_ptr_add_ref(basic_cv_list_entry * p); + void intrusive_ptr_release(basic_cv_list_entry * p); + + class basic_cv_list_entry + { + private: + detail::win32::handle_manager semaphore; + detail::win32::handle_manager wake_sem; + long waiters; + bool notified; + long references; + + basic_cv_list_entry(basic_cv_list_entry&); + void operator=(basic_cv_list_entry&); + + public: + explicit basic_cv_list_entry(detail::win32::handle_manager const& wake_sem_): + semaphore(detail::win32::create_anonymous_semaphore(0,LONG_MAX)), + wake_sem(wake_sem_.duplicate()), + waiters(1),notified(false),references(0) + {} + + static bool no_waiters(boost::intrusive_ptr const& entry) + { + return !detail::interlocked_read_acquire(&entry->waiters); + } + + void add_waiter() + { + BOOST_INTERLOCKED_INCREMENT(&waiters); + } + + void remove_waiter() + { + BOOST_INTERLOCKED_DECREMENT(&waiters); + } + + void release(unsigned count_to_release) + { + notified=true; + detail::win32::ReleaseSemaphore(semaphore,count_to_release,0); + } + + void release_waiters() + { + release(detail::interlocked_read_acquire(&waiters)); + } + + bool is_notified() const + { + return notified; + } + + bool wait(timeout wait_until) + { + return this_thread::interruptible_wait(semaphore,wait_until); + } + + bool woken() + { + unsigned long const woken_result=detail::win32::WaitForSingleObject(wake_sem,0); + BOOST_ASSERT((woken_result==detail::win32::timeout) || (woken_result==0)); + return woken_result==0; + } + + friend void intrusive_ptr_add_ref(basic_cv_list_entry * p); + friend void intrusive_ptr_release(basic_cv_list_entry * p); + }; + + inline void intrusive_ptr_add_ref(basic_cv_list_entry * p) + { + BOOST_INTERLOCKED_INCREMENT(&p->references); + } + + inline void intrusive_ptr_release(basic_cv_list_entry * p) + { + if(!BOOST_INTERLOCKED_DECREMENT(&p->references)) + { + delete p; + } + } + class basic_condition_variable { boost::mutex internal_mutex; long total_count; unsigned active_generation_count; - struct list_entry - { - detail::win32::handle_manager semaphore; - detail::win32::handle_manager wake_sem; - long waiters; - bool notified; - long references; - - list_entry(): - semaphore(detail::win32::create_anonymous_semaphore(0,LONG_MAX)), - wake_sem(0), - waiters(1),notified(false),references(0) - {} - - void release(unsigned count_to_release) - { - notified=true; - detail::win32::ReleaseSemaphore(semaphore,count_to_release,0); - } - }; - - friend void intrusive_ptr_add_ref(list_entry * p); - friend void intrusive_ptr_release(list_entry * p); + typedef basic_cv_list_entry list_entry; typedef boost::intrusive_ptr entry_ptr; typedef std::vector generation_list; @@ -104,16 +165,15 @@ namespace boost } detail::interlocked_write_release(&total_count,total_count+1); - if(generations.empty() || generations.back()->notified) + if(generations.empty() || generations.back()->is_notified()) { - entry_ptr new_entry(new list_entry); - new_entry->wake_sem=wake_sem.duplicate(); + entry_ptr new_entry(new list_entry(wake_sem)); generations.push_back(new_entry); return new_entry; } else { - BOOST_INTERLOCKED_INCREMENT(&generations.back()->waiters); + generations.back()->add_waiter(); return generations.back(); } } @@ -128,7 +188,7 @@ namespace boost ~entry_manager() { - BOOST_INTERLOCKED_DECREMENT(&entry->waiters); + entry->remove_waiter(); } list_entry* operator->() @@ -155,15 +215,12 @@ namespace boost bool woken=false; while(!woken) { - if(!this_thread::interruptible_wait(entry->semaphore,wait_until)) + if(!entry->wait(wait_until)) { return false; } - unsigned long const woken_result=detail::win32::WaitForSingleObject(entry->wake_sem,0); - BOOST_ASSERT(woken_result==detail::win32::timeout || woken_result==0); - - woken=(woken_result==0); + woken=entry->woken(); } return woken; } @@ -182,10 +239,6 @@ namespace boost basic_condition_variable(const basic_condition_variable& other); basic_condition_variable& operator=(const basic_condition_variable& other); - static bool no_waiters(entry_ptr const& entry) - { - return !detail::interlocked_read_acquire(&entry->waiters); - } public: basic_condition_variable(): total_count(0),active_generation_count(0),wake_sem(0) @@ -211,7 +264,7 @@ namespace boost { (*it)->release(1); } - generations.erase(std::remove_if(generations.begin(),generations.end(),no_waiters),generations.end()); + generations.erase(std::remove_if(generations.begin(),generations.end(),&basic_cv_list_entry::no_waiters),generations.end()); } } @@ -229,7 +282,7 @@ namespace boost end=generations.end(); it!=end;++it) { - (*it)->release(detail::interlocked_read_acquire(&(*it)->waiters)); + (*it)->release_waiters(); } generations.clear(); wake_sem=detail::win32::handle(0); @@ -237,20 +290,6 @@ namespace boost } }; - inline void intrusive_ptr_add_ref(basic_condition_variable::list_entry * p) - { - BOOST_INTERLOCKED_INCREMENT(&p->references); - } - - inline void intrusive_ptr_release(basic_condition_variable::list_entry * p) - { - if(!BOOST_INTERLOCKED_DECREMENT(&p->references)) - { - delete p; - } - } - - } class condition_variable: From 5b83d81e407b91438892aa499e96e75a3af87ea1 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 4 Jun 2008 15:50:34 +0000 Subject: [PATCH 165/214] Added free function swap() for threads [SVN r46121] --- include/boost/thread/detail/thread.hpp | 5 +++++ test/test_thread.cpp | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/include/boost/thread/detail/thread.hpp b/include/boost/thread/detail/thread.hpp index 1ada38f9..afa22c32 100644 --- a/include/boost/thread/detail/thread.hpp +++ b/include/boost/thread/detail/thread.hpp @@ -311,6 +311,11 @@ namespace boost bool interruption_requested() const; }; + inline void swap(thread& lhs,thread& rhs) + { + return lhs.swap(rhs); + } + #ifdef BOOST_HAS_RVALUE_REFS inline thread&& move(thread&& t) { diff --git a/test/test_thread.cpp b/test/test_thread.cpp index 5535deb0..480e258a 100644 --- a/test/test_thread.cpp +++ b/test/test_thread.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf +// Copyright (C) 2008 Anthony Williams // // 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) @@ -198,6 +199,22 @@ void test_timed_join() timed_test(&do_test_timed_join, 10); } +void test_swap() +{ + boost::thread t(simple_thread); + boost::thread t2(simple_thread); + boost::thread::id id1=t.get_id(); + boost::thread::id id2=t2.get_id(); + + t.swap(t2); + BOOST_CHECK(t.get_id()==id2); + BOOST_CHECK(t2.get_id()==id1); + + swap(t,t2); + BOOST_CHECK(t.get_id()==id1); + BOOST_CHECK(t2.get_id()==id2); +} + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { @@ -211,6 +228,7 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) test->add(BOOST_TEST_CASE(test_thread_no_interrupt_if_interrupts_disabled_at_interruption_point)); test->add(BOOST_TEST_CASE(test_creation_through_reference_wrapper)); test->add(BOOST_TEST_CASE(test_timed_join)); + test->add(BOOST_TEST_CASE(test_swap)); return test; } From a5e95845b36f8b2db2afa46ab1bf9287b52747aa Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 4 Jun 2008 16:00:13 +0000 Subject: [PATCH 166/214] Added documentation for swap() [SVN r46122] --- doc/thread_ref.qbk | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk index 55ff306f..e9bc5150 100644 --- a/doc/thread_ref.qbk +++ b/doc/thread_ref.qbk @@ -206,6 +206,8 @@ __thread_id__ yield a total order for every non-equal thread ID. static void sleep(const system_time& xt); }; + void swap(thread& lhs,thread& rhs); + [section:default_constructor Default Constructor] thread(); @@ -451,6 +453,36 @@ implementation. If no such instance exists, `native_handle()` and `native_handle [endsect] +[section:swap Member function `swap()`] + + void swap(thread& other); + +[variablelist + +[[Effects:] [Exchanges the threads of execution associated with `*this` and `other`, so `*this` is associated with the thread of +execution associated with `other` prior to the call, and vice-versa.]] + +[[Postconditions:] [`this->get_id()` returns the same value as `other.get_id()` prior to the call. `other.get_id()` returns the same +value as `this->get_id()` prior to the call.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:non_member_swap Non-member function `swap()`] + + void swap(thread& lhs,thread& rhs); + +[variablelist + +[[Effects:] [[link thread.thread_management.thread.swap `lhs.swap(rhs)`].]] + +] + +[endsect] + [section:id Class `boost::thread::id`] From 5882a675bb952db0c2c0e6faea2d96daa6035071 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 4 Jun 2008 16:03:51 +0000 Subject: [PATCH 167/214] Added extra initializer to timeout to try and eliminate warnings with some compilers [SVN r46123] --- include/boost/thread/pthread/timespec.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/thread/pthread/timespec.hpp b/include/boost/thread/pthread/timespec.hpp index 105030ff..d7465c1a 100644 --- a/include/boost/thread/pthread/timespec.hpp +++ b/include/boost/thread/pthread/timespec.hpp @@ -21,7 +21,7 @@ namespace boost { inline struct timespec get_timespec(boost::system_time const& abs_time) { - struct timespec timeout={0}; + struct timespec timeout={0,0}; boost::posix_time::time_duration const time_since_epoch=abs_time-boost::posix_time::from_time_t(0); timeout.tv_sec=time_since_epoch.total_seconds(); From 6c60cce60d50b851d9e8cc534c37006e7b7d4ccc Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 4 Jun 2008 16:05:29 +0000 Subject: [PATCH 168/214] Removed partial initializer for res: both values will be assigned later, so no need to initialize either [SVN r46124] --- include/boost/thread/xtime.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/thread/xtime.hpp b/include/boost/thread/xtime.hpp index 54f91720..7cc6272d 100644 --- a/include/boost/thread/xtime.hpp +++ b/include/boost/thread/xtime.hpp @@ -58,7 +58,7 @@ struct xtime inline xtime get_xtime(boost::system_time const& abs_time) { - xtime res={0}; + xtime res; boost::posix_time::time_duration const time_since_epoch=abs_time-boost::posix_time::from_time_t(0); res.sec=static_cast(time_since_epoch.total_seconds()); From 8af680f3077403aa7fc503ed678675ee8182451d Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 5 Jun 2008 10:39:08 +0000 Subject: [PATCH 169/214] Added swap for unique_lock [SVN r46160] --- include/boost/thread/locks.hpp | 6 ++++++ test/test_lock_concept.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 8b183d38..3ceac7b5 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -275,6 +275,12 @@ namespace boost friend class upgrade_lock; }; + template + void swap(unique_lock& lhs,unique_lock& rhs) + { + lhs.swap(rhs); + } + template class shared_lock { diff --git a/test/test_lock_concept.cpp b/test/test_lock_concept.cpp index 6b8b483f..200892d8 100644 --- a/test/test_lock_concept.cpp +++ b/test/test_lock_concept.cpp @@ -196,6 +196,35 @@ struct test_default_constructed_has_no_mutex_and_unlocked }; }; + +template +struct test_locks_can_be_swapped +{ + void operator()() const + { + Mutex m1; + Mutex m2; + Lock l1(m1); + Lock l2(m2); + + BOOST_CHECK_EQUAL(l1.mutex(),&m1); + BOOST_CHECK_EQUAL(l2.mutex(),&m2); + + l1.swap(l2); + + BOOST_CHECK_EQUAL(l1.mutex(),&m2); + BOOST_CHECK_EQUAL(l2.mutex(),&m1); + + swap(l1,l2); + + BOOST_CHECK_EQUAL(l1.mutex(),&m1); + BOOST_CHECK_EQUAL(l2.mutex(),&m2); + + } +}; + + + BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_lock_concept,Mutex) { typedef typename Mutex::scoped_lock Lock; @@ -208,6 +237,7 @@ BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_lock_concept,Mutex) test_locked_after_lock_called()(); test_throws_if_lock_called_when_already_locked()(); test_throws_if_unlock_called_when_already_unlocked()(); + test_locks_can_be_swapped()(); } BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_try_lock_concept,Mutex) From 685e4d446bd6393a69314c6b7f8a80bfc02d7bfd Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 5 Jun 2008 11:16:05 +0000 Subject: [PATCH 170/214] Test and fix for bug #1958 on Win32 [SVN r46161] --- include/boost/thread/win32/tss.hpp | 10 +++++-- test/test_tss.cpp | 48 ++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/include/boost/thread/win32/tss.hpp b/include/boost/thread/win32/tss.hpp index a5f1c31c..82e3290f 100644 --- a/include/boost/thread/win32/tss.hpp +++ b/include/boost/thread/win32/tss.hpp @@ -64,9 +64,13 @@ namespace boost thread_specific_ptr(): cleanup(detail::heap_new(),detail::do_heap_delete()) {} - explicit thread_specific_ptr(void (*func_)(T*)): - cleanup(detail::heap_new(func_),detail::do_heap_delete()) - {} + explicit thread_specific_ptr(void (*func_)(T*)) + { + if(func_) + { + cleanup.reset(detail::heap_new(func_),detail::do_heap_delete()); + } + } ~thread_specific_ptr() { reset(); diff --git a/test/test_tss.cpp b/test/test_tss.cpp index bbd78d42..8a6545ce 100644 --- a/test/test_tss.cpp +++ b/test/test_tss.cpp @@ -258,11 +258,58 @@ void do_test_tss_does_no_cleanup_after_release() } } +struct dummy_class_tracks_deletions +{ + static unsigned deletions; + + ~dummy_class_tracks_deletions() + { + ++deletions; + } + +}; + +unsigned dummy_class_tracks_deletions::deletions=0; + +boost::thread_specific_ptr tss_with_null_cleanup(NULL); + +void tss_thread_with_null_cleanup(dummy_class_tracks_deletions* delete_tracker) +{ + tss_with_null_cleanup.reset(delete_tracker); +} + +void do_test_tss_does_no_cleanup_with_null_cleanup_function() +{ + dummy_class_tracks_deletions* delete_tracker=new dummy_class_tracks_deletions; + boost::thread t(tss_thread_with_null_cleanup,delete_tracker); + try + { + t.join(); + } + catch(...) + { + t.interrupt(); + t.join(); + throw; + } + + BOOST_CHECK(!dummy_class_tracks_deletions::deletions); + if(!dummy_class_tracks_deletions::deletions) + { + delete delete_tracker; + } +} + void test_tss_does_no_cleanup_after_release() { timed_test(&do_test_tss_does_no_cleanup_after_release, 2); } +void test_tss_does_no_cleanup_with_null_cleanup_function() +{ + timed_test(&do_test_tss_does_no_cleanup_with_null_cleanup_function, 2); +} + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = @@ -271,6 +318,7 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) test->add(BOOST_TEST_CASE(test_tss)); test->add(BOOST_TEST_CASE(test_tss_with_custom_cleanup)); test->add(BOOST_TEST_CASE(test_tss_does_no_cleanup_after_release)); + test->add(BOOST_TEST_CASE(test_tss_does_no_cleanup_with_null_cleanup_function)); return test; } From e984dff4e4e38a5bee427c4d38b0b928554e1148 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 5 Jun 2008 11:19:06 +0000 Subject: [PATCH 171/214] Combined TSS header for pthread and win32, #1958 now fixed for pthread too [SVN r46162] --- include/boost/thread/pthread/tss.hpp | 107 ------------------------ include/boost/thread/tss.hpp | 118 ++++++++++++++++++++++++--- include/boost/thread/win32/tss.hpp | 110 ------------------------- 3 files changed, 105 insertions(+), 230 deletions(-) delete mode 100644 include/boost/thread/pthread/tss.hpp delete mode 100644 include/boost/thread/win32/tss.hpp diff --git a/include/boost/thread/pthread/tss.hpp b/include/boost/thread/pthread/tss.hpp deleted file mode 100644 index 1f3cb246..00000000 --- a/include/boost/thread/pthread/tss.hpp +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef BOOST_THREAD_PTHREAD_TSS_HPP -#define BOOST_THREAD_PTHREAD_TSS_HPP - -// 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) -// (C) Copyright 2007-8 Anthony Williams - -#include -#include - -#include - -namespace boost -{ - namespace detail - { - struct tss_cleanup_function - { - virtual ~tss_cleanup_function() - {} - - virtual void operator()(void* data)=0; - }; - - BOOST_THREAD_DECL void set_tss_data(void const* key,boost::shared_ptr func,void* tss_data,bool cleanup_existing); - BOOST_THREAD_DECL void* get_tss_data(void const* key); - } - - template - class thread_specific_ptr - { - private: - thread_specific_ptr(thread_specific_ptr&); - thread_specific_ptr& operator=(thread_specific_ptr&); - - struct delete_data: - detail::tss_cleanup_function - { - void operator()(void* data) - { - delete static_cast(data); - } - }; - - struct run_custom_cleanup_function: - detail::tss_cleanup_function - { - void (*cleanup_function)(T*); - - explicit run_custom_cleanup_function(void (*cleanup_function_)(T*)): - cleanup_function(cleanup_function_) - {} - - void operator()(void* data) - { - cleanup_function(static_cast(data)); - } - }; - - - boost::shared_ptr cleanup; - - public: - thread_specific_ptr(): - cleanup(new delete_data) - {} - explicit thread_specific_ptr(void (*func_)(T*)): - cleanup(new run_custom_cleanup_function(func_)) - {} - ~thread_specific_ptr() - { - reset(); - } - - T* get() const - { - return static_cast(detail::get_tss_data(this)); - } - T* operator->() const - { - return get(); - } - T& operator*() const - { - return *get(); - } - T* release() - { - T* const temp=get(); - detail::set_tss_data(this,boost::shared_ptr(),0,false); - return temp; - } - void reset(T* new_value=0) - { - T* const current_value=get(); - if(current_value!=new_value) - { - detail::set_tss_data(this,cleanup,new_value,true); - } - } - }; -} - -#include - -#endif diff --git a/include/boost/thread/tss.hpp b/include/boost/thread/tss.hpp index 131bcde2..4efc6e8b 100644 --- a/include/boost/thread/tss.hpp +++ b/include/boost/thread/tss.hpp @@ -1,18 +1,110 @@ -// Copyright (C) 2007 Anthony Williams -// -// 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) - #ifndef BOOST_THREAD_TSS_HPP #define BOOST_THREAD_TSS_HPP +// 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) +// (C) Copyright 2007-8 Anthony Williams -#include -#if defined(BOOST_THREAD_PLATFORM_WIN32) -#include -#elif defined(BOOST_THREAD_PLATFORM_PTHREAD) -#include -#else -#error "Boost threads unavailable on this platform" -#endif +#include +#include + +#include + +namespace boost +{ + namespace detail + { + struct tss_cleanup_function + { + virtual ~tss_cleanup_function() + {} + + virtual void operator()(void* data)=0; + }; + + BOOST_THREAD_DECL void set_tss_data(void const* key,boost::shared_ptr func,void* tss_data,bool cleanup_existing); + BOOST_THREAD_DECL void* get_tss_data(void const* key); + } + + template + class thread_specific_ptr + { + private: + thread_specific_ptr(thread_specific_ptr&); + thread_specific_ptr& operator=(thread_specific_ptr&); + + struct delete_data: + detail::tss_cleanup_function + { + void operator()(void* data) + { + delete static_cast(data); + } + }; + + struct run_custom_cleanup_function: + detail::tss_cleanup_function + { + void (*cleanup_function)(T*); + + explicit run_custom_cleanup_function(void (*cleanup_function_)(T*)): + cleanup_function(cleanup_function_) + {} + + void operator()(void* data) + { + cleanup_function(static_cast(data)); + } + }; + + + boost::shared_ptr cleanup; + + public: + thread_specific_ptr(): + cleanup(detail::heap_new(),detail::do_heap_delete()) + {} + explicit thread_specific_ptr(void (*func_)(T*)) + { + if(func_) + { + cleanup.reset(detail::heap_new(func_),detail::do_heap_delete()); + } + } + ~thread_specific_ptr() + { + reset(); + } + + T* get() const + { + return static_cast(detail::get_tss_data(this)); + } + T* operator->() const + { + return get(); + } + T& operator*() const + { + return *get(); + } + T* release() + { + T* const temp=get(); + detail::set_tss_data(this,boost::shared_ptr(),0,false); + return temp; + } + void reset(T* new_value=0) + { + T* const current_value=get(); + if(current_value!=new_value) + { + detail::set_tss_data(this,cleanup,new_value,true); + } + } + }; +} + +#include #endif diff --git a/include/boost/thread/win32/tss.hpp b/include/boost/thread/win32/tss.hpp deleted file mode 100644 index 82e3290f..00000000 --- a/include/boost/thread/win32/tss.hpp +++ /dev/null @@ -1,110 +0,0 @@ -#ifndef BOOST_THREAD_WIN32_TSS_HPP -#define BOOST_THREAD_WIN32_TSS_HPP -// 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) -// (C) Copyright 2007-8 Anthony Williams - -#include -#include "thread_heap_alloc.hpp" - -#include - -namespace boost -{ - namespace detail - { - struct tss_cleanup_function - { - virtual ~tss_cleanup_function() - {} - - virtual void operator()(void* data)=0; - }; - - BOOST_THREAD_DECL void set_tss_data(void const* key,boost::shared_ptr func,void* tss_data,bool cleanup_existing); - BOOST_THREAD_DECL void* get_tss_data(void const* key); - } - - template - class thread_specific_ptr - { - private: - thread_specific_ptr(thread_specific_ptr&); - thread_specific_ptr& operator=(thread_specific_ptr&); - - struct delete_data: - detail::tss_cleanup_function - { - void operator()(void* data) - { - delete static_cast(data); - } - }; - - struct run_custom_cleanup_function: - detail::tss_cleanup_function - { - void (*cleanup_function)(T*); - - explicit run_custom_cleanup_function(void (*cleanup_function_)(T*)): - cleanup_function(cleanup_function_) - {} - - void operator()(void* data) - { - cleanup_function(static_cast(data)); - } - }; - - - boost::shared_ptr cleanup; - - public: - thread_specific_ptr(): - cleanup(detail::heap_new(),detail::do_heap_delete()) - {} - explicit thread_specific_ptr(void (*func_)(T*)) - { - if(func_) - { - cleanup.reset(detail::heap_new(func_),detail::do_heap_delete()); - } - } - ~thread_specific_ptr() - { - reset(); - } - - T* get() const - { - return static_cast(detail::get_tss_data(this)); - } - T* operator->() const - { - return get(); - } - T& operator*() const - { - return *get(); - } - T* release() - { - T* const temp=get(); - detail::set_tss_data(this,boost::shared_ptr(),0,false); - return temp; - } - void reset(T* new_value=0) - { - T* const current_value=get(); - if(current_value!=new_value) - { - detail::set_tss_data(this,cleanup,new_value,true); - } - } - }; -} - -#include - -#endif From a56887167ef19b8c24ff8f1fd709c4fd47c4957a Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 5 Jun 2008 12:25:58 +0000 Subject: [PATCH 172/214] Added swap for try_lock_wrapper [SVN r46164] --- include/boost/thread/locks.hpp | 6 ++++++ test/test_lock_concept.cpp | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 3ceac7b5..b175c859 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -729,6 +729,12 @@ namespace boost return static_cast(*this); } }; + + template + void swap(try_lock_wrapper& lhs,try_lock_wrapper& rhs) + { + lhs.swap(rhs); + } template unsigned try_lock_internal(MutexType1& m1,MutexType2& m2) diff --git a/test/test_lock_concept.cpp b/test/test_lock_concept.cpp index 200892d8..8b16dfb5 100644 --- a/test/test_lock_concept.cpp +++ b/test/test_lock_concept.cpp @@ -1,4 +1,4 @@ -// (C) Copyright 2006-7 Anthony Williams +// (C) Copyright 2006-8 Anthony Williams // 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) @@ -255,6 +255,7 @@ BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_try_lock_concept,Mutex) test_throws_if_lock_called_when_already_locked()(); test_throws_if_try_lock_called_when_already_locked()(); test_throws_if_unlock_called_when_already_unlocked()(); + test_locks_can_be_swapped()(); } boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) From 309acb959779dcc85156251a19c6f7541589a6a5 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Sat, 7 Jun 2008 20:54:19 +0000 Subject: [PATCH 173/214] Don't try and use _interlockedbittestandset primitives if we don't know they're present [SVN r46219] --- include/boost/thread/win32/thread_primitives.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/boost/thread/win32/thread_primitives.hpp b/include/boost/thread/win32/thread_primitives.hpp index 022dc9ee..de58c45b 100644 --- a/include/boost/thread/win32/thread_primitives.hpp +++ b/include/boost/thread/win32/thread_primitives.hpp @@ -281,8 +281,7 @@ namespace boost } } -#if defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN) -#if _MSC_VER>=1400 +#if defined(BOOST_MSVC) && (_MSC_VER>=1400) && !defined(UNDER_CE) #if _MSC_VER==1400 extern "C" unsigned char _interlockedbittestandset(long *a,long b); extern "C" unsigned char _interlockedbittestandreset(long *a,long b); @@ -314,7 +313,7 @@ namespace boost } } #define BOOST_THREAD_BTS_DEFINED -#elif defined(_M_IX86) +#elif (defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN)) && defined(_M_IX86) namespace boost { namespace detail @@ -346,7 +345,6 @@ namespace boost } #define BOOST_THREAD_BTS_DEFINED #endif -#endif #ifndef BOOST_THREAD_BTS_DEFINED From 9bebd7b35fc9fb60d4c64a8c447ee6212c51090a Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 9 Jun 2008 14:00:03 +0000 Subject: [PATCH 174/214] Disable general templated thread constructor for movable types, in order to prevent it trying to act as a thread copy constructor for EDG based compilers [SVN r46273] --- include/boost/thread/detail/thread.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/boost/thread/detail/thread.hpp b/include/boost/thread/detail/thread.hpp index afa22c32..a2f044d9 100644 --- a/include/boost/thread/detail/thread.hpp +++ b/include/boost/thread/detail/thread.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -134,6 +135,8 @@ namespace boost { return detail::thread_data_ptr(detail::heap_new >(f)); } + + struct dummy; #endif public: thread(); @@ -166,12 +169,12 @@ namespace boost #else template - explicit thread(F f): + explicit thread(F f,typename disable_if >, dummy* >::type=0): thread_info(make_thread_info(f)) { start_thread(); } - + template thread(detail::thread_move_t f): thread_info(make_thread_info(f)) From 851d6a987fcdd5b8d57859b284ae5adb19a3f497 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 10 Jun 2008 15:29:35 +0000 Subject: [PATCH 175/214] Correctly remove the reference type when copying the thread function into the thread data area so we don't end up with a dangling reference [SVN r46295] --- include/boost/thread/detail/thread.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/boost/thread/detail/thread.hpp b/include/boost/thread/detail/thread.hpp index a2f044d9..a7e82f7f 100644 --- a/include/boost/thread/detail/thread.hpp +++ b/include/boost/thread/detail/thread.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -122,7 +123,11 @@ namespace boost template static inline detail::thread_data_ptr make_thread_info(F&& f) { - return detail::thread_data_ptr(detail::heap_new >(static_cast(f))); + return detail::thread_data_ptr(detail::heap_new::type> >(static_cast(f))); + } + static inline detail::thread_data_ptr make_thread_info(void (*f)()) + { + return detail::thread_data_ptr(detail::heap_new >(f)); } #else template From 880bac06330b1a87ab488d71af7786c8ca6c010e Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 23 Jun 2008 12:14:58 +0000 Subject: [PATCH 176/214] Added missing include of detail/config.hpp [SVN r46624] --- include/boost/thread/tss.hpp | 221 ++++++++++++++++++----------------- 1 file changed, 111 insertions(+), 110 deletions(-) diff --git a/include/boost/thread/tss.hpp b/include/boost/thread/tss.hpp index 4efc6e8b..73248239 100644 --- a/include/boost/thread/tss.hpp +++ b/include/boost/thread/tss.hpp @@ -1,110 +1,111 @@ -#ifndef BOOST_THREAD_TSS_HPP -#define BOOST_THREAD_TSS_HPP -// 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) -// (C) Copyright 2007-8 Anthony Williams - -#include -#include - -#include - -namespace boost -{ - namespace detail - { - struct tss_cleanup_function - { - virtual ~tss_cleanup_function() - {} - - virtual void operator()(void* data)=0; - }; - - BOOST_THREAD_DECL void set_tss_data(void const* key,boost::shared_ptr func,void* tss_data,bool cleanup_existing); - BOOST_THREAD_DECL void* get_tss_data(void const* key); - } - - template - class thread_specific_ptr - { - private: - thread_specific_ptr(thread_specific_ptr&); - thread_specific_ptr& operator=(thread_specific_ptr&); - - struct delete_data: - detail::tss_cleanup_function - { - void operator()(void* data) - { - delete static_cast(data); - } - }; - - struct run_custom_cleanup_function: - detail::tss_cleanup_function - { - void (*cleanup_function)(T*); - - explicit run_custom_cleanup_function(void (*cleanup_function_)(T*)): - cleanup_function(cleanup_function_) - {} - - void operator()(void* data) - { - cleanup_function(static_cast(data)); - } - }; - - - boost::shared_ptr cleanup; - - public: - thread_specific_ptr(): - cleanup(detail::heap_new(),detail::do_heap_delete()) - {} - explicit thread_specific_ptr(void (*func_)(T*)) - { - if(func_) - { - cleanup.reset(detail::heap_new(func_),detail::do_heap_delete()); - } - } - ~thread_specific_ptr() - { - reset(); - } - - T* get() const - { - return static_cast(detail::get_tss_data(this)); - } - T* operator->() const - { - return get(); - } - T& operator*() const - { - return *get(); - } - T* release() - { - T* const temp=get(); - detail::set_tss_data(this,boost::shared_ptr(),0,false); - return temp; - } - void reset(T* new_value=0) - { - T* const current_value=get(); - if(current_value!=new_value) - { - detail::set_tss_data(this,cleanup,new_value,true); - } - } - }; -} - -#include - -#endif +#ifndef BOOST_THREAD_TSS_HPP +#define BOOST_THREAD_TSS_HPP +// 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) +// (C) Copyright 2007-8 Anthony Williams + +#include +#include +#include + +#include + +namespace boost +{ + namespace detail + { + struct tss_cleanup_function + { + virtual ~tss_cleanup_function() + {} + + virtual void operator()(void* data)=0; + }; + + BOOST_THREAD_DECL void set_tss_data(void const* key,boost::shared_ptr func,void* tss_data,bool cleanup_existing); + BOOST_THREAD_DECL void* get_tss_data(void const* key); + } + + template + class thread_specific_ptr + { + private: + thread_specific_ptr(thread_specific_ptr&); + thread_specific_ptr& operator=(thread_specific_ptr&); + + struct delete_data: + detail::tss_cleanup_function + { + void operator()(void* data) + { + delete static_cast(data); + } + }; + + struct run_custom_cleanup_function: + detail::tss_cleanup_function + { + void (*cleanup_function)(T*); + + explicit run_custom_cleanup_function(void (*cleanup_function_)(T*)): + cleanup_function(cleanup_function_) + {} + + void operator()(void* data) + { + cleanup_function(static_cast(data)); + } + }; + + + boost::shared_ptr cleanup; + + public: + thread_specific_ptr(): + cleanup(detail::heap_new(),detail::do_heap_delete()) + {} + explicit thread_specific_ptr(void (*func_)(T*)) + { + if(func_) + { + cleanup.reset(detail::heap_new(func_),detail::do_heap_delete()); + } + } + ~thread_specific_ptr() + { + reset(); + } + + T* get() const + { + return static_cast(detail::get_tss_data(this)); + } + T* operator->() const + { + return get(); + } + T& operator*() const + { + return *get(); + } + T* release() + { + T* const temp=get(); + detail::set_tss_data(this,boost::shared_ptr(),0,false); + return temp; + } + void reset(T* new_value=0) + { + T* const current_value=get(); + if(current_value!=new_value) + { + detail::set_tss_data(this,cleanup,new_value,true); + } + } + }; +} + +#include + +#endif From eb30688937adebd336ab935adeb2c04768836976 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 26 Jun 2008 06:41:00 +0000 Subject: [PATCH 177/214] Added license and copyright to docs [SVN r46705] --- doc/acknowledgements.qbk | 7 +++++++ doc/barrier.qbk | 7 +++++++ doc/changes.qbk | 7 +++++++ doc/condition_variables.qbk | 7 +++++++ doc/mutex_concepts.qbk | 7 +++++++ doc/mutexes.qbk | 7 +++++++ doc/once.qbk | 7 +++++++ doc/overview.qbk | 7 +++++++ doc/shared_mutex_ref.qbk | 7 +++++++ doc/thread.qbk | 7 +++++++ doc/thread_ref.qbk | 7 +++++++ doc/time.qbk | 7 +++++++ doc/tss.qbk | 7 +++++++ 13 files changed, 91 insertions(+) diff --git a/doc/acknowledgements.qbk b/doc/acknowledgements.qbk index ec008cf3..1f21921f 100644 --- a/doc/acknowledgements.qbk +++ b/doc/acknowledgements.qbk @@ -1,3 +1,10 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + 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). +] + [section:acknowledgements Acknowledgments] The original implementation of __boost_thread__ was written by William Kempf, with contributions from numerous others. This new diff --git a/doc/barrier.qbk b/doc/barrier.qbk index c5df571d..7e7c3e12 100644 --- a/doc/barrier.qbk +++ b/doc/barrier.qbk @@ -1,3 +1,10 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + 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). +] + [section:barriers Barriers] A barrier is a simple concept. Also known as a ['rendezvous], it is a synchronization point between multiple threads. The barrier is diff --git a/doc/changes.qbk b/doc/changes.qbk index 62a6630f..bfbc55ae 100644 --- a/doc/changes.qbk +++ b/doc/changes.qbk @@ -1,3 +1,10 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + 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). +] + [section:changes Changes since boost 1.34] Almost every line of code in __boost_thread__ has been changed since the 1.34 release of boost. However, most of the interface diff --git a/doc/condition_variables.qbk b/doc/condition_variables.qbk index e793a777..7ca68950 100644 --- a/doc/condition_variables.qbk +++ b/doc/condition_variables.qbk @@ -1,3 +1,10 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + 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). +] + [section:condvar_ref Condition Variables] [heading Synopsis] diff --git a/doc/mutex_concepts.qbk b/doc/mutex_concepts.qbk index 2fc5cb97..0440dc0f 100644 --- a/doc/mutex_concepts.qbk +++ b/doc/mutex_concepts.qbk @@ -1,3 +1,10 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + 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). +] + [section:mutex_concepts Mutex Concepts] A mutex object facilitates protection against data races and allows thread-safe synchronization of data between threads. A thread diff --git a/doc/mutexes.qbk b/doc/mutexes.qbk index cf920235..3a6e1efb 100644 --- a/doc/mutexes.qbk +++ b/doc/mutexes.qbk @@ -1,3 +1,10 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + 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). +] + [section:mutex_types Mutex Types] [section:mutex Class `mutex`] diff --git a/doc/once.qbk b/doc/once.qbk index 86b0ef2b..e6ead35a 100644 --- a/doc/once.qbk +++ b/doc/once.qbk @@ -1,3 +1,10 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + 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). +] + [section:once One-time Initialization] `boost::call_once` provides a mechanism for ensuring that an initialization routine is run exactly once without data races or deadlocks. diff --git a/doc/overview.qbk b/doc/overview.qbk index 63edba0f..f9b867b7 100644 --- a/doc/overview.qbk +++ b/doc/overview.qbk @@ -1,3 +1,10 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + 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). +] + [section:overview Overview] __boost_thread__ enables the use of multiple threads of execution with shared data in portable C++ code. It provides classes and diff --git a/doc/shared_mutex_ref.qbk b/doc/shared_mutex_ref.qbk index 4b2c2ec4..e7bf657e 100644 --- a/doc/shared_mutex_ref.qbk +++ b/doc/shared_mutex_ref.qbk @@ -1,3 +1,10 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + 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). +] + [section:shared_mutex Class `shared_mutex`] class shared_mutex diff --git a/doc/thread.qbk b/doc/thread.qbk index b2ea6e6b..693ea648 100644 --- a/doc/thread.qbk +++ b/doc/thread.qbk @@ -1,3 +1,10 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + 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). +] + [article Thread [quickbook 1.4] [authors [Williams, Anthony]] diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk index e9bc5150..e94bbd2d 100644 --- a/doc/thread_ref.qbk +++ b/doc/thread_ref.qbk @@ -1,3 +1,10 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + 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). +] + [section:thread_management Thread Management] [heading Synopsis] diff --git a/doc/time.qbk b/doc/time.qbk index f817206c..321a9c6a 100644 --- a/doc/time.qbk +++ b/doc/time.qbk @@ -1,3 +1,10 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + 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). +] + [section:time Date and Time Requirements] As of Boost 1.35.0, the __boost_thread__ library uses the [link date_time Boost.Date_Time] library for all operations that require a diff --git a/doc/tss.qbk b/doc/tss.qbk index b6e1ff69..d3a61bd7 100644 --- a/doc/tss.qbk +++ b/doc/tss.qbk @@ -1,3 +1,10 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + 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). +] + [section Thread Local Storage] [heading Synopsis] From 77130424b40fddbe7638d630d7871af8f1af9bb4 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 26 Jun 2008 06:43:30 +0000 Subject: [PATCH 178/214] Removed tabs from source files [SVN r46706] --- include/boost/thread/detail/platform.hpp | 6 +++--- include/boost/thread/win32/mutex.hpp | 2 +- include/boost/thread/win32/thread_primitives.hpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/boost/thread/detail/platform.hpp b/include/boost/thread/detail/platform.hpp index 32a8a22b..4a37cf38 100644 --- a/include/boost/thread/detail/platform.hpp +++ b/include/boost/thread/detail/platform.hpp @@ -42,9 +42,9 @@ #elif defined(__QNXNTO__) # define BOOST_THREAD_QNXNTO #elif defined(unix) || defined(__unix) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) -# if defined(BOOST_HAS_PTHREADS) && !defined(BOOST_THREAD_POSIX) -# define BOOST_THREAD_POSIX -# endif +# if defined(BOOST_HAS_PTHREADS) && !defined(BOOST_THREAD_POSIX) +# define BOOST_THREAD_POSIX +# endif #endif // For every supported platform add a new entry into the dispatch table below. diff --git a/include/boost/thread/win32/mutex.hpp b/include/boost/thread/win32/mutex.hpp index 3fa11d9c..efe6241d 100644 --- a/include/boost/thread/win32/mutex.hpp +++ b/include/boost/thread/win32/mutex.hpp @@ -20,7 +20,7 @@ namespace boost } class mutex: - boost::noncopyable, + boost::noncopyable, public ::boost::detail::underlying_mutex { public: diff --git a/include/boost/thread/win32/thread_primitives.hpp b/include/boost/thread/win32/thread_primitives.hpp index de58c45b..55062fa1 100644 --- a/include/boost/thread/win32/thread_primitives.hpp +++ b/include/boost/thread/win32/thread_primitives.hpp @@ -64,7 +64,7 @@ namespace boost # ifdef UNDER_CE # ifndef WINAPI # ifndef _WIN32_WCE_EMULATION -# define WINAPI __cdecl // Note this doesn't match the desktop definition +# define WINAPI __cdecl // Note this doesn't match the desktop definition # else # define WINAPI __stdcall # endif From d24a5790336e99528d9c5c4354914ed827695f4e Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 1 Jul 2008 16:04:43 +0000 Subject: [PATCH 179/214] Partial fix for issue #1867 - ensure boost::shared_mutex supports try_lock [SVN r46955] --- test/test_lock_concept.cpp | 98 ++++++++++++++++++++++++++++++++++---- 1 file changed, 89 insertions(+), 9 deletions(-) diff --git a/test/test_lock_concept.cpp b/test/test_lock_concept.cpp index 8b16dfb5..5530398f 100644 --- a/test/test_lock_concept.cpp +++ b/test/test_lock_concept.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -149,6 +150,64 @@ struct test_locked_after_try_lock_called } }; +template +struct test_unlocked_after_try_lock_if_other_thread_has_lock +{ + Mutex m; + boost::mutex done_mutex; + bool done; + bool locked; + boost::condition_variable done_cond; + + test_unlocked_after_try_lock_if_other_thread_has_lock(): + done(false),locked(false) + {} + + void locking_thread() + { + Lock lock(m,boost::defer_lock); + + boost::lock_guard lk(done_mutex); + locked=lock.owns_lock(); + done=true; + done_cond.notify_one(); + } + + bool is_done() const + { + return done; + } + + + void operator()() + { + Lock lock(m); + + typedef test_unlocked_after_try_lock_if_other_thread_has_lock this_type; + + boost::thread t(&this_type::locking_thread,this); + + try + { + { + boost::mutex::scoped_lock lk(done_mutex); + BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), + boost::bind(&this_type::is_done,this))); + BOOST_CHECK(!locked); + } + + lock.unlock(); + t.join(); + } + catch(...) + { + lock.unlock(); + t.join(); + throw; + } + } +}; + template struct test_throws_if_lock_called_when_already_locked { @@ -223,12 +282,9 @@ struct test_locks_can_be_swapped } }; - - -BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_lock_concept,Mutex) +template +void test_lock_is_scoped_lock_concept_for_mutex() { - typedef typename Mutex::scoped_lock Lock; - test_default_constructed_has_no_mutex_and_unlocked()(); test_initially_locked()(); test_initially_unlocked_with_defer_lock_parameter()(); @@ -238,6 +294,24 @@ BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_lock_concept,Mutex) test_throws_if_lock_called_when_already_locked()(); test_throws_if_unlock_called_when_already_unlocked()(); test_locks_can_be_swapped()(); + test_locked_after_try_lock_called()(); + test_throws_if_try_lock_called_when_already_locked()(); + test_unlocked_after_try_lock_if_other_thread_has_lock()(); +} + + +BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_lock_concept,Mutex) +{ + typedef typename Mutex::scoped_lock Lock; + + test_lock_is_scoped_lock_concept_for_mutex(); +} + +BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_unique_lock_is_scoped_lock,Mutex) +{ + typedef boost::unique_lock Lock; + + test_lock_is_scoped_lock_concept_for_mutex(); } BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_try_lock_concept,Mutex) @@ -252,6 +326,7 @@ BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_try_lock_concept,Mutex) test_unlocked_after_unlock_called()(); test_locked_after_lock_called()(); test_locked_after_try_lock_called()(); + test_unlocked_after_try_lock_if_other_thread_has_lock()(); test_throws_if_lock_called_when_already_locked()(); test_throws_if_try_lock_called_when_already_locked()(); test_throws_if_unlock_called_when_already_unlocked()(); @@ -264,14 +339,19 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) BOOST_TEST_SUITE("Boost.Threads: lock concept test suite"); typedef boost::mpl::vector mutex_types; + boost::recursive_mutex,boost::recursive_try_mutex,boost::recursive_timed_mutex> mutex_types_with_scoped_lock; - test->add(BOOST_TEST_CASE_TEMPLATE(test_scoped_lock_concept,mutex_types)); + test->add(BOOST_TEST_CASE_TEMPLATE(test_scoped_lock_concept,mutex_types_with_scoped_lock)); typedef boost::mpl::vector try_mutex_types; + boost::recursive_try_mutex,boost::recursive_timed_mutex> mutex_types_with_scoped_try_lock; - test->add(BOOST_TEST_CASE_TEMPLATE(test_scoped_try_lock_concept,try_mutex_types)); + test->add(BOOST_TEST_CASE_TEMPLATE(test_scoped_try_lock_concept,mutex_types_with_scoped_try_lock)); + + typedef boost::mpl::vector all_mutex_types; + + test->add(BOOST_TEST_CASE_TEMPLATE(test_unique_lock_is_scoped_lock,all_mutex_types)); return test; } From 495e561398acd8d8c081872f120001eb2ed9dd5e Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 1 Jul 2008 16:04:51 +0000 Subject: [PATCH 180/214] Partial fix for issue #1867 - ensure boost::shared_mutex supports try_lock [SVN r46956] --- include/boost/thread/win32/shared_mutex.hpp | 183 +++++--------------- 1 file changed, 42 insertions(+), 141 deletions(-) diff --git a/include/boost/thread/win32/shared_mutex.hpp b/include/boost/thread/win32/shared_mutex.hpp index 9f3c489e..7a579462 100644 --- a/include/boost/thread/win32/shared_mutex.hpp +++ b/include/boost/thread/win32/shared_mutex.hpp @@ -91,7 +91,7 @@ namespace boost bool try_lock_shared() { state_data old_state=state; - do + for(;;) { state_data new_state=old_state; if(!new_state.exclusive && !new_state.exclusive_waiting_blocked) @@ -106,14 +106,6 @@ namespace boost } old_state=current_state; } -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true); -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif return !(old_state.exclusive| old_state.exclusive_waiting_blocked); } @@ -130,17 +122,10 @@ namespace boost bool timed_lock_shared(boost::system_time const& wait_until) { -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true) -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif + for(;;) { state_data old_state=state; - do + for(;;) { state_data new_state=old_state; if(new_state.exclusive || new_state.exclusive_waiting_blocked) @@ -159,14 +144,6 @@ namespace boost } old_state=current_state; } -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true); -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif if(!(old_state.exclusive| old_state.exclusive_waiting_blocked)) { @@ -176,7 +153,7 @@ namespace boost unsigned long const res=detail::win32::WaitForSingleObject(unlock_sem,::boost::detail::get_milliseconds_until(wait_until)); if(res==detail::win32::timeout) { - do + for(;;) { state_data new_state=old_state; if(new_state.exclusive || new_state.exclusive_waiting_blocked) @@ -198,14 +175,6 @@ namespace boost } old_state=current_state; } -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true); -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif if(!(old_state.exclusive| old_state.exclusive_waiting_blocked)) { @@ -221,7 +190,7 @@ namespace boost void unlock_shared() { state_data old_state=state; - do + for(;;) { state_data new_state=old_state; bool const last_reader=!--new_state.shared_count; @@ -262,14 +231,6 @@ namespace boost } old_state=current_state; } -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true); -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif } void lock() @@ -283,20 +244,39 @@ namespace boost return timed_lock(get_system_time()+relative_time); } + bool try_lock() + { + state_data old_state=state; + for(;;) + { + state_data new_state=old_state; + if(new_state.shared_count || new_state.exclusive) + { + return false; + } + else + { + new_state.exclusive=true; + } + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + break; + } + old_state=current_state; + } + return true; + } + + bool timed_lock(boost::system_time const& wait_until) { -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true) -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif + for(;;) { state_data old_state=state; - do + for(;;) { state_data new_state=old_state; if(new_state.shared_count || new_state.exclusive) @@ -316,14 +296,6 @@ namespace boost } old_state=current_state; } -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true); -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif if(!old_state.shared_count && !old_state.exclusive) { @@ -332,7 +304,7 @@ namespace boost unsigned long const wait_res=detail::win32::WaitForMultipleObjects(2,semaphores,true,::boost::detail::get_milliseconds_until(wait_until)); if(wait_res==detail::win32::timeout) { - do + for(;;) { state_data new_state=old_state; if(new_state.shared_count || new_state.exclusive) @@ -357,14 +329,6 @@ namespace boost } old_state=current_state; } -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true); -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif if(!old_state.shared_count && !old_state.exclusive) { return true; @@ -378,7 +342,7 @@ namespace boost void unlock() { state_data old_state=state; - do + for(;;) { state_data new_state=old_state; new_state.exclusive=false; @@ -396,30 +360,15 @@ namespace boost } old_state=current_state; } -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true); -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif release_waiters(old_state); } void lock_upgrade() { -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true) -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif + for(;;) { state_data old_state=state; - do + for(;;) { state_data new_state=old_state; if(new_state.exclusive || new_state.exclusive_waiting_blocked || new_state.upgrade) @@ -439,14 +388,6 @@ namespace boost } old_state=current_state; } -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true); -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif if(!(old_state.exclusive|| old_state.exclusive_waiting_blocked|| old_state.upgrade)) { @@ -460,7 +401,7 @@ namespace boost void unlock_upgrade() { state_data old_state=state; - do + for(;;) { state_data new_state=old_state; new_state.upgrade=false; @@ -487,20 +428,12 @@ namespace boost } old_state=current_state; } -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true); -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif } void unlock_upgrade_and_lock() { state_data old_state=state; - do + for(;;) { state_data new_state=old_state; bool const last_reader=!--new_state.shared_count; @@ -522,20 +455,12 @@ namespace boost } old_state=current_state; } -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true); -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif } void unlock_and_lock_upgrade() { state_data old_state=state; - do + for(;;) { state_data new_state=old_state; new_state.exclusive=false; @@ -555,21 +480,13 @@ namespace boost } old_state=current_state; } -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true); -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif release_waiters(old_state); } void unlock_and_lock_shared() { state_data old_state=state; - do + for(;;) { state_data new_state=old_state; new_state.exclusive=false; @@ -588,21 +505,13 @@ namespace boost } old_state=current_state; } -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true); -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif release_waiters(old_state); } void unlock_upgrade_and_lock_shared() { state_data old_state=state; - do + for(;;) { state_data new_state=old_state; new_state.upgrade=false; @@ -620,14 +529,6 @@ namespace boost } old_state=current_state; } -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable:4127) -#endif - while(true); -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif release_waiters(old_state); } From 373f557ef77dbf0b3f00c381c5c7c9cdde14c1f5 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 1 Jul 2008 16:22:47 +0000 Subject: [PATCH 181/214] Reduced thread counts to make tests run faster [SVN r46958] --- test/test_shared_mutex.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/test_shared_mutex.cpp b/test/test_shared_mutex.cpp index a8daef4b..2cd4502b 100644 --- a/test/test_shared_mutex.cpp +++ b/test/test_shared_mutex.cpp @@ -17,7 +17,7 @@ void test_multiple_readers() { - unsigned const number_of_threads=100; + unsigned const number_of_threads=10; boost::thread_group pool; @@ -64,7 +64,7 @@ void test_multiple_readers() void test_only_one_writer_permitted() { - unsigned const number_of_threads=100; + unsigned const number_of_threads=10; boost::thread_group pool; @@ -164,7 +164,7 @@ void test_unlocking_writer_unblocks_all_readers() boost::mutex finish_mutex; boost::mutex::scoped_lock finish_lock(finish_mutex); - unsigned const reader_count=100; + unsigned const reader_count=10; try { @@ -218,8 +218,8 @@ void test_unlocking_last_reader_only_unblocks_one_writer() boost::mutex finish_writing_mutex; boost::mutex::scoped_lock finish_writing_lock(finish_writing_mutex); - unsigned const reader_count=100; - unsigned const writer_count=100; + unsigned const reader_count=10; + unsigned const writer_count=10; try { From 4462124ff2d9f01502839e3d8165882f2ab2240e Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 1 Jul 2008 16:27:59 +0000 Subject: [PATCH 182/214] Added try_lock_upgrade to shared_mutex: second half of #1867 fix [SVN r46960] --- include/boost/thread/win32/shared_mutex.hpp | 26 +++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/include/boost/thread/win32/shared_mutex.hpp b/include/boost/thread/win32/shared_mutex.hpp index 7a579462..58e8093a 100644 --- a/include/boost/thread/win32/shared_mutex.hpp +++ b/include/boost/thread/win32/shared_mutex.hpp @@ -398,6 +398,32 @@ namespace boost } } + bool try_lock_upgrade() + { + state_data old_state=state; + for(;;) + { + state_data new_state=old_state; + if(new_state.exclusive || new_state.exclusive_waiting_blocked || new_state.upgrade) + { + return false; + } + else + { + ++new_state.shared_count; + new_state.upgrade=true; + } + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + break; + } + old_state=current_state; + } + return true; + } + void unlock_upgrade() { state_data old_state=state; From 3c48a054370ae8cb153623f664c1223ff069d597 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 1 Jul 2008 16:28:06 +0000 Subject: [PATCH 183/214] Added try_lock_upgrade to shared_mutex: second half of #1867 fix [SVN r46961] --- test/test_shared_mutex_part_2.cpp | 115 +++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) diff --git a/test/test_shared_mutex_part_2.cpp b/test/test_shared_mutex_part_2.cpp index 2daa8582..ae38838b 100644 --- a/test/test_shared_mutex_part_2.cpp +++ b/test/test_shared_mutex_part_2.cpp @@ -15,10 +15,41 @@ BOOST_CHECK_EQUAL(value,expected_value); \ } +class simple_upgrade_thread +{ + boost::shared_mutex& rwm; + boost::mutex& finish_mutex; + boost::mutex& unblocked_mutex; + unsigned& unblocked_count; + + void operator=(simple_upgrade_thread&); + +public: + simple_upgrade_thread(boost::shared_mutex& rwm_, + boost::mutex& finish_mutex_, + boost::mutex& unblocked_mutex_, + unsigned& unblocked_count_): + rwm(rwm_),finish_mutex(finish_mutex_), + unblocked_mutex(unblocked_mutex_),unblocked_count(unblocked_count_) + {} + + void operator()() + { + boost::upgrade_lock lk(rwm); + + { + boost::mutex::scoped_lock ulk(unblocked_mutex); + ++unblocked_count; + } + + boost::mutex::scoped_lock flk(finish_mutex); + } +}; + void test_only_one_upgrade_lock_permitted() { - unsigned const number_of_threads=100; + unsigned const number_of_threads=10; boost::thread_group pool; @@ -71,7 +102,7 @@ void test_can_lock_upgrade_if_currently_locked_shared() boost::mutex finish_mutex; boost::mutex::scoped_lock finish_lock(finish_mutex); - unsigned const reader_count=100; + unsigned const reader_count=10; try { @@ -130,6 +161,29 @@ void test_if_other_thread_has_write_lock_try_lock_shared_returns_false() writer.join(); } +void test_if_other_thread_has_write_lock_try_lock_upgrade_returns_false() +{ + + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + unsigned unblocked_count=0; + boost::mutex::scoped_lock finish_lock(finish_mutex); + boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); + boost::this_thread::sleep(boost::posix_time::seconds(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); + + bool const try_succeeded=rw_mutex.try_lock_upgrade(); + BOOST_CHECK(!try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_upgrade(); + } + + finish_lock.unlock(); + writer.join(); +} + void test_if_no_thread_has_lock_try_lock_shared_returns_true() { boost::shared_mutex rw_mutex; @@ -141,6 +195,17 @@ void test_if_no_thread_has_lock_try_lock_shared_returns_true() } } +void test_if_no_thread_has_lock_try_lock_upgrade_returns_true() +{ + boost::shared_mutex rw_mutex; + bool const try_succeeded=rw_mutex.try_lock_upgrade(); + BOOST_CHECK(try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_upgrade(); + } +} + void test_if_other_thread_has_shared_lock_try_lock_shared_returns_true() { @@ -164,6 +229,52 @@ void test_if_other_thread_has_shared_lock_try_lock_shared_returns_true() writer.join(); } +void test_if_other_thread_has_shared_lock_try_lock_upgrade_returns_true() +{ + + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + unsigned unblocked_count=0; + boost::mutex::scoped_lock finish_lock(finish_mutex); + boost::thread writer(simple_reading_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); + + bool const try_succeeded=rw_mutex.try_lock_upgrade(); + BOOST_CHECK(try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_upgrade(); + } + + finish_lock.unlock(); + writer.join(); +} + +void test_if_other_thread_has_upgrade_lock_try_lock_upgrade_returns_false() +{ + + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + unsigned unblocked_count=0; + boost::mutex::scoped_lock finish_lock(finish_mutex); + boost::thread writer(simple_upgrade_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); + boost::this_thread::sleep(boost::posix_time::seconds(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); + + bool const try_succeeded=rw_mutex.try_lock_upgrade(); + BOOST_CHECK(!try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_upgrade(); + } + + finish_lock.unlock(); + writer.join(); +} + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = From 5d9ad59af2f83aab8e3f136003c6eaf01a1e1848 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Thu, 3 Jul 2008 09:16:49 +0000 Subject: [PATCH 184/214] Use rvalue refs for move semantics of unique_lock where available [SVN r47033] --- include/boost/thread/locks.hpp | 57 ++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index b175c859..8cdd8c8d 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -126,8 +126,10 @@ namespace boost private: Mutex* m; bool is_locked; - explicit unique_lock(unique_lock&); + unique_lock(unique_lock&); + explicit unique_lock(upgrade_lock&); unique_lock& operator=(unique_lock&); + unique_lock& operator=(upgrade_lock& other); public: unique_lock(): m(0),is_locked(false) @@ -154,6 +156,35 @@ namespace boost { timed_lock(target_time); } +#ifdef BOOST_HAS_RVALUE_REFS + unique_lock(unique_lock&& other): + m(other.m),is_locked(other.is_locked) + { + other.is_locked=false; + other.m=0; + } + explicit unique_lock(upgrade_lock&& other); + + unique_lock&& move() + { + return static_cast&&>(*this); + } + + + unique_lock& operator=(unique_lock&& other) + { + unique_lock temp(other); + swap(temp); + return *this; + } + + unique_lock& operator=(upgrade_lock&& other) + { + unique_lock temp(other); + swap(temp); + return *this; + } +#else unique_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { @@ -185,7 +216,7 @@ namespace boost swap(temp); return *this; } - +#endif void swap(unique_lock& other) { std::swap(m,other.m); @@ -281,6 +312,14 @@ namespace boost lhs.swap(rhs); } +#ifdef BOOST_HAS_RVALUE_REFS + template + inline unique_lock&& move(unique_lock&& ul) + { + return ul; + } +#endif + template class shared_lock { @@ -576,6 +615,18 @@ namespace boost }; +#ifdef BOOST_HAS_RVALUE_REFS + template + unique_lock::unique_lock(upgrade_lock&& other): + m(other.m),is_locked(other.is_locked) + { + other.is_locked=false; + if(is_locked) + { + m.unlock_upgrade_and_lock(); + } + } +#else template unique_lock::unique_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) @@ -586,7 +637,7 @@ namespace boost m->unlock_upgrade_and_lock(); } } - +#endif template class upgrade_to_unique_lock { From 6ac5e6953a521ba15ba2f204d84095fce535c8a2 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 4 Jul 2008 07:30:35 +0000 Subject: [PATCH 185/214] Qualify everything with boost:: to try and avoid name clashes on AIX [SVN r47070] --- test/test_thread_mf.cpp | 94 ++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/test/test_thread_mf.cpp b/test/test_thread_mf.cpp index fa1f8a9e..aa5a5dc1 100644 --- a/test/test_thread_mf.cpp +++ b/test/test_thread_mf.cpp @@ -45,92 +45,90 @@ struct X int main() { - using namespace boost; - X x; // 0 - thread( &X::f0, &x ).join(); - thread( &X::f0, ref(x) ).join(); + boost::thread( &X::f0, &x ).join(); + boost::thread( &X::f0, boost::ref(x) ).join(); - thread( &X::g0, &x ).join(); - thread( &X::g0, x ).join(); - thread( &X::g0, ref(x) ).join(); + boost::thread( &X::g0, &x ).join(); + boost::thread( &X::g0, x ).join(); + boost::thread( &X::g0, boost::ref(x) ).join(); // 1 - thread( &X::f1, &x, 1 ).join(); - thread( &X::f1, ref(x), 1 ).join(); + boost::thread( &X::f1, &x, 1 ).join(); + boost::thread( &X::f1, boost::ref(x), 1 ).join(); - thread( &X::g1, &x, 1 ).join(); - thread( &X::g1, x, 1 ).join(); - thread( &X::g1, ref(x), 1 ).join(); + boost::thread( &X::g1, &x, 1 ).join(); + boost::thread( &X::g1, x, 1 ).join(); + boost::thread( &X::g1, boost::ref(x), 1 ).join(); // 2 - thread( &X::f2, &x, 1, 2 ).join(); - thread( &X::f2, ref(x), 1, 2 ).join(); + boost::thread( &X::f2, &x, 1, 2 ).join(); + boost::thread( &X::f2, boost::ref(x), 1, 2 ).join(); - thread( &X::g2, &x, 1, 2 ).join(); - thread( &X::g2, x, 1, 2 ).join(); - thread( &X::g2, ref(x), 1, 2 ).join(); + boost::thread( &X::g2, &x, 1, 2 ).join(); + boost::thread( &X::g2, x, 1, 2 ).join(); + boost::thread( &X::g2, boost::ref(x), 1, 2 ).join(); // 3 - thread( &X::f3, &x, 1, 2, 3 ).join(); - thread( &X::f3, ref(x), 1, 2, 3 ).join(); + boost::thread( &X::f3, &x, 1, 2, 3 ).join(); + boost::thread( &X::f3, boost::ref(x), 1, 2, 3 ).join(); - thread( &X::g3, &x, 1, 2, 3 ).join(); - thread( &X::g3, x, 1, 2, 3 ).join(); - thread( &X::g3, ref(x), 1, 2, 3 ).join(); + boost::thread( &X::g3, &x, 1, 2, 3 ).join(); + boost::thread( &X::g3, x, 1, 2, 3 ).join(); + boost::thread( &X::g3, boost::ref(x), 1, 2, 3 ).join(); // 4 - thread( &X::f4, &x, 1, 2, 3, 4 ).join(); - thread( &X::f4, ref(x), 1, 2, 3, 4 ).join(); + boost::thread( &X::f4, &x, 1, 2, 3, 4 ).join(); + boost::thread( &X::f4, boost::ref(x), 1, 2, 3, 4 ).join(); - thread( &X::g4, &x, 1, 2, 3, 4 ).join(); - thread( &X::g4, x, 1, 2, 3, 4 ).join(); - thread( &X::g4, ref(x), 1, 2, 3, 4 ).join(); + boost::thread( &X::g4, &x, 1, 2, 3, 4 ).join(); + boost::thread( &X::g4, x, 1, 2, 3, 4 ).join(); + boost::thread( &X::g4, boost::ref(x), 1, 2, 3, 4 ).join(); // 5 - thread( &X::f5, &x, 1, 2, 3, 4, 5 ).join(); - thread( &X::f5, ref(x), 1, 2, 3, 4, 5 ).join(); + boost::thread( &X::f5, &x, 1, 2, 3, 4, 5 ).join(); + boost::thread( &X::f5, boost::ref(x), 1, 2, 3, 4, 5 ).join(); - thread( &X::g5, &x, 1, 2, 3, 4, 5 ).join(); - thread( &X::g5, x, 1, 2, 3, 4, 5 ).join(); - thread( &X::g5, ref(x), 1, 2, 3, 4, 5 ).join(); + boost::thread( &X::g5, &x, 1, 2, 3, 4, 5 ).join(); + boost::thread( &X::g5, x, 1, 2, 3, 4, 5 ).join(); + boost::thread( &X::g5, boost::ref(x), 1, 2, 3, 4, 5 ).join(); // 6 - thread( &X::f6, &x, 1, 2, 3, 4, 5, 6 ).join(); - thread( &X::f6, ref(x), 1, 2, 3, 4, 5, 6 ).join(); + boost::thread( &X::f6, &x, 1, 2, 3, 4, 5, 6 ).join(); + boost::thread( &X::f6, boost::ref(x), 1, 2, 3, 4, 5, 6 ).join(); - thread( &X::g6, &x, 1, 2, 3, 4, 5, 6 ).join(); - thread( &X::g6, x, 1, 2, 3, 4, 5, 6 ).join(); - thread( &X::g6, ref(x), 1, 2, 3, 4, 5, 6 ).join(); + boost::thread( &X::g6, &x, 1, 2, 3, 4, 5, 6 ).join(); + boost::thread( &X::g6, x, 1, 2, 3, 4, 5, 6 ).join(); + boost::thread( &X::g6, boost::ref(x), 1, 2, 3, 4, 5, 6 ).join(); // 7 - thread( &X::f7, &x, 1, 2, 3, 4, 5, 6, 7).join(); - thread( &X::f7, ref(x), 1, 2, 3, 4, 5, 6, 7).join(); + boost::thread( &X::f7, &x, 1, 2, 3, 4, 5, 6, 7).join(); + boost::thread( &X::f7, boost::ref(x), 1, 2, 3, 4, 5, 6, 7).join(); - thread( &X::g7, &x, 1, 2, 3, 4, 5, 6, 7).join(); - thread( &X::g7, x, 1, 2, 3, 4, 5, 6, 7).join(); - thread( &X::g7, ref(x), 1, 2, 3, 4, 5, 6, 7).join(); + boost::thread( &X::g7, &x, 1, 2, 3, 4, 5, 6, 7).join(); + boost::thread( &X::g7, x, 1, 2, 3, 4, 5, 6, 7).join(); + boost::thread( &X::g7, boost::ref(x), 1, 2, 3, 4, 5, 6, 7).join(); // 8 - thread( &X::f8, &x, 1, 2, 3, 4, 5, 6, 7, 8 ).join(); - thread( &X::f8, ref(x), 1, 2, 3, 4, 5, 6, 7, 8 ).join(); + boost::thread( &X::f8, &x, 1, 2, 3, 4, 5, 6, 7, 8 ).join(); + boost::thread( &X::f8, boost::ref(x), 1, 2, 3, 4, 5, 6, 7, 8 ).join(); - thread( &X::g8, &x, 1, 2, 3, 4, 5, 6, 7, 8 ).join(); - thread( &X::g8, x, 1, 2, 3, 4, 5, 6, 7, 8 ).join(); - thread( &X::g8, ref(x), 1, 2, 3, 4, 5, 6, 7, 8 ).join(); + boost::thread( &X::g8, &x, 1, 2, 3, 4, 5, 6, 7, 8 ).join(); + boost::thread( &X::g8, x, 1, 2, 3, 4, 5, 6, 7, 8 ).join(); + boost::thread( &X::g8, boost::ref(x), 1, 2, 3, 4, 5, 6, 7, 8 ).join(); BOOST_TEST( x.hash == 23558 ); - return report_errors(); + return boost::report_errors(); } From 58c8ce61c7174d6a0e6e5389f98a18216db243aa Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 4 Jul 2008 15:45:52 +0000 Subject: [PATCH 186/214] Fix for issue #2065 [SVN r47077] --- .../boost/thread/win32/thread_primitives.hpp | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/include/boost/thread/win32/thread_primitives.hpp b/include/boost/thread/win32/thread_primitives.hpp index 55062fa1..67a1bc3c 100644 --- a/include/boost/thread/win32/thread_primitives.hpp +++ b/include/boost/thread/win32/thread_primitives.hpp @@ -282,16 +282,6 @@ namespace boost } #if defined(BOOST_MSVC) && (_MSC_VER>=1400) && !defined(UNDER_CE) -#if _MSC_VER==1400 -extern "C" unsigned char _interlockedbittestandset(long *a,long b); -extern "C" unsigned char _interlockedbittestandreset(long *a,long b); -#else -extern "C" unsigned char _interlockedbittestandset(volatile long *a,long b); -extern "C" unsigned char _interlockedbittestandreset(volatile long *a,long b); -#endif - -#pragma intrinsic(_interlockedbittestandset) -#pragma intrinsic(_interlockedbittestandreset) namespace boost { @@ -299,6 +289,17 @@ namespace boost { namespace win32 { +#if _MSC_VER==1400 + extern "C" unsigned char _interlockedbittestandset(long *a,long b); + extern "C" unsigned char _interlockedbittestandreset(long *a,long b); +#else + extern "C" unsigned char _interlockedbittestandset(volatile long *a,long b); + extern "C" unsigned char _interlockedbittestandreset(volatile long *a,long b); +#endif + +#pragma intrinsic(_interlockedbittestandset) +#pragma intrinsic(_interlockedbittestandreset) + inline bool interlocked_bit_test_and_set(long* x,long bit) { return _interlockedbittestandset(x,bit)!=0; From eff0c84553cd7d4e0af7475318450d582fbecbca Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Sat, 5 Jul 2008 21:55:36 +0000 Subject: [PATCH 187/214] Test and fix for issue #2076 [SVN r47120] --- .../thread/pthread/condition_variable.hpp | 11 +++ .../thread/pthread/condition_variable_fwd.hpp | 10 +++ test/test_condition_timed_wait_times_out.cpp | 88 ++++++++++++++++++- 3 files changed, 107 insertions(+), 2 deletions(-) diff --git a/include/boost/thread/pthread/condition_variable.hpp b/include/boost/thread/pthread/condition_variable.hpp index 2d130a63..8ec6ae7f 100644 --- a/include/boost/thread/pthread/condition_variable.hpp +++ b/include/boost/thread/pthread/condition_variable.hpp @@ -121,6 +121,17 @@ namespace boost } return true; } + template + bool timed_wait(lock_type& m,xtime const& wait_until) + { + return timed_wait(m,system_time(wait_until)); + } + + template + bool timed_wait(lock_type& m,duration_type const& wait_duration) + { + return timed_wait(m,get_system_time()+wait_duration); + } template bool timed_wait(lock_type& m,boost::system_time const& wait_until,predicate_type pred) diff --git a/include/boost/thread/pthread/condition_variable_fwd.hpp b/include/boost/thread/pthread/condition_variable_fwd.hpp index c3941511..4048cdfb 100644 --- a/include/boost/thread/pthread/condition_variable_fwd.hpp +++ b/include/boost/thread/pthread/condition_variable_fwd.hpp @@ -47,6 +47,16 @@ namespace boost } bool timed_wait(unique_lock& m,boost::system_time const& wait_until); + bool timed_wait(unique_lock& m,xtime const& wait_until) + { + return timed_wait(m,system_time(wait_until)); + } + + template + bool timed_wait(unique_lock& m,duration_type const& wait_duration) + { + return timed_wait(m,get_system_time()+wait_duration); + } template bool timed_wait(unique_lock& m,boost::system_time const& wait_until,predicate_type pred) diff --git a/test/test_condition_timed_wait_times_out.cpp b/test/test_condition_timed_wait_times_out.cpp index 128922a4..9e17003f 100644 --- a/test/test_condition_timed_wait_times_out.cpp +++ b/test/test_condition_timed_wait_times_out.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2007 Anthony Williams +// Copyright (C) 2007-8 Anthony Williams // // 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) @@ -16,7 +16,7 @@ bool fake_predicate() return false; } -unsigned const timeout_seconds=5; +unsigned const timeout_seconds=2; unsigned const timeout_grace=1; boost::posix_time::milliseconds const timeout_resolution(100); @@ -70,12 +70,96 @@ void do_test_relative_timed_wait_with_predicate_times_out() BOOST_CHECK((delay-timeout_resolution)<=(end-start)); } +void do_test_timed_wait_relative_times_out() +{ + boost::condition_variable cond; + boost::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + boost::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + + while(cond.timed_wait(lock,delay)); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_cv_any_timed_wait_times_out() +{ + boost::condition_variable_any cond; + boost::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + boost::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+delay; + + while(cond.timed_wait(lock,timeout)); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_cv_any_timed_wait_with_predicate_times_out() +{ + boost::condition_variable_any cond; + boost::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + boost::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+delay; + + bool const res=cond.timed_wait(lock,timeout,fake_predicate); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK(!res); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_cv_any_relative_timed_wait_with_predicate_times_out() +{ + boost::condition_variable_any cond; + boost::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + boost::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + + bool const res=cond.timed_wait(lock,delay,fake_predicate); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK(!res); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_cv_any_timed_wait_relative_times_out() +{ + boost::condition_variable_any cond; + boost::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + boost::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + + while(cond.timed_wait(lock,delay)); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + void test_timed_wait_times_out() { timed_test(&do_test_timed_wait_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); timed_test(&do_test_timed_wait_with_predicate_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); timed_test(&do_test_relative_timed_wait_with_predicate_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_timed_wait_relative_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_cv_any_timed_wait_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_cv_any_timed_wait_with_predicate_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_cv_any_relative_timed_wait_with_predicate_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_cv_any_timed_wait_relative_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); } boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) From f1f7eac1f23f7065fa1b403f7624b1df729733ae Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Sun, 6 Jul 2008 21:58:11 +0000 Subject: [PATCH 188/214] Backwards compatibility with xtime --- test and fix for issue #2052 [SVN r47149] --- include/boost/thread/locks.hpp | 7 +++ include/boost/thread/pthread/mutex.hpp | 5 +++ .../boost/thread/win32/basic_timed_mutex.hpp | 6 +++ test/test_xtime.cpp | 44 +++++++++++++++++++ 4 files changed, 62 insertions(+) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 8cdd8c8d..33e8aa88 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -16,6 +16,8 @@ namespace boost { + struct xtime; + namespace detail { template @@ -265,6 +267,11 @@ namespace boost is_locked=m->timed_lock(absolute_time); return is_locked; } + bool timed_lock(::boost::xtime const& absolute_time) + { + is_locked=m->timed_lock(absolute_time); + return is_locked; + } void unlock() { if(!owns_lock()) diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index 128a9405..51d62ae5 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include "timespec.hpp" @@ -113,6 +114,10 @@ namespace boost { return timed_lock(get_system_time()+relative_time); } + bool timed_lock(boost::xtime const & absolute_time) + { + return timed_lock(system_time(absolute_time)); + } #ifdef BOOST_PTHREAD_HAS_TIMEDLOCK void lock() diff --git a/include/boost/thread/win32/basic_timed_mutex.hpp b/include/boost/thread/win32/basic_timed_mutex.hpp index a218233c..00206061 100644 --- a/include/boost/thread/win32/basic_timed_mutex.hpp +++ b/include/boost/thread/win32/basic_timed_mutex.hpp @@ -13,6 +13,7 @@ #include "thread_primitives.hpp" #include "interlocked_read.hpp" #include +#include #include #include @@ -117,6 +118,11 @@ namespace boost return timed_lock(get_system_time()+timeout); } + bool timed_lock(boost::xtime const& timeout) + { + return timed_lock(system_time(timeout)); + } + long get_active_count() { return ::boost::detail::interlocked_read_acquire(&active_count); diff --git a/test/test_xtime.cpp b/test/test_xtime.cpp index 099f3432..efdfd3c7 100644 --- a/test/test_xtime.cpp +++ b/test/test_xtime.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf +// Copyright (C) 2008 Anthony Williams // // 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) @@ -9,6 +10,8 @@ #include #include +#include +#include void test_xtime_cmp() { @@ -53,6 +56,45 @@ void test_xtime_get() } } +void test_xtime_mutex_backwards_compatibility() +{ + boost::timed_mutex m; + BOOST_CHECK(m.timed_lock(boost::get_xtime(boost::get_system_time()+boost::posix_time::milliseconds(10)))); + m.unlock(); + boost::timed_mutex::scoped_timed_lock lk(m,boost::get_xtime(boost::get_system_time()+boost::posix_time::milliseconds(10))); + BOOST_CHECK(lk.owns_lock()); + if(lk.owns_lock()) + { + lk.unlock(); + } + BOOST_CHECK(lk.timed_lock(boost::get_xtime(boost::get_system_time()+boost::posix_time::milliseconds(10)))); + if(lk.owns_lock()) + { + lk.unlock(); + } +} + +bool predicate() +{ + return false; +} + + +void test_xtime_condvar_backwards_compatibility() +{ + boost::condition_variable cond; + boost::condition_variable_any cond_any; + boost::mutex m; + + boost::mutex::scoped_lock lk(m); + cond.timed_wait(lk,boost::get_xtime(boost::get_system_time()+boost::posix_time::milliseconds(10))); + cond.timed_wait(lk,boost::get_xtime(boost::get_system_time()+boost::posix_time::milliseconds(10)),predicate); + cond_any.timed_wait(lk,boost::get_xtime(boost::get_system_time()+boost::posix_time::milliseconds(10))); + cond_any.timed_wait(lk,boost::get_xtime(boost::get_system_time()+boost::posix_time::milliseconds(10)),predicate); +} + + + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = @@ -60,6 +102,8 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) test->add(BOOST_TEST_CASE(&test_xtime_cmp)); test->add(BOOST_TEST_CASE(&test_xtime_get)); + test->add(BOOST_TEST_CASE(&test_xtime_mutex_backwards_compatibility)); + test->add(BOOST_TEST_CASE(&test_xtime_condvar_backwards_compatibility)); return test; } From e92aeac7d7ce8d53f467150e7e665305c0466cf1 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 7 Jul 2008 07:28:32 +0000 Subject: [PATCH 189/214] Added notify functions to class synopsis [SVN r47171] --- doc/condition_variables.qbk | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/condition_variables.qbk b/doc/condition_variables.qbk index 7ca68950..4f2940cc 100644 --- a/doc/condition_variables.qbk +++ b/doc/condition_variables.qbk @@ -82,6 +82,9 @@ optimizations in some cases, based on the knowledge of the mutex type; condition_variable(); ~condition_variable(); + void notify_one(); + void notify_all(); + void wait(boost::unique_lock& lock); template @@ -299,6 +302,9 @@ return true; condition_variable_any(); ~condition_variable_any(); + void notify_one(); + void notify_all(); + template void wait(lock_type& lock); From 63e675a6bb63fe964ea671dbabbddcc0f5c45a3f Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 7 Jul 2008 07:30:27 +0000 Subject: [PATCH 190/214] Corrected description to avoid reference to arguments [SVN r47172] --- doc/once.qbk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/once.qbk b/doc/once.qbk index e6ead35a..dddcdb1c 100644 --- a/doc/once.qbk +++ b/doc/once.qbk @@ -31,8 +31,8 @@ Objects of type `boost::once_flag` shall be initialized with `BOOST_ONCE_INIT`: be equivalent to calling the original. ]] [[Effects:] [Calls to `call_once` on the same `once_flag` object are serialized. If there has been no prior effective `call_once` on -the same `once_flag` object, the argument `func` (or a copy thereof) is called as-if by invoking `func(args)`, and the invocation of -`call_once` is effective if and only if `func(args)` returns without exception. If an exception is thrown, the exception is +the same `once_flag` object, the argument `func` (or a copy thereof) is called as-if by invoking `func()`, and the invocation of +`call_once` is effective if and only if `func()` returns without exception. If an exception is thrown, the exception is propagated to the caller. If there has been a prior effective `call_once` on the same `once_flag` object, the `call_once` returns without invoking `func`. ]] From 5fe4312c6cb91209cf8b18a77b610834f64315c1 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 7 Jul 2008 22:04:10 +0000 Subject: [PATCH 191/214] test and fix for issue #2081 [SVN r47197] --- include/boost/thread/locks.hpp | 63 ++++++++++++++- test/test_lock_concept.cpp | 144 ++++++++++++++++++++++++++++++++- 2 files changed, 205 insertions(+), 2 deletions(-) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 33e8aa88..b0ad4705 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -186,6 +186,11 @@ namespace boost swap(temp); return *this; } + void swap(unique_lock&& other) + { + std::swap(m,other.m); + std::swap(is_locked,other.is_locked); + } #else unique_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) @@ -218,7 +223,6 @@ namespace boost swap(temp); return *this; } -#endif void swap(unique_lock& other) { std::swap(m,other.m); @@ -229,6 +233,7 @@ namespace boost std::swap(m,other->m); std::swap(is_locked,other->is_locked); } +#endif ~unique_lock() { @@ -313,11 +318,19 @@ namespace boost friend class upgrade_lock; }; +#ifdef BOOST_HAS_RVALUE_REFS + template + void swap(unique_lock&& lhs,unique_lock&& rhs) + { + lhs.swap(rhs); + } +#else template void swap(unique_lock& lhs,unique_lock& rhs) { lhs.swap(rhs); } +#endif #ifdef BOOST_HAS_RVALUE_REFS template @@ -424,11 +437,29 @@ namespace boost return *this; } +#ifdef BOOST_HAS_RVALUE_REFS + void swap(shared_lock&& other) + { + std::swap(m,other.m); + std::swap(is_locked,other.is_locked); + } +#else void swap(shared_lock& other) { std::swap(m,other.m); std::swap(is_locked,other.is_locked); } + void swap(boost::detail::thread_move_t other) + { + std::swap(m,other->m); + std::swap(is_locked,other->is_locked); + } +#endif + + Mutex* mutex() const + { + return m; + } ~shared_lock() { @@ -490,6 +521,20 @@ namespace boost }; +#ifdef BOOST_HAS_RVALUE_REFS + template + void swap(shared_lock&& lhs,shared_lock&& rhs) + { + lhs.swap(rhs); + } +#else + template + void swap(shared_lock& lhs,shared_lock& rhs) + { + lhs.swap(rhs); + } +#endif + template class upgrade_lock { @@ -743,6 +788,12 @@ namespace boost return *this; } +#ifdef BOOST_HAS_RVALUE_REFS + void swap(try_lock_wrapper&& other) + { + base::swap(other); + } +#else void swap(try_lock_wrapper& other) { base::swap(other); @@ -751,6 +802,7 @@ namespace boost { base::swap(*other); } +#endif void lock() { @@ -788,11 +840,19 @@ namespace boost } }; +#ifdef BOOST_HAS_RVALUE_REFS + template + void swap(try_lock_wrapper&& lhs,try_lock_wrapper&& rhs) + { + lhs.swap(rhs); + } +#else template void swap(try_lock_wrapper& lhs,try_lock_wrapper& rhs) { lhs.swap(rhs); } +#endif template unsigned try_lock_internal(MutexType1& m1,MutexType2& m2) @@ -1189,5 +1249,6 @@ namespace boost } #include +#include #endif diff --git a/test/test_lock_concept.cpp b/test/test_lock_concept.cpp index 5530398f..8659c27b 100644 --- a/test/test_lock_concept.cpp +++ b/test/test_lock_concept.cpp @@ -83,6 +83,122 @@ struct test_initially_unlocked_if_other_thread_has_lock } }; +template +struct test_initially_unlocked_with_try_lock_if_other_thread_has_unique_lock +{ + Mutex m; + boost::mutex done_mutex; + bool done; + bool locked; + boost::condition_variable done_cond; + + test_initially_unlocked_with_try_lock_if_other_thread_has_unique_lock(): + done(false),locked(false) + {} + + void locking_thread() + { + Lock lock(m,boost::try_to_lock); + + boost::lock_guard lk(done_mutex); + locked=lock.owns_lock(); + done=true; + done_cond.notify_one(); + } + + bool is_done() const + { + return done; + } + + + void operator()() + { + boost::unique_lock lock(m); + + typedef test_initially_unlocked_with_try_lock_if_other_thread_has_unique_lock this_type; + + boost::thread t(&this_type::locking_thread,this); + + try + { + { + boost::mutex::scoped_lock lk(done_mutex); + BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), + boost::bind(&this_type::is_done,this))); + BOOST_CHECK(!locked); + } + + lock.unlock(); + t.join(); + } + catch(...) + { + lock.unlock(); + t.join(); + throw; + } + } +}; + +template +struct test_initially_locked_if_other_thread_has_shared_lock +{ + Mutex m; + boost::mutex done_mutex; + bool done; + bool locked; + boost::condition_variable done_cond; + + test_initially_locked_if_other_thread_has_shared_lock(): + done(false),locked(false) + {} + + void locking_thread() + { + Lock lock(m); + + boost::lock_guard lk(done_mutex); + locked=lock.owns_lock(); + done=true; + done_cond.notify_one(); + } + + bool is_done() const + { + return done; + } + + + void operator()() + { + boost::shared_lock lock(m); + + typedef test_initially_locked_if_other_thread_has_shared_lock this_type; + + boost::thread t(&this_type::locking_thread,this); + + try + { + { + boost::mutex::scoped_lock lk(done_mutex); + BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), + boost::bind(&this_type::is_done,this))); + BOOST_CHECK(locked); + } + + lock.unlock(); + t.join(); + } + catch(...) + { + lock.unlock(); + t.join(); + throw; + } + } +}; + template struct test_initially_unlocked_with_defer_lock_parameter { @@ -263,6 +379,8 @@ struct test_locks_can_be_swapped { Mutex m1; Mutex m2; + Mutex m3; + Lock l1(m1); Lock l2(m2); @@ -278,7 +396,10 @@ struct test_locks_can_be_swapped BOOST_CHECK_EQUAL(l1.mutex(),&m1); BOOST_CHECK_EQUAL(l2.mutex(),&m2); - + + l1.swap(Lock(m3)); + + BOOST_CHECK_EQUAL(l1.mutex(),&m3); } }; @@ -333,6 +454,26 @@ BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_try_lock_concept,Mutex) test_locks_can_be_swapped()(); } +void test_shared_lock() +{ + typedef boost::shared_mutex Mutex; + typedef boost::shared_lock Lock; + + test_default_constructed_has_no_mutex_and_unlocked()(); + test_initially_locked()(); + test_initially_unlocked_with_try_lock_if_other_thread_has_unique_lock()(); + test_initially_locked_if_other_thread_has_shared_lock()(); + test_initially_unlocked_with_defer_lock_parameter()(); + test_initially_locked_with_adopt_lock_parameter()(); + test_unlocked_after_unlock_called()(); + test_locked_after_lock_called()(); + test_locked_after_try_lock_called()(); + test_throws_if_lock_called_when_already_locked()(); + test_throws_if_try_lock_called_when_already_locked()(); + test_throws_if_unlock_called_when_already_unlocked()(); + test_locks_can_be_swapped()(); +} + boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = @@ -352,6 +493,7 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) boost::recursive_mutex,boost::recursive_try_mutex,boost::recursive_timed_mutex,boost::shared_mutex> all_mutex_types; test->add(BOOST_TEST_CASE_TEMPLATE(test_unique_lock_is_scoped_lock,all_mutex_types)); + test->add(BOOST_TEST_CASE(&test_shared_lock)); return test; } From 4dfc636c84d1a86eb3e0ba02e4f9e397ba72c14a Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 7 Jul 2008 22:19:28 +0000 Subject: [PATCH 192/214] test and fix for issue #2080 [SVN r47199] --- include/boost/thread/locks.hpp | 10 +++++ test/test_lock_concept.cpp | 72 ++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index b0ad4705..66a07371 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -495,6 +495,16 @@ namespace boost is_locked=m->timed_lock_shared(target_time); return is_locked; } + template + bool timed_lock(Duration const& target_time) + { + if(owns_lock()) + { + throw boost::lock_error(); + } + is_locked=m->timed_lock_shared(target_time); + return is_locked; + } void unlock() { if(!owns_lock()) diff --git a/test/test_lock_concept.cpp b/test/test_lock_concept.cpp index 8659c27b..06367d5b 100644 --- a/test/test_lock_concept.cpp +++ b/test/test_lock_concept.cpp @@ -454,6 +454,68 @@ BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_try_lock_concept,Mutex) test_locks_can_be_swapped()(); } +struct dummy_shared_mutex +{ + bool locked; + bool shared_locked; + bool shared_unlocked; + bool shared_timed_locked_relative; + bool shared_timed_locked_absolute; + bool timed_locked_relative; + bool timed_locked_absolute; + + dummy_shared_mutex(): + locked(false),shared_locked(false),shared_unlocked(false), + shared_timed_locked_relative(false), + shared_timed_locked_absolute(false), + timed_locked_relative(false), + timed_locked_absolute(false) + {} + + void lock() + { + locked=true; + } + + void lock_shared() + { + shared_locked=true; + } + + void unlock() + {} + + void unlock_shared() + { + shared_unlocked=true; + } + + bool timed_lock_shared(boost::system_time) + { + shared_timed_locked_absolute=true; + return false; + } + template + bool timed_lock_shared(Duration) + { + shared_timed_locked_relative=true; + return false; + } + bool timed_lock(boost::system_time) + { + timed_locked_absolute=true; + return false; + } + template + bool timed_lock(Duration) + { + timed_locked_relative=true; + return false; + } + +}; + + void test_shared_lock() { typedef boost::shared_mutex Mutex; @@ -472,6 +534,16 @@ void test_shared_lock() test_throws_if_try_lock_called_when_already_locked()(); test_throws_if_unlock_called_when_already_unlocked()(); test_locks_can_be_swapped()(); + + dummy_shared_mutex dummy; + boost::shared_lock lk(dummy); + BOOST_CHECK(dummy.shared_locked); + lk.unlock(); + BOOST_CHECK(dummy.shared_unlocked); + lk.timed_lock(boost::posix_time::milliseconds(5)); + BOOST_CHECK(dummy.shared_timed_locked_relative); + lk.timed_lock(boost::get_system_time()); + BOOST_CHECK(dummy.shared_timed_locked_absolute); } boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) From 3ea9ce1c8cdeb69f03ba356481bc5e3176985f1f Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 16 Jul 2008 13:19:43 +0000 Subject: [PATCH 193/214] Fixes to make basic thread functionality work with Borland compilers again [SVN r47471] --- include/boost/thread/detail/move.hpp | 4 ++++ include/boost/thread/detail/thread.hpp | 11 ++++++++++- include/boost/thread/locks.hpp | 6 +++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/include/boost/thread/detail/move.hpp b/include/boost/thread/detail/move.hpp index f8917758..719cd32b 100644 --- a/include/boost/thread/detail/move.hpp +++ b/include/boost/thread/detail/move.hpp @@ -6,8 +6,10 @@ #ifndef BOOST_THREAD_MOVE_HPP #define BOOST_THREAD_MOVE_HPP +#ifndef __BORLANDC__ #include #include +#endif #include @@ -37,11 +39,13 @@ namespace boost }; } +#ifndef __BORLANDC__ template typename enable_if >, detail::thread_move_t >::type move(T& t) { return t; } +#endif template detail::thread_move_t move(detail::thread_move_t t) diff --git a/include/boost/thread/detail/thread.hpp b/include/boost/thread/detail/thread.hpp index a7e82f7f..b88f0a69 100644 --- a/include/boost/thread/detail/thread.hpp +++ b/include/boost/thread/detail/thread.hpp @@ -172,6 +172,14 @@ namespace boost return static_cast(*this); } +#else +#ifdef __BORLANDC__ + template + explicit thread(F f): + thread_info(make_thread_info(f)) + { + start_thread(); + } #else template explicit thread(F f,typename disable_if >, dummy* >::type=0): @@ -179,9 +187,10 @@ namespace boost { start_thread(); } +#endif template - thread(detail::thread_move_t f): + explicit thread(detail::thread_move_t f): thread_info(make_thread_info(f)) { start_thread(); diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 66a07371..6821d209 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -449,7 +449,7 @@ namespace boost std::swap(m,other.m); std::swap(is_locked,other.is_locked); } - void swap(boost::detail::thread_move_t other) + void swap(boost::detail::thread_move_t > other) { std::swap(m,other->m); std::swap(is_locked,other->is_locked); @@ -515,7 +515,7 @@ namespace boost is_locked=false; } - typedef void (shared_lock::*bool_type)(); + typedef void (shared_lock::*bool_type)(); operator bool_type() const { return is_locked?&shared_lock::lock:0; @@ -846,7 +846,7 @@ namespace boost typedef typename base::bool_type bool_type; operator bool_type() const { - return static_cast(*this); + return base::operator bool_type(); } }; From 27426b18d17182d893e17a38a027f4db60d0f8de Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 16 Jul 2008 14:41:09 +0000 Subject: [PATCH 194/214] Split lock and try_lock into mutex and range overloads without using enable_if, so it works on Borland compilers [SVN r47472] --- include/boost/thread/locks.hpp | 321 ++++++++++++++++++++++++--------- test/test_generic_locks.cpp | 10 + 2 files changed, 247 insertions(+), 84 deletions(-) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 6821d209..c3874d7c 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -10,14 +10,14 @@ #include #include #include -#include #include namespace boost { struct xtime; - + +#ifndef __BORLANDC__ namespace detail { template @@ -79,7 +79,13 @@ namespace boost detail::has_member_try_lock::value); }; - +#else + template + struct is_mutex_type + { + BOOST_STATIC_CONSTANT(bool, value = false); + }; +#endif struct defer_lock_t {}; @@ -98,6 +104,74 @@ namespace boost template class upgrade_lock; + template + class unique_lock; + + namespace detail + { + template + class try_lock_wrapper; + } + +#ifdef __BORLANDC__ + template + struct is_mutex_type > + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; + + template + struct is_mutex_type > + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; + + template + struct is_mutex_type > + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; + + template + struct is_mutex_type > + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; + + class mutex; + class timed_mutex; + class recursive_mutex; + class recursive_timed_mutex; + class shared_mutex; + + template<> + struct is_mutex_type + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; + template<> + struct is_mutex_type + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; + template<> + struct is_mutex_type + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; + template<> + struct is_mutex_type + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; + template<> + struct is_mutex_type + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; + +#endif + template class lock_guard { @@ -987,28 +1061,63 @@ namespace boost } } - template - typename enable_if, void>::type lock(MutexType1& m1,MutexType2& m2) + namespace detail { - unsigned const lock_count=2; - unsigned lock_first=0; - while(true) + template + struct is_mutex_type_wrapper + {}; + + template + void lock_impl(MutexType1& m1,MutexType2& m2,is_mutex_type_wrapper) { - switch(lock_first) + unsigned const lock_count=2; + unsigned lock_first=0; + while(true) { - case 0: - lock_first=detail::lock_helper(m1,m2); - if(!lock_first) - return; - break; - case 1: - lock_first=detail::lock_helper(m2,m1); - if(!lock_first) - return; - lock_first=(lock_first+1)%lock_count; - break; + switch(lock_first) + { + case 0: + lock_first=detail::lock_helper(m1,m2); + if(!lock_first) + return; + break; + case 1: + lock_first=detail::lock_helper(m2,m1); + if(!lock_first) + return; + lock_first=(lock_first+1)%lock_count; + break; + } } } + + template + void lock_impl(Iterator begin,Iterator end,is_mutex_type_wrapper); + } + + + template + void lock(MutexType1& m1,MutexType2& m2) + { + detail::lock_impl(m1,m2,detail::is_mutex_type_wrapper::value>()); + } + + template + void lock(const MutexType1& m1,MutexType2& m2) + { + detail::lock_impl(m1,m2,detail::is_mutex_type_wrapper::value>()); + } + + template + void lock(MutexType1& m1,const MutexType2& m2) + { + detail::lock_impl(m1,m2,detail::is_mutex_type_wrapper::value>()); + } + + template + void lock(const MutexType1& m1,const MutexType2& m2) + { + detail::lock_impl(m1,m2,detail::is_mutex_type_wrapper::value>()); } template @@ -1123,10 +1232,52 @@ namespace boost } } - template - typename enable_if, int>::type try_lock(MutexType1& m1,MutexType2& m2) + namespace detail { - return ((int)detail::try_lock_internal(m1,m2))-1; + template::value> + struct try_lock_impl_return + { + typedef int type; + }; + + template + struct try_lock_impl_return + { + typedef Iterator type; + }; + + template + int try_lock_impl(MutexType1& m1,MutexType2& m2,is_mutex_type_wrapper) + { + return ((int)detail::try_lock_internal(m1,m2))-1; + } + + template + Iterator try_lock_impl(Iterator begin,Iterator end,is_mutex_type_wrapper); + } + + template + typename detail::try_lock_impl_return::type try_lock(MutexType1& m1,MutexType2& m2) + { + return detail::try_lock_impl(m1,m2,detail::is_mutex_type_wrapper::value>()); + } + + template + typename detail::try_lock_impl_return::type try_lock(const MutexType1& m1,MutexType2& m2) + { + return detail::try_lock_impl(m1,m2,detail::is_mutex_type_wrapper::value>()); + } + + template + typename detail::try_lock_impl_return::type try_lock(MutexType1& m1,const MutexType2& m2) + { + return detail::try_lock_impl(m1,m2,detail::is_mutex_type_wrapper::value>()); + } + + template + typename detail::try_lock_impl_return::type try_lock(const MutexType1& m1,const MutexType2& m2) + { + return detail::try_lock_impl(m1,m2,detail::is_mutex_type_wrapper::value>()); } template @@ -1148,9 +1299,6 @@ namespace boost } - template - typename disable_if, void>::type lock(Iterator begin,Iterator end); - namespace detail { template @@ -1178,70 +1326,59 @@ namespace boost } } }; - } - template - typename disable_if, Iterator>::type try_lock(Iterator begin,Iterator end) - { - if(begin==end) - { - return end; - } - typedef typename std::iterator_traits::value_type lock_type; - unique_lock guard(*begin,try_to_lock); - - if(!guard.owns_lock()) - { - return begin; - } - Iterator const failed=try_lock(++begin,end); - if(failed==end) - { - guard.release(); - } - - return failed; - } + template + Iterator try_lock_impl(Iterator begin,Iterator end,is_mutex_type_wrapper) - template - typename disable_if, void>::type lock(Iterator begin,Iterator end) - { - typedef typename std::iterator_traits::value_type lock_type; - - if(begin==end) { - return; - } - bool start_with_begin=true; - Iterator second=begin; - ++second; - Iterator next=second; - - for(;;) - { - unique_lock begin_lock(*begin,defer_lock); - if(start_with_begin) + if(begin==end) { - begin_lock.lock(); - Iterator const failed_lock=try_lock(next,end); - if(failed_lock==end) - { - begin_lock.release(); - return; - } - start_with_begin=false; - next=failed_lock; + return end; } - else + typedef typename std::iterator_traits::value_type lock_type; + unique_lock guard(*begin,try_to_lock); + + if(!guard.owns_lock()) { - detail::range_lock_guard guard(next,end); - if(begin_lock.try_lock()) + return begin; + } + Iterator const failed=try_lock(++begin,end); + if(failed==end) + { + guard.release(); + } + + return failed; + } + } + + + namespace detail + { + template + void lock_impl(Iterator begin,Iterator end,is_mutex_type_wrapper) + { + typedef typename std::iterator_traits::value_type lock_type; + + if(begin==end) + { + return; + } + bool start_with_begin=true; + Iterator second=begin; + ++second; + Iterator next=second; + + for(;;) + { + unique_lock begin_lock(*begin,defer_lock); + if(start_with_begin) { - Iterator const failed_lock=try_lock(second,next); - if(failed_lock==next) + begin_lock.lock(); + Iterator const failed_lock=try_lock(next,end); + if(failed_lock==end) { begin_lock.release(); - guard.release(); return; } start_with_begin=false; @@ -1249,16 +1386,32 @@ namespace boost } else { - start_with_begin=true; - next=second; + detail::range_lock_guard guard(next,end); + if(begin_lock.try_lock()) + { + Iterator const failed_lock=try_lock(second,next); + if(failed_lock==next) + { + begin_lock.release(); + guard.release(); + return; + } + start_with_begin=false; + next=failed_lock; + } + else + { + start_with_begin=true; + next=second; + } } } } + } } #include -#include #endif diff --git a/test/test_generic_locks.cpp b/test/test_generic_locks.cpp index a091976e..d3880d5c 100644 --- a/test/test_generic_locks.cpp +++ b/test/test_generic_locks.cpp @@ -272,6 +272,16 @@ struct dummy_mutex } }; +namespace boost +{ + template<> + struct is_mutex_type + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; +} + + void test_lock_five_in_range() { From 36c44b6f45d0685aeab85fbeff80b89e5e403daf Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 18 Jul 2008 13:42:10 +0000 Subject: [PATCH 195/214] Borland-specific fixes should apply to all compilers for which enable_if is broken: check for BOOST_NO_SFINAE instead [SVN r47554] --- include/boost/thread/detail/move.hpp | 4 ++-- include/boost/thread/detail/thread.hpp | 2 +- include/boost/thread/locks.hpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/boost/thread/detail/move.hpp b/include/boost/thread/detail/move.hpp index 719cd32b..91b2eda1 100644 --- a/include/boost/thread/detail/move.hpp +++ b/include/boost/thread/detail/move.hpp @@ -6,7 +6,7 @@ #ifndef BOOST_THREAD_MOVE_HPP #define BOOST_THREAD_MOVE_HPP -#ifndef __BORLANDC__ +#ifndef BOOST_NO_SFINAE #include #include #endif @@ -39,7 +39,7 @@ namespace boost }; } -#ifndef __BORLANDC__ +#ifndef BOOST_NO_SFINAE template typename enable_if >, detail::thread_move_t >::type move(T& t) { diff --git a/include/boost/thread/detail/thread.hpp b/include/boost/thread/detail/thread.hpp index b88f0a69..3d39d1df 100644 --- a/include/boost/thread/detail/thread.hpp +++ b/include/boost/thread/detail/thread.hpp @@ -173,7 +173,7 @@ namespace boost } #else -#ifdef __BORLANDC__ +#ifdef BOOST_NO_SFINAE template explicit thread(F f): thread_info(make_thread_info(f)) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index c3874d7c..ec555e25 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -17,7 +17,7 @@ namespace boost { struct xtime; -#ifndef __BORLANDC__ +#ifndef BOOST_NO_SFINAE namespace detail { template @@ -113,7 +113,7 @@ namespace boost class try_lock_wrapper; } -#ifdef __BORLANDC__ +#ifdef BOOST_NO_SFINAE template struct is_mutex_type > { From 31a98f0a1e8b16ac0ce48bbacca37de40e50d69f Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 21 Jul 2008 10:04:26 +0000 Subject: [PATCH 196/214] BOOST_NO_SFINAE isn't enough to identify compilers that can't auto-detect mutexes, so create a new macro for that, and add IBM and Sun compilers to list [SVN r47652] --- include/boost/thread/locks.hpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index ec555e25..6e4341ad 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -17,7 +18,13 @@ namespace boost { struct xtime; -#ifndef BOOST_NO_SFINAE +#if defined(BOOST_NO_SFINAE) || \ + BOOST_WORKAROUND(__IBMCPP__, BOOST_TESTED_AT(600)) || \ + BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590)) +#define BOOST_THREAD_NO_AUTO_DETECT_MUTEX_TYPES +#endif + +#ifndef BOOST_THREAD_NO_AUTO_DETECT_MUTEX_TYPES namespace detail { template @@ -113,7 +120,7 @@ namespace boost class try_lock_wrapper; } -#ifdef BOOST_NO_SFINAE +#ifdef BOOST_THREAD_NO_AUTO_DETECT_MUTEX_TYPES template struct is_mutex_type > { From 25460c652c7a80f4da3b23d4a7e8cea9d29a260c Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 21 Jul 2008 10:25:08 +0000 Subject: [PATCH 197/214] Use sysconf to detect number of processors on AIX too [SVN r47653] --- src/pthread/thread.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 2ceb68af..e8e8736c 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -18,7 +18,7 @@ #elif defined(__APPLE__) || defined(__FreeBSD__) #include #include -#elif defined(__sun) || defined(__CYGWIN__) +#elif defined(__sun) || defined(__CYGWIN__) || defined(_AIX) #include #endif @@ -394,7 +394,7 @@ namespace boost int count; size_t size=sizeof(count); return sysctlbyname("hw.ncpu",&count,&size,NULL,0)?0:count; -#elif defined(__sun) || defined(__CYGWIN__) +#elif defined(__sun) || defined(__CYGWIN__) || defined(_AIX) int const count=sysconf(_SC_NPROCESSORS_ONLN); return (count>0)?count:0; #else From 442dc58e0fc0e4f73904c2d4ca8b33e9416a4e64 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 21 Jul 2008 10:39:50 +0000 Subject: [PATCH 198/214] Use sysconf(_SC_NPROCESSORS_ONLN) where it is available, as a fallback [SVN r47654] --- src/pthread/thread.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index e8e8736c..9e1a5146 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -18,7 +18,7 @@ #elif defined(__APPLE__) || defined(__FreeBSD__) #include #include -#elif defined(__sun) || defined(__CYGWIN__) || defined(_AIX) +#elif defined BOOST_HAS_UNISTD_H #include #endif @@ -394,7 +394,7 @@ namespace boost int count; size_t size=sizeof(count); return sysctlbyname("hw.ncpu",&count,&size,NULL,0)?0:count; -#elif defined(__sun) || defined(__CYGWIN__) || defined(_AIX) +#elif defined(BOOST_HAS_UNISTD_H) && defined(_SC_NPROCESSORS_ONLN) int const count=sysconf(_SC_NPROCESSORS_ONLN); return (count>0)?count:0; #else From 48c857e02cb0ea3a77e2f9237f7daa836d48760d Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 25 Jul 2008 21:12:29 +0000 Subject: [PATCH 199/214] Fix for issue #2105: specify which header to include for each class or function [SVN r47810] --- doc/condition_variables.qbk | 6 ++++++ doc/mutex_concepts.qbk | 10 ++++++++++ doc/mutexes.qbk | 12 ++++++++++++ doc/once.qbk | 4 ++++ doc/overview.qbk | 8 ++++++++ doc/shared_mutex_ref.qbk | 2 ++ doc/thread_ref.qbk | 26 ++++++++++++++++++++++++++ doc/time.qbk | 4 ++++ doc/tss.qbk | 2 ++ 9 files changed, 74 insertions(+) diff --git a/doc/condition_variables.qbk b/doc/condition_variables.qbk index 4f2940cc..e54dd34c 100644 --- a/doc/condition_variables.qbk +++ b/doc/condition_variables.qbk @@ -74,6 +74,8 @@ optimizations in some cases, based on the knowledge of the mutex type; [section:condition_variable Class `condition_variable`] + #include + namespace boost { class condition_variable @@ -294,6 +296,8 @@ return true; [section:condition_variable_any Class `condition_variable_any`] + #include + namespace boost { class condition_variable_any @@ -498,6 +502,8 @@ return true; [section:condition Typedef `condition`] + #include + typedef condition_variable_any condition; The typedef `condition` is provided for backwards compatibility with previous boost releases. diff --git a/doc/mutex_concepts.qbk b/doc/mutex_concepts.qbk index 0440dc0f..81174218 100644 --- a/doc/mutex_concepts.qbk +++ b/doc/mutex_concepts.qbk @@ -312,6 +312,8 @@ without blocking.]] [section:lock_guard Class template `lock_guard`] + #include + template class lock_guard { @@ -376,6 +378,8 @@ object passed to the constructor.]] [section:unique_lock Class template `unique_lock`] + #include + template class unique_lock { @@ -617,6 +621,8 @@ __owns_lock_ref__ returns `false`.]] [section:shared_lock Class template `shared_lock`] + #include + template class shared_lock { @@ -850,6 +856,8 @@ __owns_lock_shared_ref__ returns `false`.]] [section:upgrade_lock Class template `upgrade_lock`] + #include + template class upgrade_lock { @@ -897,6 +905,8 @@ state (including the destructor) must be called by the same thread that acquired [section:upgrade_to_unique_lock Class template `upgrade_to_unique_lock`] + #include + template class upgrade_to_unique_lock { diff --git a/doc/mutexes.qbk b/doc/mutexes.qbk index 3a6e1efb..695b3834 100644 --- a/doc/mutexes.qbk +++ b/doc/mutexes.qbk @@ -9,6 +9,8 @@ [section:mutex Class `mutex`] + #include + class mutex: boost::noncopyable { @@ -51,6 +53,8 @@ implementation. If no such instance exists, `native_handle()` and `native_handle [section:try_mutex Typedef `try_mutex`] + #include + typedef mutex try_mutex; __try_mutex__ is a `typedef` to __mutex__, provided for backwards compatibility with previous releases of boost. @@ -59,6 +63,8 @@ __try_mutex__ is a `typedef` to __mutex__, provided for backwards compatibility [section:timed_mutex Class `timed_mutex`] + #include + class timed_mutex: boost::noncopyable { @@ -106,6 +112,8 @@ implementation. If no such instance exists, `native_handle()` and `native_handle [section:recursive_mutex Class `recursive_mutex`] + #include + class recursive_mutex: boost::noncopyable { @@ -150,6 +158,8 @@ implementation. If no such instance exists, `native_handle()` and `native_handle [section:recursive_try_mutex Typedef `recursive_try_mutex`] + #include + typedef recursive_mutex recursive_try_mutex; __recursive_try_mutex__ is a `typedef` to __recursive_mutex__, provided for backwards compatibility with previous releases of boost. @@ -158,6 +168,8 @@ __recursive_try_mutex__ is a `typedef` to __recursive_mutex__, provided for back [section:recursive_timed_mutex Class `recursive_timed_mutex`] + #include + class recursive_timed_mutex: boost::noncopyable { diff --git a/doc/once.qbk b/doc/once.qbk index dddcdb1c..fd53e7de 100644 --- a/doc/once.qbk +++ b/doc/once.qbk @@ -11,6 +11,8 @@ [section:once_flag Typedef `once_flag`] + #include + typedef platform-specific-type once_flag; #define BOOST_ONCE_INIT platform-specific-initializer @@ -22,6 +24,8 @@ Objects of type `boost::once_flag` shall be initialized with `BOOST_ONCE_INIT`: [section:call_once Non-member function `call_once`] + #include + template void call_once(once_flag& flag,Callable func); diff --git a/doc/overview.qbk b/doc/overview.qbk index f9b867b7..c647a6d2 100644 --- a/doc/overview.qbk +++ b/doc/overview.qbk @@ -19,4 +19,12 @@ closely follow the proposals presented to the C++ Standards Committee, in partic [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2139.html N2139], and [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2094.html N2094] +In order to use the classes and functions described here, you can +either include the specific headers specified by the descriptions of +each class or function, or include the master thread library header: + + #include + +which includes all the other headers in turn. + [endsect] diff --git a/doc/shared_mutex_ref.qbk b/doc/shared_mutex_ref.qbk index e7bf657e..49cd1673 100644 --- a/doc/shared_mutex_ref.qbk +++ b/doc/shared_mutex_ref.qbk @@ -7,6 +7,8 @@ [section:shared_mutex Class `shared_mutex`] + #include + class shared_mutex { public: diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk index e94bbd2d..c74c984f 100644 --- a/doc/thread_ref.qbk +++ b/doc/thread_ref.qbk @@ -165,6 +165,8 @@ __thread_id__ yield a total order for every non-equal thread ID. [section:thread Class `thread`] + #include + class thread { public: @@ -480,6 +482,8 @@ value as `this->get_id()` prior to the call.]] [section:non_member_swap Non-member function `swap()`] + #include + void swap(thread& lhs,thread& rhs); [variablelist @@ -493,6 +497,8 @@ value as `this->get_id()` prior to the call.]] [section:id Class `boost::thread::id`] + #include + class thread::id { public: @@ -641,6 +647,8 @@ instances of __thread_id__ `a` and `b` is the same if `a==b`, and different if ` [section:get_id Non-member function `get_id()`] + #include + namespace this_thread { thread::id get_id(); @@ -658,6 +666,8 @@ instances of __thread_id__ `a` and `b` is the same if `a==b`, and different if ` [section:interruption_point Non-member function `interruption_point()`] + #include + namespace this_thread { void interruption_point(); @@ -675,6 +685,8 @@ instances of __thread_id__ `a` and `b` is the same if `a==b`, and different if ` [section:interruption_requested Non-member function `interruption_requested()`] + #include + namespace this_thread { bool interruption_requested(); @@ -692,6 +704,8 @@ instances of __thread_id__ `a` and `b` is the same if `a==b`, and different if ` [section:interruption_enabled Non-member function `interruption_enabled()`] + #include + namespace this_thread { bool interruption_enabled(); @@ -709,6 +723,8 @@ instances of __thread_id__ `a` and `b` is the same if `a==b`, and different if ` [section:sleep Non-member function `sleep()`] + #include + namespace this_thread { template @@ -729,6 +745,8 @@ instances of __thread_id__ `a` and `b` is the same if `a==b`, and different if ` [section:yield Non-member function `yield()`] + #include + namespace this_thread { void yield(); @@ -746,6 +764,8 @@ instances of __thread_id__ `a` and `b` is the same if `a==b`, and different if ` [section:disable_interruption Class `disable_interruption`] + #include + namespace this_thread { class disable_interruption @@ -797,6 +817,8 @@ interruption state on destruction. Instances of `disable_interruption` cannot be [section:restore_interruption Class `restore_interruption`] + #include + namespace this_thread { class restore_interruption @@ -851,6 +873,8 @@ is destroyed, interruption is again disabled. Instances of `restore_interruption [section:atthreadexit Non-member function template `at_thread_exit()`] + #include + template void at_thread_exit(Callable func); @@ -871,6 +895,8 @@ error occurs within the thread library. Any exception thrown whilst copying `fun [section:threadgroup Class `thread_group`] + #include + class thread_group: private noncopyable { diff --git a/doc/time.qbk b/doc/time.qbk index 321a9c6a..87985427 100644 --- a/doc/time.qbk +++ b/doc/time.qbk @@ -47,6 +47,8 @@ date_time.posix_time.time_duration Boost.Date_Time Time Duration requirements] c [section:system_time Typedef `system_time`] + #include + typedef boost::posix_time::ptime system_time; See the documentation for [link date_time.posix_time.ptime_class `boost::posix_time::ptime`] in the Boost.Date_Time library. @@ -55,6 +57,8 @@ See the documentation for [link date_time.posix_time.ptime_class `boost::posix_t [section:get_system_time Non-member function `get_system_time()`] + #include + system_time get_system_time(); [variablelist diff --git a/doc/tss.qbk b/doc/tss.qbk index d3a61bd7..6a9ffc44 100644 --- a/doc/tss.qbk +++ b/doc/tss.qbk @@ -44,6 +44,8 @@ cleaned up, that value is added to the cleanup list. Cleanup finishes when there [section:thread_specific_ptr Class `thread_specific_ptr`] + #include + template class thread_specific_ptr { From f7cb8d81411a3011de304faaa1a1001112f35c68 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 25 Jul 2008 21:30:37 +0000 Subject: [PATCH 200/214] Added a description for the scoped_try_lock typedefs [SVN r47814] --- doc/mutex_concepts.qbk | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/doc/mutex_concepts.qbk b/doc/mutex_concepts.qbk index 81174218..f4312cb8 100644 --- a/doc/mutex_concepts.qbk +++ b/doc/mutex_concepts.qbk @@ -929,6 +929,48 @@ reference to an instance of __upgrade_lock__, if that instance has upgrade owner ownership is upgraded to exclusive ownership. When the __upgrade_to_unique_lock__ instance is destroyed, the ownership of the __lockable_concept_type__ is downgraded back to ['upgrade ownership]. +[endsect] + +[section:scoped_try_lock Mutex-specific class `scoped_try_lock`] + + class MutexType::scoped_try_lock + { + private: + MutexType::scoped_try_lock(MutexType::scoped_try_lock& other); + MutexType::scoped_try_lock& operator=(MutexType::scoped_try_lock& other); + public: + MutexType::scoped_try_lock(); + explicit MutexType::scoped_try_lock(MutexType& m); + MutexType::scoped_try_lock(MutexType& m_,adopt_lock_t); + MutexType::scoped_try_lock(MutexType& m_,defer_lock_t); + MutexType::scoped_try_lock(MutexType& m_,try_to_lock_t); + + MutexType::scoped_try_lock(MutexType::scoped_try_lock&& other); + MutexType::scoped_try_lock& operator=(MutexType::scoped_try_lock&& other); + + void swap(MutexType::scoped_try_lock&& other); + + void lock(); + bool try_lock(); + void unlock(); + bool owns_lock() const; + + MutexType* mutex() const; + MutexType* release(); + bool operator!() const; + + typedef ``['unspecified-bool-type]`` bool_type; + operator bool_type() const; + }; + +The member typedef `scoped_try_lock` is provided for each distinct +`MutexType` as a typedef to a class with the preceding definition. The +semantics of each constructor and member function are identical to +those of [unique_lock_link `boost::unique_lock`] for the same `MutexType`, except +that the constructor that takes a single reference to a mutex will +call [try_lock_ref_link `m.try_lock()`] rather than `m.lock()`. + + [endsect] [endsect] From 63b44d4e327d578cc2b8831d3ea4143e647ad230 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 25 Jul 2008 21:57:33 +0000 Subject: [PATCH 201/214] Added documentation for the lock and try_lock free functions [SVN r47815] --- doc/mutex_concepts.qbk | 143 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/doc/mutex_concepts.qbk b/doc/mutex_concepts.qbk index f4312cb8..1affa78c 100644 --- a/doc/mutex_concepts.qbk +++ b/doc/mutex_concepts.qbk @@ -973,4 +973,147 @@ call [try_lock_ref_link `m.try_lock()`] rather than `m.lock()`. [endsect] +[endsect] + +[section:lock_functions Lock functions] + +[section:lock_multiple Non-member function `lock(Lockable1,Lockable2,...)`] + + template + void lock(Lockable1& l1,Lockable2& l2); + + template + void lock(Lockable1& l1,Lockable2& l2,Lockable3& l3); + + template + void lock(Lockable1& l1,Lockable2& l2,Lockable3& l3,Lockable4& l4); + + template + void lock(Lockable1& l1,Lockable2& l2,Lockable3& l3,Lockable4& l4,Lockable5& l5); + +[variablelist + +[[Effects:] [Locks the __lockable_concept_type__ objects supplied as +arguments in an unspecified and indeterminate order in a way that +avoids deadlock. It is safe to call this function concurrently from +multiple threads with the same mutexes (or other lockable objects) in +different orders without risk of deadlock. If any of the __lock_ref__ +or __try_lock_ref__ operations on the supplied +__lockable_concept_type__ objects throws an exception any locks +acquired by the function will be released before the function exits.]] + +[[Throws:] [Any exceptions thrown by calling __lock_ref__ or +__try_lock_ref__ on the supplied __lockable_concept_type__ objects.]] + +[[Postcondition:] [All the supplied __lockable_concept_type__ objects +are locked by the calling thread.]] + +] + +[endsect] + +[section:lock_range Non-member function `lock(begin,end)`] + + template + void lock(ForwardIterator begin,ForwardIterator end); + +[variablelist + +[[Preconditions:] [The `value_type` of `ForwardIterator` must implement the __lockable_concept__]] + +[[Effects:] [Locks all the __lockable_concept_type__ objects in the +supplied range in an unspecified and indeterminate order in a way that +avoids deadlock. It is safe to call this function concurrently from +multiple threads with the same mutexes (or other lockable objects) in +different orders without risk of deadlock. If any of the __lock_ref__ +or __try_lock_ref__ operations on the __lockable_concept_type__ +objects in the supplied range throws an exception any locks acquired +by the function will be released before the function exits.]] + +[[Throws:] [Any exceptions thrown by calling __lock_ref__ or +__try_lock_ref__ on the supplied __lockable_concept_type__ objects.]] + +[[Postcondition:] [All the __lockable_concept_type__ objects in the +supplied range are locked by the calling thread.]] + +] + +[endsect] + +[section:try_lock_multiple Non-member function `try_lock(Lockable1,Lockable2,...)`] + + template + int try_lock(Lockable1& l1,Lockable2& l2); + + template + int try_lock(Lockable1& l1,Lockable2& l2,Lockable3& l3); + + template + int try_lock(Lockable1& l1,Lockable2& l2,Lockable3& l3,Lockable4& l4); + + template + int try_lock(Lockable1& l1,Lockable2& l2,Lockable3& l3,Lockable4& l4,Lockable5& l5); + +[variablelist + +[[Effects:] [Calls __try_lock_ref__ on each of the +__lockable_concept_type__ objects supplied as arguments. If any of the +calls to __try_lock_ref__ returns `false` then all locks acquired are +released and the zero-based index of the failed lock is returned. + +If any of the __try_lock_ref__ operations on the supplied +__lockable_concept_type__ objects throws an exception any locks +acquired by the function will be released before the function exits.]] + +[[Returns:] [`-1` if all the supplied __lockable_concept_type__ objects +are now locked by the calling thread, the zero-based index of the +object which could not be locked otherwise.]] + +[[Throws:] [Any exceptions thrown by calling __try_lock_ref__ on the +supplied __lockable_concept_type__ objects.]] + +[[Postcondition:] [If the function returns `-1`, all the supplied +__lockable_concept_type__ objects are locked by the calling +thread. Otherwise any locks acquired by this function will have been +released.]] + +] + +[endsect] + +[section:try_lock_range Non-member function `try_lock(begin,end)`] + + template + ForwardIterator try_lock(ForwardIterator begin,ForwardIterator end); + +[variablelist + +[[Preconditions:] [The `value_type` of `ForwardIterator` must implement the __lockable_concept__]] + +[[Effects:] [Calls __try_lock_ref__ on each of the +__lockable_concept_type__ objects in the supplied range. If any of the +calls to __try_lock_ref__ returns `false` then all locks acquired are +released and an iterator referencing the failed lock is returned. + +If any of the __try_lock_ref__ operations on the supplied +__lockable_concept_type__ objects throws an exception any locks +acquired by the function will be released before the function exits.]] + +[[Returns:] [`end` if all the supplied __lockable_concept_type__ +objects are now locked by the calling thread, an iterator referencing +the object which could not be locked otherwise.]] + +[[Throws:] [Any exceptions thrown by calling __try_lock_ref__ on the +supplied __lockable_concept_type__ objects.]] + +[[Postcondition:] [If the function returns `end` then all the +__lockable_concept_type__ objects in the supplied range are locked by +the calling thread, otherwise all locks acquired by the function have +been released.]] + +] + +[endsect] + + [endsect] From b1931a3edaefc486225967bbe34c618449fdca93 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 25 Jul 2008 22:01:04 +0000 Subject: [PATCH 202/214] Fix for trac issue #2118 [SVN r47816] --- doc/thread_ref.qbk | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk index c74c984f..bebc54c4 100644 --- a/doc/thread_ref.qbk +++ b/doc/thread_ref.qbk @@ -880,7 +880,9 @@ is destroyed, interruption is again disabled. Instances of `restore_interruption [variablelist -[[Effects:] [A copy of `func` is taken and stored to in thread-specific storage. This copy is invoked when the current thread exits.]] +[[Effects:] [A copy of `func` is placed in +thread-specific storage. This copy is invoked when the current thread +exits (even if the thread has been interrupted).]] [[Postconditions:] [A copy of `func` has been saved for invocation on thread exit.]] From 69930684a94f6ceb93bcb58e2b25c665dee53e33 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 25 Jul 2008 22:21:05 +0000 Subject: [PATCH 203/214] Added a description for the new thread constructors that allow functions with arguments. [SVN r47817] --- doc/thread_ref.qbk | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk index bebc54c4..79389958 100644 --- a/doc/thread_ref.qbk +++ b/doc/thread_ref.qbk @@ -176,6 +176,9 @@ __thread_id__ yield a total order for every non-equal thread ID. template explicit thread(F f); + template + thread(F f,A1 a1,A2 a2,...); + template thread(detail::thread_move_t f); @@ -252,6 +255,28 @@ not of type __thread_interrupted__, then `std::terminate()` will be called.]] [endsect] +[section:multiple_argument_constructor Thread Constructor with arguments] + + template + thread(F f,A1 a1,A2 a2,...); + +[variablelist + +[[Preconditions:] [`F` and each `A`n must by copyable or movable.]] + +[[Effects:] [As if [link +thread.thread_management.thread.callable_constructor +`thread(boost::bind(f,a1,a2,...))`. Consequently, `f` and each `a`n +are copied into internal storage for access by the new thread.]]] + +[[Postconditions:] [`*this` refers to the newly created thread of execution.]] + +[[Throws:] [__thread_resource_error__ if an error occurs.]] + +] + +[endsect] + [section:destructor Thread Destructor] ~thread(); From 6508eff95e508512b6f4bc710d1b99385c50b67c Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 25 Jul 2008 22:22:58 +0000 Subject: [PATCH 204/214] Added note about max number of arguments [SVN r47818] --- doc/thread_ref.qbk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk index 79389958..27c0fd10 100644 --- a/doc/thread_ref.qbk +++ b/doc/thread_ref.qbk @@ -273,6 +273,8 @@ are copied into internal storage for access by the new thread.]]] [[Throws:] [__thread_resource_error__ if an error occurs.]] +[[Note:] [Currently up to nine additional arguments `a1` to `a9` can be specified in addition to the function `f`.]] + ] [endsect] From ea06434425d6325a42b307ae492bdeee13e49c53 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Sat, 26 Jul 2008 08:37:55 +0000 Subject: [PATCH 205/214] Doc updates missed by previous checkin [SVN r47826] --- doc/mutexes.qbk | 8 ++++---- doc/thread.qbk | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/mutexes.qbk b/doc/mutexes.qbk index 695b3834..ddea86a4 100644 --- a/doc/mutexes.qbk +++ b/doc/mutexes.qbk @@ -26,7 +26,7 @@ native_handle_type native_handle(); typedef unique_lock scoped_lock; - typedef scoped_lock scoped_try_lock; + typedef unspecified-type scoped_try_lock; }; __mutex__ implements the __lockable_concept__ to provide an exclusive-ownership mutex. At most one thread can own the lock on a given @@ -84,7 +84,7 @@ __try_mutex__ is a `typedef` to __mutex__, provided for backwards compatibility native_handle_type native_handle(); typedef unique_lock scoped_timed_lock; - typedef scoped_timed_lock scoped_try_lock; + typedef unspecified-type scoped_try_lock; typedef scoped_timed_lock scoped_lock; }; @@ -129,7 +129,7 @@ implementation. If no such instance exists, `native_handle()` and `native_handle native_handle_type native_handle(); typedef unique_lock scoped_lock; - typedef scoped_lock scoped_try_lock; + typedef unspecified-type scoped_try_lock; }; __recursive_mutex__ implements the __lockable_concept__ to provide an exclusive-ownership recursive mutex. At most one thread can @@ -190,7 +190,7 @@ __recursive_try_mutex__ is a `typedef` to __recursive_mutex__, provided for back native_handle_type native_handle(); typedef unique_lock scoped_lock; - typedef scoped_lock scoped_try_lock; + typedef unspecified-type scoped_try_lock; typedef scoped_lock scoped_timed_lock; }; diff --git a/doc/thread.qbk b/doc/thread.qbk index 693ea648..815ad640 100644 --- a/doc/thread.qbk +++ b/doc/thread.qbk @@ -101,8 +101,10 @@ [def __recursive_timed_mutex__ [link thread.synchronization.mutex_types.recursive_timed_mutex `boost::recursive_timed_mutex`]] [def __shared_mutex__ [link thread.synchronization.mutex_types.shared_mutex `boost::shared_mutex`]] +[template unique_lock_link[link_text] [link thread.synchronization.locks.unique_lock [link_text]]] + [def __lock_guard__ [link thread.synchronization.locks.lock_guard `boost::lock_guard`]] -[def __unique_lock__ [link thread.synchronization.locks.unique_lock `boost::unique_lock`]] +[def __unique_lock__ [unique_lock_link `boost::unique_lock`]] [def __shared_lock__ [link thread.synchronization.locks.shared_lock `boost::shared_lock`]] [def __upgrade_lock__ [link thread.synchronization.locks.upgrade_lock `boost::upgrade_lock`]] [def __upgrade_to_unique_lock__ [link thread.synchronization.locks.upgrade_to_unique_lock `boost::upgrade_to_unique_lock`]] From 2d6ed47cf2811abcb1cb0d3aad36a1d362892d90 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 8 Aug 2008 20:21:29 +0000 Subject: [PATCH 206/214] Updated signature of create_thread [SVN r48036] --- doc/thread_ref.qbk | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk index 27c0fd10..43e62ea1 100644 --- a/doc/thread_ref.qbk +++ b/doc/thread_ref.qbk @@ -933,7 +933,8 @@ error occurs within the thread library. Any exception thrown whilst copying `fun thread_group(); ~thread_group(); - thread* create_thread(const function0& threadfunc); + template + thread* create_thread(F threadfunc); void add_thread(thread* thrd); void remove_thread(thread* thrd); void join_all(); @@ -970,7 +971,8 @@ error occurs within the thread library. Any exception thrown whilst copying `fun [section:create_thread Member function `create_thread()`] - thread* create_thread(const function0& threadfunc); + template + thread* create_thread(F threadfunc); [variablelist From 0d776bcd26ee523b2ab99247ca92bdf18d312e8b Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Fri, 8 Aug 2008 20:37:30 +0000 Subject: [PATCH 207/214] Updated changes list [SVN r48037] --- doc/changes.qbk | 18 +++++++++++++++++- doc/thread.qbk | 6 ++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/doc/changes.qbk b/doc/changes.qbk index bfbc55ae..baa24fa6 100644 --- a/doc/changes.qbk +++ b/doc/changes.qbk @@ -5,7 +5,23 @@ http://www.boost.org/LICENSE_1_0.txt). ] -[section:changes Changes since boost 1.34] +[section:changes Changes since boost 1.35] + +The 1.36.0 release of Boost includes a few new features in the thread library: + +* New generic __lock_multiple_ref__ and __try_lock_multiple_ref__ functions for locking multiple mutexes at once. + +* Rvalue reference support for move semantics where the compilers supports it. + +* A few bugs fixed and missing functions added (including the serious win32 condition variable bug). + +* `scoped_try_lock` types are now backwards-compatible with Boost 1.34.0 and previous releases. + +* Support for passing function arguments to the thread function by supplying additional arguments to the __thread__ constructor. + +* Backwards-compatibility overloads added for `timed_lock` and `timed_wait` functions to allow use of `xtime` for timeouts. + +[heading Changes since boost 1.34] Almost every line of code in __boost_thread__ has been changed since the 1.34 release of boost. However, most of the interface changes have been extensions, so the new code is largely backwards-compatible with the old code. The new features and breaking diff --git a/doc/thread.qbk b/doc/thread.qbk index 815ad640..e6346db6 100644 --- a/doc/thread.qbk +++ b/doc/thread.qbk @@ -38,6 +38,12 @@ [template lock_ref_link[link_text] [link thread.synchronization.mutex_concepts.lockable.lock [link_text]]] [def __lock_ref__ [lock_ref_link `lock()`]] +[template lock_multiple_ref_link[link_text] [link thread.synchronization.lock_functions.lock_multiple [link_text]]] +[def __lock_multiple_ref__ [lock_multiple_ref_link `lock()`]] + +[template try_lock_multiple_ref_link[link_text] [link thread.synchronization.lock_functions.try_lock_multiple [link_text]]] +[def __try_lock_multiple_ref__ [try_lock_multiple_ref_link `try_lock()`]] + [template unlock_ref_link[link_text] [link thread.synchronization.mutex_concepts.lockable.unlock [link_text]]] [def __unlock_ref__ [unlock_ref_link `unlock()`]] From dcebae6d4ab9e3862007e2bccbcd1f9cdbae05bb Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 19 Aug 2008 07:03:22 +0000 Subject: [PATCH 208/214] Renamed internal bind stuff to invoker, as more expressive [SVN r48209] --- test/test_mutex.cpp | 64 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/test/test_mutex.cpp b/test/test_mutex.cpp index e85459c4..2640aca7 100644 --- a/test/test_mutex.cpp +++ b/test/test_mutex.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -109,6 +110,8 @@ struct test_timedlock void operator()() { + test_lock_times_out_if_other_thread_has_lock()(); + mutex_type mutex; boost::condition condition; @@ -178,6 +181,67 @@ struct test_recursive_lock } }; +template +struct test_lock_times_out_if_other_thread_has_lock +{ + typedef boost::unique_lock Lock; + + Mutex m; + boost::mutex done_mutex; + bool done; + bool locked; + boost::condition_variable done_cond; + + test_lock_times_out_if_other_thread_has_lock(): + done(false),locked(false) + {} + + void locking_thread() + { + Lock lock(m,boost::defer_lock); + lock.timed_lock(boost::posix_time::milliseconds(50)); + + boost::lock_guard lk(done_mutex); + locked=lock.owns_lock(); + done=true; + done_cond.notify_one(); + } + + bool is_done() const + { + return done; + } + + + void operator()() + { + Lock lock(m); + + typedef test_lock_times_out_if_other_thread_has_lock this_type; + + boost::thread t(&this_type::locking_thread,this); + + try + { + { + boost::mutex::scoped_lock lk(done_mutex); + BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), + boost::bind(&this_type::is_done,this))); + BOOST_CHECK(!locked); + } + + lock.unlock(); + t.join(); + } + catch(...) + { + lock.unlock(); + t.join(); + throw; + } + } +}; + void do_test_mutex() { test_lock()(); From b4e9be3c52284b81ecb34b9362625bae4eaca0cf Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 19 Aug 2008 10:26:53 +0000 Subject: [PATCH 209/214] Added missing relative time constructor to unique_lock [SVN r48213] --- include/boost/thread/locks.hpp | 6 ++ test/test_mutex.cpp | 140 +++++++++++++++++++-------------- 2 files changed, 86 insertions(+), 60 deletions(-) diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 6e4341ad..abbfd75b 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -234,6 +234,12 @@ namespace boost { try_lock(); } + template + unique_lock(Mutex& m_,TimeDuration const& target_time): + m(&m_),is_locked(false) + { + timed_lock(target_time); + } unique_lock(Mutex& m_,system_time const& target_time): m(&m_),is_locked(false) { diff --git a/test/test_mutex.cpp b/test/test_mutex.cpp index 2640aca7..8725a8fd 100644 --- a/test/test_mutex.cpp +++ b/test/test_mutex.cpp @@ -97,6 +97,86 @@ struct test_trylock } }; +template +struct test_lock_times_out_if_other_thread_has_lock +{ + typedef boost::unique_lock Lock; + + Mutex m; + boost::mutex done_mutex; + bool done; + bool locked; + boost::condition_variable done_cond; + + test_lock_times_out_if_other_thread_has_lock(): + done(false),locked(false) + {} + + void locking_thread() + { + Lock lock(m,boost::defer_lock); + lock.timed_lock(boost::posix_time::milliseconds(50)); + + boost::lock_guard lk(done_mutex); + locked=lock.owns_lock(); + done=true; + done_cond.notify_one(); + } + + void locking_thread_through_constructor() + { + Lock lock(m,boost::posix_time::milliseconds(50)); + + boost::lock_guard lk(done_mutex); + locked=lock.owns_lock(); + done=true; + done_cond.notify_one(); + } + + bool is_done() const + { + return done; + } + + typedef test_lock_times_out_if_other_thread_has_lock this_type; + + void do_test(void (this_type::*test_func)()) + { + Lock lock(m); + + locked=false; + done=false; + + boost::thread t(test_func,this); + + try + { + { + boost::mutex::scoped_lock lk(done_mutex); + BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), + boost::bind(&this_type::is_done,this))); + BOOST_CHECK(!locked); + } + + lock.unlock(); + t.join(); + } + catch(...) + { + lock.unlock(); + t.join(); + throw; + } + } + + + void operator()() + { + do_test(&this_type::locking_thread); + do_test(&this_type::locking_thread_through_constructor); + } +}; + template struct test_timedlock { @@ -181,66 +261,6 @@ struct test_recursive_lock } }; -template -struct test_lock_times_out_if_other_thread_has_lock -{ - typedef boost::unique_lock Lock; - - Mutex m; - boost::mutex done_mutex; - bool done; - bool locked; - boost::condition_variable done_cond; - - test_lock_times_out_if_other_thread_has_lock(): - done(false),locked(false) - {} - - void locking_thread() - { - Lock lock(m,boost::defer_lock); - lock.timed_lock(boost::posix_time::milliseconds(50)); - - boost::lock_guard lk(done_mutex); - locked=lock.owns_lock(); - done=true; - done_cond.notify_one(); - } - - bool is_done() const - { - return done; - } - - - void operator()() - { - Lock lock(m); - - typedef test_lock_times_out_if_other_thread_has_lock this_type; - - boost::thread t(&this_type::locking_thread,this); - - try - { - { - boost::mutex::scoped_lock lk(done_mutex); - BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), - boost::bind(&this_type::is_done,this))); - BOOST_CHECK(!locked); - } - - lock.unlock(); - t.join(); - } - catch(...) - { - lock.unlock(); - t.join(); - throw; - } - } -}; void do_test_mutex() { From 60d12dd395975daaaa2e073e1d87b28122c0ae4c Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 2 Sep 2008 10:22:06 +0000 Subject: [PATCH 210/214] Added recursive_mutex/condition::wait() change to list of breaking changes [SVN r48528] --- doc/changes.qbk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/changes.qbk b/doc/changes.qbk index baa24fa6..25ae1472 100644 --- a/doc/changes.qbk +++ b/doc/changes.qbk @@ -77,4 +77,7 @@ been moved to __thread_id__. * __mutex__ is now never recursive. For Boost releases prior to 1.35 __mutex__ was recursive on Windows and not on POSIX platforms. +* When using a __recursive_mutex__ with a call to [cond_any_wait_link `boost::condition_variable_any::wait()`], the mutex is only + unlocked one level, and not completely. This prior behaviour was not guaranteed and did not feature in the tests. + [endsect] From 10bf4ed5766bdae5f8901bace7ed87797c2bb30b Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 2 Sep 2008 10:38:04 +0000 Subject: [PATCH 211/214] Removed locked and get_active_count [SVN r48530] --- include/boost/thread/win32/basic_recursive_mutex.hpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/include/boost/thread/win32/basic_recursive_mutex.hpp b/include/boost/thread/win32/basic_recursive_mutex.hpp index 89c5f1dc..05eb8d76 100644 --- a/include/boost/thread/win32/basic_recursive_mutex.hpp +++ b/include/boost/thread/win32/basic_recursive_mutex.hpp @@ -64,11 +64,6 @@ namespace boost return timed_lock(get_system_time()+timeout); } - long get_active_count() - { - return mutex.get_active_count(); - } - void unlock() { if(!--recursion_count) @@ -78,11 +73,6 @@ namespace boost } } - bool locked() - { - return mutex.locked(); - } - private: bool try_recursive_lock(long current_thread_id) { From a154c2adab85e9df2ac4eb6e56d70f0ea8f4acb3 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 2 Sep 2008 10:38:17 +0000 Subject: [PATCH 212/214] Removed locked and get_active_count [SVN r48531] --- include/boost/thread/win32/basic_timed_mutex.hpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/include/boost/thread/win32/basic_timed_mutex.hpp b/include/boost/thread/win32/basic_timed_mutex.hpp index 00206061..751bdbda 100644 --- a/include/boost/thread/win32/basic_timed_mutex.hpp +++ b/include/boost/thread/win32/basic_timed_mutex.hpp @@ -123,11 +123,6 @@ namespace boost return timed_lock(system_time(timeout)); } - long get_active_count() - { - return ::boost::detail::interlocked_read_acquire(&active_count); - } - void unlock() { long const offset=lock_flag_value; @@ -141,11 +136,6 @@ namespace boost } } - bool locked() - { - return get_active_count()>=lock_flag_value; - } - private: void* get_event() { From 8eea5811ba01a6a0ccfe170fdff668ad0f1233a9 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 2 Sep 2008 16:54:56 +0000 Subject: [PATCH 213/214] Don't allocate TLS Key unless we need it; deallocate it on process exit --- partial fix for bug #2199 [SVN r48536] --- src/win32/thread.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index e54b847d..9c4f82b0 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -29,13 +29,26 @@ namespace boost void create_current_thread_tls_key() { + tss_cleanup_implemented(); // if anyone uses TSS, we need the cleanup linked in current_thread_tls_key=TlsAlloc(); BOOST_ASSERT(current_thread_tls_key!=TLS_OUT_OF_INDEXES); } + void cleanup_tls_key() + { + if(current_thread_tls_key) + { + TlsFree(current_thread_tls_key); + current_thread_tls_key=0; + } + } + detail::thread_data_base* get_current_thread_data() { - boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); + if(!current_thread_tls_key) + { + return 0; + } return (detail::thread_data_base*)TlsGetValue(current_thread_tls_key); } @@ -141,8 +154,8 @@ namespace boost } } + set_current_thread_data(0); } - set_current_thread_data(0); } unsigned __stdcall thread_start_function(void* param) @@ -544,7 +557,6 @@ namespace boost void set_tss_data(void const* key,boost::shared_ptr func,void* tss_data,bool cleanup_existing) { - tss_cleanup_implemented(); // if anyone uses TSS, we need the cleanup linked in if(tss_data_node* const current_node=find_tss_data(key)) { if(cleanup_existing && current_node->func.get()) @@ -572,7 +584,9 @@ extern "C" BOOST_THREAD_DECL void on_thread_enter() {} extern "C" BOOST_THREAD_DECL void on_process_exit() -{} +{ + boost::cleanup_tls_key(); +} extern "C" BOOST_THREAD_DECL void on_thread_exit() { From 8fd0dd0cc0945673cf463661da9fbb600dc4b50e Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Tue, 2 Sep 2008 16:56:57 +0000 Subject: [PATCH 214/214] Define a raw DLL main which is called by the C runtime if we're statically linked into a DLL --- fix for issue #2199 [SVN r48537] --- src/win32/tss_pe.cpp | 46 ++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/win32/tss_pe.cpp b/src/win32/tss_pe.cpp index bd2e61d8..ea831218 100644 --- a/src/win32/tss_pe.cpp +++ b/src/win32/tss_pe.cpp @@ -26,11 +26,11 @@ namespace { { switch (dwReason) { - case DLL_THREAD_DETACH: - { - on_thread_exit(); - break; - } + case DLL_THREAD_DETACH: + { + on_thread_exit(); + break; + } } } @@ -125,10 +125,10 @@ extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata #pragma section(".CRT$XCU",long,read) #pragma section(".CRT$XTU",long,read) #pragma section(".CRT$XLC",long,read) - static __declspec(allocate(".CRT$XLC")) _TLSCB __xl_ca=on_tls_callback; - static __declspec(allocate(".CRT$XIU"))_PVFV p_tls_prepare = on_tls_prepare; - static __declspec(allocate(".CRT$XCU"))_PVFV p_process_init = on_process_init; - static __declspec(allocate(".CRT$XTU"))_PVFV p_process_term = on_process_term; + __declspec(allocate(".CRT$XLC")) _TLSCB __xl_ca=on_tls_callback; + __declspec(allocate(".CRT$XIU"))_PVFV p_tls_prepare = on_tls_prepare; + __declspec(allocate(".CRT$XCU"))_PVFV p_process_init = on_process_init; + __declspec(allocate(".CRT$XTU"))_PVFV p_process_term = on_process_term; #else #if (_MSC_VER >= 1300) // 1300 == VC++ 7.0 # pragma data_seg(push, old_seg) @@ -168,6 +168,7 @@ extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata #pragma warning(push) #pragma warning(disable:4189) #endif + PVAPI on_tls_prepare(void) { //The following line has an important side effect: @@ -239,15 +240,32 @@ extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata { switch (dwReason) { - case DLL_THREAD_DETACH: - { - on_thread_exit(); - break; - } + case DLL_THREAD_DETACH: + on_thread_exit(); + break; } } + + BOOL WINAPI dll_callback(HANDLE, DWORD dwReason, LPVOID) + { + switch (dwReason) + { + case DLL_THREAD_DETACH: + on_thread_exit(); + break; + case DLL_PROCESS_DETACH: + on_process_exit(); + break; + } + return true; + } } //namespace +extern "C" +{ + extern BOOL (WINAPI * const _pRawDllMain)(HANDLE, DWORD, LPVOID)=&dll_callback; +} + extern "C" void tss_cleanup_implemented(void) { /*