From cef8fb0bd7a49d689b16edcfa555c03f83dd93a4 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Tue, 5 Aug 2008 05:39:57 +0000 Subject: [PATCH] merged from trunk [SVN r47983] --- doc/acknowledgements.qbk | 7 + doc/barrier.qbk | 7 + doc/changes.qbk | 7 + doc/condition_variables.qbk | 19 + doc/mutex_concepts.qbk | 202 ++++++++ doc/mutexes.qbk | 27 +- doc/once.qbk | 15 +- doc/overview.qbk | 15 + doc/shared_mutex_ref.qbk | 9 + doc/thread.qbk | 11 +- doc/thread_ref.qbk | 64 ++- doc/time.qbk | 11 + doc/tss.qbk | 9 + include/boost/thread/detail/move.hpp | 4 + include/boost/thread/detail/platform.hpp | 6 +- include/boost/thread/detail/thread.hpp | 11 +- include/boost/thread/locks.hpp | 463 ++++++++++++++---- .../thread/pthread/condition_variable.hpp | 11 + .../thread/pthread/condition_variable_fwd.hpp | 10 + include/boost/thread/pthread/mutex.hpp | 5 + include/boost/thread/tss.hpp | 1 + .../boost/thread/win32/basic_timed_mutex.hpp | 6 + include/boost/thread/win32/mutex.hpp | 2 +- include/boost/thread/win32/shared_mutex.hpp | 209 +++----- .../boost/thread/win32/thread_primitives.hpp | 23 +- src/pthread/thread.cpp | 4 +- test/test_condition_timed_wait_times_out.cpp | 88 +++- test/test_generic_locks.cpp | 10 + test/test_lock_concept.cpp | 314 +++++++++++- test/test_shared_mutex.cpp | 10 +- test/test_shared_mutex_part_2.cpp | 115 ++++- test/test_thread_mf.cpp | 94 ++-- test/test_xtime.cpp | 44 ++ 33 files changed, 1512 insertions(+), 321 deletions(-) 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..e54dd34c 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] @@ -67,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 @@ -75,6 +84,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 @@ -284,6 +296,8 @@ return true; [section:condition_variable_any Class `condition_variable_any`] + #include + namespace boost { class condition_variable_any @@ -292,6 +306,9 @@ return true; condition_variable_any(); ~condition_variable_any(); + void notify_one(); + void notify_all(); + template void wait(lock_type& lock); @@ -485,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 2fc5cb97..1affa78c 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 @@ -305,6 +312,8 @@ without blocking.]] [section:lock_guard Class template `lock_guard`] + #include + template class lock_guard { @@ -369,6 +378,8 @@ object passed to the constructor.]] [section:unique_lock Class template `unique_lock`] + #include + template class unique_lock { @@ -610,6 +621,8 @@ __owns_lock_ref__ returns `false`.]] [section:shared_lock Class template `shared_lock`] + #include + template class shared_lock { @@ -843,6 +856,8 @@ __owns_lock_shared_ref__ returns `false`.]] [section:upgrade_lock Class template `upgrade_lock`] + #include + template class upgrade_lock { @@ -890,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 { @@ -914,4 +931,189 @@ __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] + +[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] diff --git a/doc/mutexes.qbk b/doc/mutexes.qbk index cf920235..ddea86a4 100644 --- a/doc/mutexes.qbk +++ b/doc/mutexes.qbk @@ -1,7 +1,16 @@ +[/ + (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`] + #include + class mutex: boost::noncopyable { @@ -17,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 @@ -44,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. @@ -52,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 { @@ -71,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; }; @@ -99,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 { @@ -114,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 @@ -143,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. @@ -151,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 { @@ -171,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/once.qbk b/doc/once.qbk index 86b0ef2b..fd53e7de 100644 --- a/doc/once.qbk +++ b/doc/once.qbk @@ -1,9 +1,18 @@ +[/ + (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. [section:once_flag Typedef `once_flag`] + #include + typedef platform-specific-type once_flag; #define BOOST_ONCE_INIT platform-specific-initializer @@ -15,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); @@ -24,8 +35,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`. ]] diff --git a/doc/overview.qbk b/doc/overview.qbk index 63edba0f..c647a6d2 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 @@ -12,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 4b2c2ec4..49cd1673 100644 --- a/doc/shared_mutex_ref.qbk +++ b/doc/shared_mutex_ref.qbk @@ -1,5 +1,14 @@ +[/ + (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`] + #include + class shared_mutex { public: diff --git a/doc/thread.qbk b/doc/thread.qbk index b2ea6e6b..815ad640 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]] @@ -94,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`]] diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk index e9bc5150..27c0fd10 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] @@ -158,6 +165,8 @@ __thread_id__ yield a total order for every non-equal thread ID. [section:thread Class `thread`] + #include + class thread { public: @@ -167,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); @@ -243,6 +255,30 @@ 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.]] + +[[Note:] [Currently up to nine additional arguments `a1` to `a9` can be specified in addition to the function `f`.]] + +] + +[endsect] + [section:destructor Thread Destructor] ~thread(); @@ -473,6 +509,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 @@ -486,6 +524,8 @@ value as `this->get_id()` prior to the call.]] [section:id Class `boost::thread::id`] + #include + class thread::id { public: @@ -634,6 +674,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(); @@ -651,6 +693,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(); @@ -668,6 +712,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(); @@ -685,6 +731,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(); @@ -702,6 +750,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 @@ -722,6 +772,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(); @@ -739,6 +791,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 @@ -790,6 +844,8 @@ interruption state on destruction. Instances of `disable_interruption` cannot be [section:restore_interruption Class `restore_interruption`] + #include + namespace this_thread { class restore_interruption @@ -844,12 +900,16 @@ 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); [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.]] @@ -864,6 +924,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 f817206c..87985427 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 @@ -40,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. @@ -48,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 b6e1ff69..6a9ffc44 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] @@ -37,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 { diff --git a/include/boost/thread/detail/move.hpp b/include/boost/thread/detail/move.hpp index f8917758..91b2eda1 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 BOOST_NO_SFINAE #include #include +#endif #include @@ -37,11 +39,13 @@ namespace boost }; } +#ifndef BOOST_NO_SFINAE 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/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/detail/thread.hpp b/include/boost/thread/detail/thread.hpp index a7e82f7f..3d39d1df 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 BOOST_NO_SFINAE + 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 b175c859..6e4341ad 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -10,12 +10,21 @@ #include #include #include -#include +#include #include namespace boost { + struct xtime; + +#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 @@ -77,7 +86,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 {}; @@ -96,6 +111,74 @@ namespace boost template class upgrade_lock; + template + class unique_lock; + + namespace detail + { + template + class try_lock_wrapper; + } + +#ifdef BOOST_THREAD_NO_AUTO_DETECT_MUTEX_TYPES + 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 { @@ -126,8 +209,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 +239,40 @@ 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; + } + 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) { @@ -185,7 +304,6 @@ namespace boost swap(temp); return *this; } - void swap(unique_lock& other) { std::swap(m,other.m); @@ -196,6 +314,7 @@ namespace boost std::swap(m,other->m); std::swap(is_locked,other->is_locked); } +#endif ~unique_lock() { @@ -234,6 +353,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()) @@ -275,11 +399,27 @@ 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 + inline unique_lock&& move(unique_lock&& ul) + { + return ul; + } +#endif template class shared_lock @@ -378,11 +518,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() { @@ -418,6 +576,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()) @@ -428,7 +596,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; @@ -444,6 +612,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 { @@ -576,6 +758,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 +780,7 @@ namespace boost m->unlock_upgrade_and_lock(); } } - +#endif template class upgrade_to_unique_lock { @@ -685,6 +879,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); @@ -693,6 +893,7 @@ namespace boost { base::swap(*other); } +#endif void lock() { @@ -726,15 +927,23 @@ namespace boost typedef typename base::bool_type bool_type; operator bool_type() const { - return static_cast(*this); + return base::operator bool_type(); } }; +#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) @@ -859,28 +1068,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 @@ -995,10 +1239,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 @@ -1020,9 +1306,6 @@ namespace boost } - template - typename disable_if, void>::type lock(Iterator begin,Iterator end); - namespace detail { template @@ -1050,70 +1333,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; @@ -1121,11 +1393,28 @@ 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; + } } } } + } } 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/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/tss.hpp b/include/boost/thread/tss.hpp index 4efc6e8b..76380aa6 100644 --- a/include/boost/thread/tss.hpp +++ b/include/boost/thread/tss.hpp @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) // (C) Copyright 2007-8 Anthony Williams +#include #include #include 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/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/shared_mutex.hpp b/include/boost/thread/win32/shared_mutex.hpp index 9f3c489e..58e8093a 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)) { @@ -457,10 +398,36 @@ 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; - do + for(;;) { state_data new_state=old_state; new_state.upgrade=false; @@ -487,20 +454,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 +481,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 +506,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 +531,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 +555,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); } diff --git a/include/boost/thread/win32/thread_primitives.hpp b/include/boost/thread/win32/thread_primitives.hpp index de58c45b..67a1bc3c 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 @@ -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; diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 2ceb68af..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__) +#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__) +#elif defined(BOOST_HAS_UNISTD_H) && defined(_SC_NPROCESSORS_ONLN) int const count=sysconf(_SC_NPROCESSORS_ONLN); return (count>0)?count:0; #else 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*[]) 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() { diff --git a/test/test_lock_concept.cpp b/test/test_lock_concept.cpp index 8b16dfb5..06367d5b 100644 --- a/test/test_lock_concept.cpp +++ b/test/test_lock_concept.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -82,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 { @@ -149,6 +266,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 { @@ -204,6 +379,8 @@ struct test_locks_can_be_swapped { Mutex m1; Mutex m2; + Mutex m3; + Lock l1(m1); Lock l2(m2); @@ -219,16 +396,16 @@ 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); } }; - - -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 +415,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,26 +447,125 @@ 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()(); 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; + 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()(); + + 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*[]) { boost::unit_test_framework::test_suite* test = 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)); + test->add(BOOST_TEST_CASE(&test_shared_lock)); return test; } 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 { 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 = 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(); } 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; }