From 677dbe768819b2aa5b4ca07fccd055f149ad4a70 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sun, 31 Mar 2013 10:56:43 +0000 Subject: [PATCH] Thread: merge from trunk 1.54. Fix #8027,#8323,#8337. [SVN r83660] --- doc/changes.qbk | 25 +- doc/future_ref.qbk | 2 +- doc/internal_locking.qbk | 2 +- doc/mutex_concepts.qbk | 53 +- doc/mutexes.qbk | 404 ------------ doc/once.qbk | 8 + doc/sync_queues_ref.qbk | 414 ++++++++++++ doc/sync_streams.qbk | 109 ++++ doc/sync_tutorial.qbk | 2 - doc/synchronized_value.qbk | 13 +- doc/synchronized_value_ref.qbk | 408 ++++++++++++ doc/thread.qbk | 12 +- example/condition.cpp | 2 +- example/future_then.cpp | 4 +- example/monitor.cpp | 2 +- example/not_interleaved.cpp | 10 +- example/perf_shared_mutex.cpp | 2 +- example/producer_consumer.cpp | 127 ++++ example/producer_consumer_bounded.cpp | 126 ++++ example/shared_monitor.cpp | 2 +- example/shared_mutex.cpp | 2 +- example/starvephil.cpp | 2 +- example/tennis.cpp | 2 +- example/thread.cpp | 2 +- example/thread_guard.cpp | 2 +- example/xtime.cpp | 2 +- include/boost/thread/detail/thread.hpp | 9 +- include/boost/thread/future.hpp | 92 ++- include/boost/thread/pthread/shared_mutex.hpp | 201 +++++- include/boost/thread/scoped_thread.hpp | 2 +- include/boost/thread/sync_bounded_queue.hpp | 594 ++++++++++++++++++ include/boost/thread/sync_queue.hpp | 516 +++++++++++++++ include/boost/thread/thread_functors.hpp | 2 +- test/Jamfile.v2 | 5 +- test/sync/futures/future/then_pass.cpp | 2 +- .../shared_lock/cons/adopt_lock_pass.cpp | 2 +- .../shared_lock_guard/adopt_lock_pass.cpp | 4 +- test/test_2741.cpp | 2 +- test/test_3837.cpp | 1 + test/test_4521.cpp | 3 +- test/test_4882.cpp | 2 +- test/test_5351.cpp | 2 +- test/test_5502.cpp | 2 +- test/test_5542_1.cpp | 2 +- test/test_5542_2.cpp | 2 +- test/test_5542_3.cpp | 2 +- test/test_5891.cpp | 2 +- test/test_6130.cpp | 2 +- test/test_6174.cpp | 3 +- test/test_7160.cpp | 2 +- test/test_7328.cpp | 2 +- test/test_7571.cpp | 2 +- test/test_7665.cpp | 2 +- test/test_7666.cpp | 2 +- test/test_7720.cpp | 2 +- test/test_7755.cpp | 2 +- test/test_condition.cpp | 2 +- test/test_condition_notify_all.cpp | 2 +- test/test_condition_notify_one.cpp | 2 +- test/test_condition_timed_wait_times_out.cpp | 2 +- test/test_futures.cpp | 2 +- test/test_generic_locks.cpp | 2 +- test/test_hardware_concurrency.cpp | 2 +- test/test_lock_concept.cpp | 17 +- test/test_move_function.cpp | 2 +- test/test_mutex.cpp | 2 +- test/test_shared_mutex.cpp | 5 + test/test_shared_mutex_part_2.cpp | 2 +- test/test_shared_mutex_timed_locks.cpp | 2 +- test/test_thread.cpp | 2 +- test/test_thread_exit.cpp | 2 +- test/test_thread_id.cpp | 2 +- test/test_thread_launching.cpp | 2 +- test/test_thread_mf.cpp | 2 +- test/test_thread_move.cpp | 2 +- test/test_thread_move_return.cpp | 2 +- test/test_thread_return_local.cpp | 2 +- .../this_thread/get_id/get_id_pass.cpp | 2 +- .../this_thread/sleep_for/sleep_for_pass.cpp | 2 +- .../sleep_until/sleep_until_pass.cpp | 2 +- test/threads/thread/assign/copy_fail.cpp | 2 +- test/threads/thread/assign/move_pass.cpp | 2 +- test/threads/thread/constr/FArgs_pass.cpp | 2 +- test/threads/thread/constr/F_pass.cpp | 2 +- .../thread/constr/FrvalueArgs_pass.cpp | 2 +- test/threads/thread/constr/Frvalue_pass.cpp | 2 +- test/threads/thread/constr/copy_fail.cpp | 2 +- test/threads/thread/constr/default_pass.cpp | 2 +- test/threads/thread/constr/move_pass.cpp | 2 +- test/threads/thread/destr/dtor_pass.cpp | 2 +- test/threads/thread/id/hash_pass.cpp | 2 +- test/threads/thread/members/detach_pass.cpp | 2 +- test/threads/thread/members/get_id_pass.cpp | 2 +- test/threads/thread/members/join_pass.cpp | 2 +- test/threads/thread/members/joinable_pass.cpp | 2 +- .../thread/members/native_handle_pass.cpp | 4 +- test/threads/thread/members/swap_pass.cpp | 2 +- .../thread/members/try_join_for_pass.cpp | 2 +- .../thread/members/try_join_until_pass.cpp | 2 +- test/threads/thread/non_members/swap_pass.cpp | 2 +- .../static/hardware_concurrency_pass.cpp | 2 +- 101 files changed, 2763 insertions(+), 554 deletions(-) create mode 100644 doc/sync_queues_ref.qbk create mode 100644 doc/sync_streams.qbk create mode 100644 doc/synchronized_value_ref.qbk create mode 100644 example/producer_consumer.cpp create mode 100644 example/producer_consumer_bounded.cpp create mode 100644 include/boost/thread/sync_bounded_queue.hpp create mode 100644 include/boost/thread/sync_queue.hpp diff --git a/doc/changes.qbk b/doc/changes.qbk index 49d153dd..aa345f4c 100644 --- a/doc/changes.qbk +++ b/doc/changes.qbk @@ -8,27 +8,42 @@ [section:changes History] +[/ +[heading Version 4.2.0 - boost 1.55] + +[*New Features:] + +* [@http://svn.boost.org/trac/boost/ticket/8273 #8273] Add externally locked streams +* [@http://svn.boost.org/trac/boost/ticket/8274 #8274] Add concurrent queue + +[*Fixed Bugs:] + +] + [heading Version 4.1.0 - boost 1.54] +[*New Features:] + * [@http://svn.boost.org/trac/boost/ticket/7285 #7285] C++11 compliance: Allow to pass movable arguments for call_once. * [@http://svn.boost.org/trac/boost/ticket/7449 #7449] Synchro: Add a synchronized value class -[heading Version 4.0.0 - boost 1.53] +[*Fixed Bugs:] * [@http://svn.boost.org/trac/boost/ticket/4882 #4882] Win32 shared_mutex does not handle timeouts correctly. * [@http://svn.boost.org/trac/boost/ticket/6652 #6652] Boost.Thread shared_mutex.hpp:50:99: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] * [@http://svn.boost.org/trac/boost/ticket/7720 #7720] exception lock_error while intensive locking/unlocking of mutex * [@http://svn.boost.org/trac/boost/ticket/7755 #7755] Thread: deadlock with shared_mutex on Windows -* [@http://svn.boost.org/trac/boost/ticket/4882 #8070] prefer GetTickCount64 over GetTickCount +* [@http://svn.boost.org/trac/boost/ticket/8027 #8027] thread library fails to compile with Visual Studio 2003 +* [@http://svn.boost.org/trac/boost/ticket/8070 #8070] prefer GetTickCount64 over GetTickCount * [@http://svn.boost.org/trac/boost/ticket/8136 #8136] boost::this_thread::sleep_for() sleeps longer than it should in Windows * [@http://svn.boost.org/trac/boost/ticket/8212 #8212] Boost thread compilation error on Solaris 10 * [@http://svn.boost.org/trac/boost/ticket/8237 #8237] fix documentation for 'thread_group' * [@http://svn.boost.org/trac/boost/ticket/8239 #8239] barrier::wait() not marked as interruption_point +* [@http://svn.boost.org/trac/boost/ticket/8323 #8323] boost::thread::try_join_for/try_join_until may block indefinitely due to a combination of problems in Boost.Thread and Boost.Chrono +* [@http://svn.boost.org/trac/boost/ticket/8337 #8337] The internal representation of "std::string(this->code()->message())" escapes, but is destroyed when it exits scope. -[*New Features:] - -[*Fixed Bugs:] +[heading Version 4.0.0 - boost 1.53] [/ [*Breaking changes:] diff --git a/doc/future_ref.qbk b/doc/future_ref.qbk index 44ae2251..36ef6157 100644 --- a/doc/future_ref.qbk +++ b/doc/future_ref.qbk @@ -1621,7 +1621,7 @@ provides the result of the function in a future object with which it shares a sh [warning `async(launch::deferred, F)` is NOT YET IMPLEMENTED!] -[warning the variadic prototype is provided only on C++11 compilers supporting rvalue references, variadic templates, decltype and a standard library providing , and BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK is defined.] +[warning the variadic prototype is provided only on C++11 compilers supporting rvalue references, variadic templates, decltype and a standard library providing (waiting for a boost::tuple that is move aware), and BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK is defined.] [heading Non-Variadic variant] diff --git a/doc/internal_locking.qbk b/doc/internal_locking.qbk index b5def7c4..88c70ef1 100644 --- a/doc/internal_locking.qbk +++ b/doc/internal_locking.qbk @@ -78,7 +78,7 @@ The above example works well as long as the bankAgent and Joe doesn't access Joe mtx_.lock(); int b = balance_; mtx_.unlock(); - return balance_; + return b; } }; diff --git a/doc/mutex_concepts.qbk b/doc/mutex_concepts.qbk index 8f575598..5c89e9b2 100644 --- a/doc/mutex_concepts.qbk +++ b/doc/mutex_concepts.qbk @@ -42,6 +42,8 @@ Lock ownership acquired through a call to __lock_ref__ must be released through [variablelist +[[Requires:] [The calling thread doesn't owns the mutex if the mutex is not recursive.]] + [[Effects:] [The current thread blocks until ownership can be obtained for the current thread.]] [[Synchronization:] [Prior `unlock()` operations on the same object synchronizes with this operation. ]] @@ -97,7 +99,12 @@ Lock ownership acquired through a call to __lock_ref__ must be released through } } -Some of the algorithms on mutexes use this trait via SFINAE. If BOOST_THREAD_NO_AUTO_DETECT_MUTEX_TYPES is defined you will need to specialize this traits for the models of BasicLockable you could build. + +Some of the algorithms on mutexes use this trait via SFINAE. + +This trait is true_type if the parameter L meets the __Lockable requirements. + +[warning If BOOST_THREAD_NO_AUTO_DETECT_MUTEX_TYPES is defined you will need to specialize this traits for the models of BasicLockable you could build.] [endsect] [endsect] @@ -121,6 +128,9 @@ Lock ownership acquired through a call to __try_lock_ref__ must be released thro [variablelist + +[[Requires:] [The calling thread doesn't owns the mutex if the mutex is not recursive.]] + [[Effects:] [Attempt to obtain ownership for the current thread without blocking.]] [[Synchronization:] [If `try_lock()` returns true, prior `unlock()` operations on the same object synchronize with this operation.]] @@ -152,7 +162,11 @@ failure, even in the absence of spurious failures.]] } } -Some of the algorithms on mutexes use this trait via SFINAE. If BOOST_THREAD_NO_AUTO_DETECT_MUTEX_TYPES is defined you will need to specialize this traits for the models of Lockable you could build. +Some of the algorithms on mutexes use this trait via SFINAE. + +This trait is true_type if the parameter L meets the __Lockable requirements. + +[warning If BOOST_THREAD_NO_AUTO_DETECT_MUTEX_TYPES is defined you will need to specialize this traits for the models of Lockable you could build.] [endsect] [endsect] @@ -185,6 +199,37 @@ It should be specialized by the user providing other model of recursive lockable [endsect] +[section:is_recursive_basic_lockable `is_recursive_basic_lockable` trait -- EXTENSION] + + // #include + namespace boost + { + namespace sync + { + template + class is_recursive_basic_lockable;// EXTENSION + } + } + +This traits is true_type if is_basic_lockable and is_recursive_mutex_sur_parolle. + +[endsect] +[section:is_recursive_lockable `is_recursive_lockable` trait -- EXTENSION] + + // #include + namespace boost + { + namespace sync + { + template + class is_recursive_lockable;// EXTENSION + } + } + +This traits is true_type if is_lockable and is_recursive_mutex_sur_parolle. + +[endsect] + [endsect] @@ -220,6 +265,8 @@ Lock ownership acquired through a call to __try_lock_for or __try_lock_until mus [variablelist +[[Requires:] [The calling thread doesn't owns the mutex if the mutex is not recursive.]] + [[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__.]] @@ -239,6 +286,8 @@ reached. If the specified time has already passed, behaves as __try_lock_ref__.] [variablelist +[[Requires:] [The calling thread doesn't owns the mutex if the mutex is not recursive.]] + [[Effects:] [As-if `__try_lock_until(chrono::steady_clock::now() + rel_time)`.]] [[Synchronization:] [If `try_lock_for()` returns true, prior `unlock()` operations on the same object synchronize with this operation.]] diff --git a/doc/mutexes.qbk b/doc/mutexes.qbk index 48cba6fa..b32f380f 100644 --- a/doc/mutexes.qbk +++ b/doc/mutexes.qbk @@ -240,409 +240,5 @@ implementation. If no such instance exists, `native_handle()` and `native_handle [endsect] -[section:synchronized_value_ref Synchronized Values] - namespace boost - { - - template - class synchronized_value; - - // Specialized swap algorithm - template - void swap(synchronized_value & lhs, synchronized_value & rhs); - template - void swap(synchronized_value & lhs, T & rhs); - template - void swap(T & lhs, synchronized_value & rhs); - - // Hash support - template - struct hash >; - - // Comparison - template - bool operator==(synchronized_value const&lhs, synchronized_value const& rhs) - template - bool operator!=(synchronized_value const&lhs, synchronized_value const& rhs) - template - bool operator<(synchronized_value const&lhs, synchronized_value const& rhs) - template - bool operator<=(synchronized_value const&lhs, synchronized_value const& rhs) - template - bool operator>(synchronized_value const&lhs, synchronized_value const& rhs) - template - bool operator>=(synchronized_value const&lhs, synchronized_value const& rhs) - - // Comparison with T - template - bool operator==(T const& lhs, synchronized_value const&rhs); - template - bool operator!=(T const& lhs, synchronized_value const&rhs); - template - bool operator<(T const& lhs, synchronized_value const&rhs); - template - bool operator<=(T const& lhs, synchronized_value const&rhs); - template - bool operator>(T const& lhs, synchronized_value const&rhs); - template - bool operator>=(T const& lhs, synchronized_value const&rhs); - - template - bool operator==(synchronized_value const& lhs, T const& rhs); - template - bool operator!=(synchronized_value const& lhs, T const& rhs); - template - bool operator<(synchronized_value const& lhs, T const& rhs); - template - bool operator<=(synchronized_value const& lhs, T const& rhs); - template - bool operator>(synchronized_value const& lhs, T const& rhs); - template - bool operator>=(synchronized_value const& lhs, T const& rhs); - - #if ! defined(BOOST_THREAD_NO_SYNCHRONIZE) - template - std::tuple::type ...> synchronize(SV& ...sv); - #endif - } - -[section:synchronized_value Class `synchronized_value`] - - #include - - namespace boost - { - - template - class synchronized_value - { - public: - typedef T value_type; - typedef Lockable mutex_type; - - synchronized_value() noexept(is_nothrow_default_constructible::value); - synchronized_value(T const& other) noexept(is_nothrow_copy_constructible::value); - synchronized_value(T&& other) noexept(is_nothrow_move_constructible::value); - synchronized_value(synchronized_value const& rhs); - synchronized_value(synchronized_value&& other); - - // mutation - synchronized_value& operator=(synchronized_value const& rhs); - synchronized_value& operator=(value_type const& val); - void swap(synchronized_value & rhs); - void swap(value_type & rhs); - - //observers - T get() const; - #if ! defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) - explicit operator T() const; - #endif - - strict_lock_ptr operator->(); - const_strict_lock_ptr operator->() const; - strict_lock_ptr synchronize(); - const_strict_lock_ptr synchronize() const; - - deref_value operator*();; - const_deref_value operator*() const; - - private: - T value_; // for exposition only - mutable mutex_type mtx_; // for exposition only - }; - } - -[variablelist - -[[Requires:] [`Lockable` is `Lockable`.]] - -] - - -[section:constructor `synchronized_value()`] - - synchronized_value() noexept(is_nothrow_default_constructible::value); - -[variablelist - -[[Requires:] [`T` is `DefaultConstructible`.]] -[[Effects:] [Default constructs the cloaked value_type]] - -[[Throws:] [Any exception thrown by `value_type()`.]] - -] - -[endsect] - - -[section:constructor_vt `synchronized_value(T const&)`] - - synchronized_value(T const& other) noexept(is_nothrow_copy_constructible::value); - -[variablelist - -[[Requires:] [`T` is `CopyConstructible`.]] -[[Effects:] [Copy constructs the cloaked value_type using the parameter `other`]] - -[[Throws:] [Any exception thrown by `value_type(other)`.]] - -] - -[endsect] - -[section:copy_cons `synchronized_value(synchronized_value const&)`] - - synchronized_value(synchronized_value const& rhs); - -[variablelist - -[[Requires:] [`T` is `DefaultConstructible` and `Assignable`.]] -[[Effects:] [Assigns the value on a scope protected by the mutex of the rhs. The mutex is not copied.]] - -[[Throws:] [Any exception thrown by `value_type()` or `value_type& operator=(value_type&)` or `mtx_.lock()`.]] - -] - -[endsect] - -[section:move_vt `synchronized_value(T&&)`] - - synchronized_value(T&& other) noexept(is_nothrow_move_constructible::value); - -[variablelist - -[[Requires:] [`T` is `CopyMovable `.]] -[[Effects:] [Move constructs the cloaked value_type]] - -[[Throws:] [Any exception thrown by `value_type(value_type&&)`.]] - -] - -[endsect] - -[section:move `synchronized_value(synchronized_value&&)`] - - synchronized_value(synchronized_value&& other); - -[variablelist - -[[Requires:] [`T` is `CopyMovable `.]] -[[Effects:] [Move constructs the cloaked value_type]] - -[[Throws:] [Any exception thrown by `value_type(value_type&&)` or `mtx_.lock()`.]] - -] - -[endsect] - -[section:assign `operator=(synchronized_value const&)`] - - synchronized_value& operator=(synchronized_value const& rhs); - -[variablelist - -[[Requires:] [`T` is `Assignale`.]] -[[Effects:] [Copies the underlying value on a scope protected by the two mutexes. The mutex is not copied. The locks are acquired avoiding deadlock. For example, there is no problem if one thread assigns `a = b` and the other assigns `b = a`.]] -[[Return:] [`*this`]] - -[[Throws:] [Any exception thrown by `value_type& operator(value_type const&)` or `mtx_.lock()`.]] - -] - -[endsect] -[section:assign_vt `operator=(T const&)`] - - synchronized_value& operator=(value_type const& val); - -[variablelist - -[[Requires:] [`T` is `Assignale`.]] -[[Effects:] [Copies the value on a scope protected by the mutex.]] -[[Return:] [`*this`]] - -[[Throws:] [Any exception thrown by `value_type& operator(value_type const&)` or `mtx_.lock()`.]] - -] - -[endsect] - -[section:get `get() const`] - - T get() const; - -[variablelist - -[[Requires:] [`T` is `CopyConstructible`.]] -[[Return:] [`A copy of the protected value obtained on a scope protected by the mutex.`]] - -[[Throws:] [Any exception thrown by `value_type(value_type const&)` or `mtx_.lock()`.]] - -] - -[endsect] - - -[section:T `operator T() const`] - - #if ! defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) - explicit operator T() const; - #endif - -[variablelist - -[[Requires:] [`T` is `CopyConstructible`.]] -[[Return:] [`A copy of the protected value obtained on a scope protected by the mutex.`]] - -[[Throws:] [Any exception thrown by `value_type(value_type const&)` or `mtx_.lock()`.]] - -] - -[endsect] - -[section:swap `swap(synchronized_value&)`] - - void swap(synchronized_value & rhs); - -[variablelist - -[[Requires:] [`T` is `Assignale`.]] -[[Effects:] [Swaps the data on a scope protected by both mutex. Both mutex are acquired to avoid dead-lock. The mutexes are not swapped.]] - -[[Throws:] [Any exception thrown by `swap(value_, rhs.value)` or `mtx_.lock()` or `rhs_.mtx_.lock()`.]] - -] - -[endsect] - -[section:swap_vt `swap(synchronized_value&)`] - - void swap(value_type & rhs); - -[variablelist - -[[Requires:] [`T` is `Swapable`.]] -[[Effects:] [Swaps the data on a scope protected by both mutex. Both mutex are acquired to avoid dead-lock. The mutexes are not swapped.]] - -[[Throws:] [Any exception thrown by `swap(value_, rhs)` or `mtx_.lock()`.]] - -] - -[endsect] -[section:indir `operator->()`] - - strict_lock_ptr operator->(); - - -Essentially calling a method `obj->foo(x, y, z)` calls the method `foo(x, y, z)` inside a critical section as long-lived as the call itself. - -[variablelist - -[[Return:] [`A strict_lock_ptr<>.`]] - -[[Throws:] [Nothing.]] - -] - -[endsect] -[section:indir_const `operator->() const`] - - const_strict_lock_ptr operator->() const; - - -If the `synchronized_value` object involved is const-qualified, then you'll only be able to call const methods -through `operator->`. So, for example, `vec->push_back("xyz")` won't work if `vec` were const-qualified. -The locking mechanism capitalizes on the assumption that const methods don't modify their underlying data. - -[variablelist - -[[Return:] [`A const_strict_lock_ptr <>.`]] - -[[Throws:] [Nothing.]] - -] - -[endsect] -[section:synchronize `synchronize()`] - - strict_lock_ptr synchronize(); - -The synchronize() factory make easier to lock on a scope. As discussed, `operator->` can only lock over the duration of a call, so it is insufficient for complex operations. With `synchronize()` you get to lock the object in a scoped and to directly access the object inside that scope. - -[*Example:] - - void fun(synchronized_value> & vec) { - auto vec2=vec.synchronize(); - vec2.push_back(42); - assert(vec2.back() == 42); - } - -[variablelist - -[[Return:] [`A strict_lock_ptr <>.`]] - -[[Throws:] [Nothing.]] - -] - -[endsect] -[section:synchronize_const `synchronize() const`] - - const_strict_lock_ptr synchronize() const; - -[variablelist - -[[Return:] [`A const_strict_lock_ptr <>.`]] - -[[Throws:] [Nothing.]] - -] - -[endsect] - -[section:deref `operator*()`] - - deref_value operator*();; - -[variablelist - -[[Return:] [`A an instance of a class that locks the mutex on construction and unlocks it on destruction and provides implicit conversion to a reference to the protected value.`]] - -[[Throws:] [Nothing.]] - -] - -[endsect] -[section:deref_const `operator*() const`] - - const_deref_value operator*() const; - - -[variablelist - -[[Return:] [`A an instance of a class that locks the mutex on construction and unlocks it on destruction and provides implicit conversion to a constant reference to the protected value.`]] - -[[Throws:] [Nothing.]] - -] - -[endsect] - - - - -[endsect] -[section:synchronize Non-Member Function `synchronize`] - - #include - namespace boost - { - #if ! defined(BOOST_THREAD_NO_SYNCHRONIZE) - template - std::tuple::type ...> synchronize(SV& ...sv); - #endif - } - -[endsect] -[endsect] diff --git a/doc/once.qbk b/doc/once.qbk index d5a398b3..8b80cc61 100644 --- a/doc/once.qbk +++ b/doc/once.qbk @@ -21,6 +21,11 @@ } +[warning the variadic prototype is provided only on C++11 compilers supporting variadic templates, otherwise the interface is limited up to 3 parameters.] + +[warning the move semantics is ensured only on C++11 compilers supporting SFINAE expression, decltype N3276 and auto. Waiting for a boost::bind that is move aware.] + + `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`] @@ -71,6 +76,9 @@ proceed even though the call to `call_once` didn't actually call the function, in which case it could also avoid calling `call_once` recursively.]] + +[[Note:] [On some compilers this function has some restrictions, e.g. if variadic templates are not supported the number of arguments is limited to 3; .]] + ] void call_once(void (*func)(),once_flag& flag); diff --git a/doc/sync_queues_ref.qbk b/doc/sync_queues_ref.qbk new file mode 100644 index 00000000..eaaae932 --- /dev/null +++ b/doc/sync_queues_ref.qbk @@ -0,0 +1,414 @@ +[section:synchronized_queues Synchronized Queues -- EXPERIMENTAL] + +[warning These features are experimental and subject to change in future versions. There are not too much tests yet, so it is possible that you can find out some trivial bugs :(] + +[note These features are based on the [@http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3533.html [*N3533 - C++ Concurrent Queues]] C++1y proposal from Lawrence Crowl and Chris Mysen and [@http://www.manning.com/williams/ [*C++ Concurrency in Action]] from Anthony Williams.] + + +[section:tutorial Tutorial] + +Concurrent queues are a well know mechanism for communicating data between different threads. + +Concurrent queues have inherently copy/move semantics for the data handling operation. Reference-returning interfaces are forbidden as multiple access to these references can not be thread-safe. + +[endsect] + +[section:ref Reference] + +[section:sync_queue_req Synchronized Queue Model] + +The BasicConcurrentQueue concept models . +A type `Q` meets the BasicConcurrentQueue requirements if the following expressions are well-formed and have the specified semantics + +* `q` denotes a value of type `Q`, +* `e` denotes a value of type Q::value_type, +* `u` denotes a value of type Q::size_type, +* `lve` denotes a lvalue referece of type Q::value_type, +* `rve` denotes a rvalue referece of type Q::value_type: +* `spe` denotes a shared_ptr + +Basic expressions + +* Q::value_type +* Q::size_type +* `q.push(e);` +* `q.push(rve);` +* `q.pull(lre);` +* `lre = q.pull();` +* `spe = q.ptr_pull();` +* `b = q.empty();` +* `u = q.size();` + +Non-waiting operations + +* `b = q.try_push(e);` +* `b = q.try_push(rve);` +* `b = q.try_pull(lre);` + +Non-blocking operations are provided by BlockingQueues + +* `b = q.try_push(nb, e);` +* `b = q.try_push(nb, rve);` +* `b = q.try_pull(nb, lre);` + +Bounded queues add the following valid expressions + +* `Q q(u);` +* `b = q.full();` +* `u = q.capacity();` + +Closed queues + +Threads using a queue for communication need some mechanism to signal when the queue is no longer needed. The usual approach is add an additional out-of-band signal. However, this approach suffers from the flaw that threads waiting on either full or empty queues need to be woken up when the queue is no longer needed. Rather than require an out-of-band signal, we chose to directly support such a signal in the queue itself, which considerably simplifies coding. + +To achieve this signal, a thread may close a queue. Once closed, no new elements may be pushed onto the queue. Push operations on a closed queue will either return queue_op_status::closed (when they have a queue_op_status return type), set the closed parameter if it has one or throw sync_queue::closed (when they do not). Elements already on the queue may be popped off. When a queue is empty and closed, pop operations will either return queue_op_status::closed (when they have a status return) or throw queue_op_status::closed (when they do not). + +* `q.close();` +* `b = q.closed();` + +Basic expressions + +* Q::value_type +* `q.push(e,c);` +* `q.push(rve,c);` +* `q.pull(lre,c);` +* `spe = q.ptr_pull(c);` + +Non-waiting operations + +* `b = q.try_push(e, c);` +* `b = q.try_push(rve, c);` +* `b = q.try_pull(lre, c);` + +Non-blocking operations are provided by BlockingQueues + +* `b = q.try_push(nb, e, c);` +* `b = q.try_push(nb, rve, c);` +* `b = q.try_pull(nb, lre, c);` + + +[section:exception Blocking Queues Throw specification] + +All the functions that can throw are defined as if we had in addition to its specific Throw specification the following: + +[variablelist +[[Throws:] [Any exception thrown by the internal locking.]] +] + +[endsect] + + +[section:exception2 Closed Queues Throw specification] + +All the modifier functions that have no a specific closed parameter can throw are defined as if we had in addition to its specific Throw specification the following: + +[variablelist +[[Throws:] [Any exception thrown by the internal locking.]] +] + +[endsect] + + +[section:push `q.push(e);`] + +[variablelist + +[[Effects:] [Waits until the queue is not full (for bounded queues) and then push the `e` onto the queue copying it.]] + +[[Synchronization:] [Prior pull-like operations on the same object synchronizes with this operation.]] + +[[Postcondition:] [`! q.empty()`.]] + +[[Return type:] [`void`.]] + +[[Throws:] [If the queue was closed, throws sync_queue_is_closed. Any exception thrown by the the copy of `e`.]] + +[[Exception safety:] [If an exception is thrown then the queue state is unmodified.]] + +] + +[endsect] +[section:push_m `q.push(rve);`] + +[variablelist + +[[Effects:] [Waits until the queue is not full (for bounded queues) and then push the `e` onto the queue moving it.]] + +[[Synchronization:] [Prior pull-like operations on the same object synchronizes with this operation.]] + +[[Postcondition:] [`! q.empty()`.]] + +[[Return type:] [`void`.]] + +[[Throws:] [If the queue is closed, throws sync_queue_is_closed. Any exception thrown by the the copy of `e`.]] + +[[Exception safety:] [If an exception is thrown then the queue state is unmodified.]] + +] + +[endsect] + +[section:try_push `q.try_push(e);`] + +[variablelist + +[[Effects:] [If the queue `q` is not full, push the `e` onto the queue copying it.]] + +[[Synchronization:] [Prior pull-like operations on the same object synchronizes with this operation when the operation succeeds. ]] + +[[Return type:] [`bool`.]] + +[[Return:] [If the queue `q` is full return `false`, otherwise return `true`;]] + +[[Postcondition:] [If the call returns `true`, `! q.empty()`.]] + +[[Throws:] [If the queue is closed, throws sync_queue_is_closed. Any exception thrown by the the copy of `e`.]] + +[[Exception safety:] [If an exception is thrown then the queue state is unmodified.]] + +] + +[endsect] +[section:try_push_m `q.try_push(rve());`] + +[variablelist + +[[Effects:] [If the queue `q` is not full, push the `e` onto the queue moving it.]] + +[[Synchronization:] [Prior pull-like operations on the same object synchronizes with this operation.]] + +[[Return type:] [`bool`.]] + +[[Return:] [If the queue `q` is full return `false`, otherwise return `true`;]] + +[[Postcondition:] [If the call returns `true`, `! q.empty()`.]] + +[[Throws:] [If the queue is closed, throws sync_queue_is_closed. Any exception thrown by the the copy of `e`.]] + +[[Exception safety:] [If an exception is thrown then the queue state is unmodified.]] + +] + +[endsect] +[section:pull `e = q.pull()`] + +[variablelist + +[[Requires:] [Q::value_type is no throw copy movable. This is needed to ensure the exception safety]] + +[[Effects:] [Waits until the queue is not empty and then pull the element from the queue `q` and moves the pulled element.]] + +[[Synchronization:] [Prior pull-like operations on the same object synchronizes with this operation.]] + +[[Postcondition:] [`! q.full()`.]] + +[[Return type:] [`Q::value_type`.]] + +[[Return:] [The pulled element.]] + +[[Throws:] [Any exception thrown by the copy of `e`.]] + +[[Exception safety:] [If an exception is thrown then the queue state is unmodified.]] + +] + +[endsect] + + +[endsect] + +[section:sync_bounded_queue_ref Synchronized Bounded Queue] + + #include + + namespace boost + { + struct sync_queue_is_closed : std::exception {}; + + template + class sync_bounded_queue; + + // Stream-like operators + template + sync_bounded_queue& operator<<(sync_bounded_queue& sbq, ValueType&& elem); + template + sync_bounded_queue& operator<<(sync_bounded_queue& sbq, ValueType const&elem); + template + sync_bounded_queue& operator>>(sync_bounded_queue& sbq, ValueType &elem); + } + +[section:sync_queue_is_closed Class `sync_queue_is_closed`] + + #include + + namespace boost + { + struct sync_queue_is_closed : std::exception {}; + } + +[endsect] + +[section:sync_bounded_queue Class `sync_bounded_queue<>`] + + #include + namespace boost + { + template + class sync_bounded_queue + { + public: + typedef ValueType value_type; + typedef std::size_t size_type; + + sync_bounded_queue(sync_bounded_queue const&) = delete; + sync_bounded_queue& operator=(sync_bounded_queue const&) = delete; + explicit sync_bounded_queue(size_type max_elems); + template + sync_bounded_queue(size_type max_elems, Range range); + ~sync_bounded_queue(); + + // Observers + bool empty() const; + bool full() const; + size_type capacity() const; + size_type size() const; + bool closed() const; + + // Modifiers + void push(const value_type& x); + void push(value_type&& x); + bool try_push(const value_type& x); + bool try_push(value_type&& x); + bool try_push(no_block_tag, const value_type& x); + bool try_push(no_block_tag, value_type&& x); + + void pull(value_type&); + // enable_if is_nothrow_movable + value_type pull(); + shared_ptr ptr_pull(); + bool try_pull(value_type&); + bool try_pull(no_block_tag,value_type&); + shared_ptr try_pull(); + + void close(); + }; + } + +[endsect] + +[section:stream_out_operators Non-Member Function `operator<<()`] + + #include + namespace boost + { + template + sync_bounded_queue& operator<<(sync_bounded_queue& sbq, ValueType&& elem); + template + sync_bounded_queue& operator<<(sync_bounded_queue& sbq, ValueType const&elem); + } + +[endsect] +[section:stream_in_operators Non-Member Function `operator>>()`] + + #include + namespace boost + { + template + sync_bounded_queue& operator>>(sync_bounded_queue& sbq, ValueType &elem); + } + +[endsect] +[endsect] + +[section:sync_queue_ref Synchronized Unbounded Queue] + + #include + namespace boost + { + template + class sync_queue; + + // Stream-like operators + template + sync_queue& operator<<(sync_queue& sbq, ValueType&& elem); + template + sync_queue& operator<<(sync_queue& sbq, ValueType const&elem); + template + sync_queue& operator>>(sync_queue& sbq, ValueType &elem); + } + +[section:sync_queue Class `sync_queue<>`] + + #include + + namespace boost + { + template + class sync_queue + { + public: + typedef ValueType value_type; + typedef std::size_t size_type; + + sync_queue(sync_queue const&) = delete; + sync_queue& operator=(sync_queue const&) = delete; + sync_queue(); + explicit template + sync_queue(Range range); + ~sync_queue(); + + // Observers + bool empty() const; + bool full() const; + size_type size() const; + bool closed() const; + + // Modifiers + void push(const value_type& x); + void push(value_type&& x); + bool try_push(const value_type& x); + bool try_push(value_type&&) x); + bool try_push(no_block_tag, const value_type& x); + bool try_push(no_block_tag, value_type&& x); + + void pull(value_type&); + // enable_if is_nothrow_movable + value_type pull(); + shared_ptr ptr_pull(); + bool try_pull(value_type&); + bool try_pull(no_block_tag,value_type&); + shared_ptr try_pull(); + + void close(); + }; + } + +[endsect] + +[section:stream_out_operators Non-Member Function `operator<<()`] + + #include + namespace boost + { + template + sync_queue& operator<<(sync_queue& sbq, ValueType&& elem); + template + sync_queue& operator<<(sync_queue& sbq, ValueType const&elem); + } + +[endsect] +[section:stream_in_operators Non-Member Function `operator>>()`] + + #include + namespace boost + { + template + sync_queue& operator>>(sync_queue& sbq, ValueType &elem); + } + +[endsect] + +[endsect] + +[endsect] +[endsect] diff --git a/doc/sync_streams.qbk b/doc/sync_streams.qbk new file mode 100644 index 00000000..d7ee42cd --- /dev/null +++ b/doc/sync_streams.qbk @@ -0,0 +1,109 @@ +[/ + / Copyright (c) 2013 Vicente J. Botet Escriba + / + / 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:ext_locked_streams Externally Locked Streams - EXPERIMENTAL] + +[warning These features are experimental and subject to change in future versions. There are not too much tests yet, so it is possible that you can find out some trivial bugs :(] + +[note These features are based on the [@http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3535.html [*N3535 - C++ Streams Mutex]] C++1y proposal, even if the library proposes al alternative interface.] + +[section:tutorial Tutorial] + +[endsect] [/tutorial] + +[section:ref Reference] + + #include + namespace boost + { + template + class externally_locked_stream; + template + class stream_guard; + template + struct is_strict_lock_sur_parolle > : true_type {}; + + // Stream-like operators + template + const stream_guard& operator<<(const stream_guard& lck, T arg); + template + const stream_guard& + operator<<(const stream_guard& lck, Stream& (*arg)(Stream&)); + template + const stream_guard& + operator>>(const stream_guard& lck, T& arg); + template + stream_guard + operator<<(externally_locked_stream& mtx, T arg); + template + stream_guard + operator<<(externally_locked_stream& mtx, Stream& (*arg)(Stream&)); + template + stream_guard + operator>>(externally_locked_stream& mtx, T& arg); + } + + +[section:stream_guard Class `stream_guard`] + + #include + namespace boost + { + template + class stream_guard + { + public: + typedef typename externally_locked_stream::mutex_type mutex_type; + + // Constructors, Assignment and Destructors + stream_guard(stream_guard const&) = delete; + stream_guard& operator=(stream_guard const&) = delete; + stream_guard(externally_locked_stream& mtx); + stream_guard(externally_locked_stream& mtx, adopt_lock_t); + stream_guard(BOOST_THREAD_RV_REF(stream_guard) rhs); + ~stream_guard(); + + // Observers + bool owns_lock(mutex_type const* l) const BOOST_NOEXCEPT; + Stream& get() const; + }; + } + +[endsect] +[section:externally_locked_stream Class `externally_locked_stream `] + + #include + namespace boost + { + template + class externally_locked_stream: public externally_locked + { + public: + // Constructors, Assignment and Destructors + externally_locked_stream(externally_locked_stream const&) = delete; + externally_locked_stream& operator=(externally_locked_stream const&) = delete; + // Effects: Constructs an externally locked object storing the cloaked reference object. + externally_locked_stream(Stream& stream, RecursiveMutex& mtx); + + // Observers + stream_guard hold(); + Stream& hold(strict_lock& lk); + }; + } + +`externally_locked_stream` cloaks a reference to an stream of type `Stream`, and actually +provides full access to that object through the `get` member functions, provided you +pass a reference to a strict lock object. + + +[endsect] + + +[endsect] [/ref] + +[endsect] [/Externally Locked Streams] diff --git a/doc/sync_tutorial.qbk b/doc/sync_tutorial.qbk index 52e0fe2c..15e4044e 100644 --- a/doc/sync_tutorial.qbk +++ b/doc/sync_tutorial.qbk @@ -18,8 +18,6 @@ In addition to the C++11 standard locks, Boost.Thread provides other locks and s [include external_locking.qbk] -[include synchronized_value.qbk] - [section:with Executing Around a Function] In particular, the library provides some lock factories. diff --git a/doc/synchronized_value.qbk b/doc/synchronized_value.qbk index b77bfc42..29d4110f 100644 --- a/doc/synchronized_value.qbk +++ b/doc/synchronized_value.qbk @@ -6,7 +6,13 @@ /] -[section Synchronized values] +[section:synchronized_valuesxxx Synchronized values - EXPERIMENTAL] + +[warning These features are experimental and subject to change in future versions. There are not too much tests yet, so it is possible that you can find out some trivial bugs :(] + +[section:tutorial Tutorial] + + [note This tutorial is an adaptation of the paper of Anthony Williams "Enforcing Correct Mutex Usage with Synchronized Values" to the Boost library.] [section The Problem with Mutexes] @@ -133,8 +139,9 @@ synchronized_value has value semantics even if the syntax lets is close to a poi [endsect] [/Value semantics] -[endsect] [/Synchronized variables] - +[endsect] [/tutorial] +[include synchronized_value_ref.qbk] +[endsect] [/Synchronized values] diff --git a/doc/synchronized_value_ref.qbk b/doc/synchronized_value_ref.qbk new file mode 100644 index 00000000..5dc235a6 --- /dev/null +++ b/doc/synchronized_value_ref.qbk @@ -0,0 +1,408 @@ +[section:synchronized_value_ref Reference ] + + + #include + namespace boost + { + + template + class synchronized_value; + + // Specialized swap algorithm + template + void swap(synchronized_value & lhs, synchronized_value & rhs); + template + void swap(synchronized_value & lhs, T & rhs); + template + void swap(T & lhs, synchronized_value & rhs); + + // Hash support + template + struct hash >; + + // Comparison + template + bool operator==(synchronized_value const&lhs, synchronized_value const& rhs) + template + bool operator!=(synchronized_value const&lhs, synchronized_value const& rhs) + template + bool operator<(synchronized_value const&lhs, synchronized_value const& rhs) + template + bool operator<=(synchronized_value const&lhs, synchronized_value const& rhs) + template + bool operator>(synchronized_value const&lhs, synchronized_value const& rhs) + template + bool operator>=(synchronized_value const&lhs, synchronized_value const& rhs) + + // Comparison with T + template + bool operator==(T const& lhs, synchronized_value const&rhs); + template + bool operator!=(T const& lhs, synchronized_value const&rhs); + template + bool operator<(T const& lhs, synchronized_value const&rhs); + template + bool operator<=(T const& lhs, synchronized_value const&rhs); + template + bool operator>(T const& lhs, synchronized_value const&rhs); + template + bool operator>=(T const& lhs, synchronized_value const&rhs); + + template + bool operator==(synchronized_value const& lhs, T const& rhs); + template + bool operator!=(synchronized_value const& lhs, T const& rhs); + template + bool operator<(synchronized_value const& lhs, T const& rhs); + template + bool operator<=(synchronized_value const& lhs, T const& rhs); + template + bool operator>(synchronized_value const& lhs, T const& rhs); + template + bool operator>=(synchronized_value const& lhs, T const& rhs); + + #if ! defined(BOOST_THREAD_NO_SYNCHRONIZE) + template + std::tuple::type ...> synchronize(SV& ...sv); + #endif + } + +[section:synchronized_value Class `synchronized_value`] + + #include + + namespace boost + { + + template + class synchronized_value + { + public: + typedef T value_type; + typedef Lockable mutex_type; + + synchronized_value() noexept(is_nothrow_default_constructible::value); + synchronized_value(T const& other) noexept(is_nothrow_copy_constructible::value); + synchronized_value(T&& other) noexept(is_nothrow_move_constructible::value); + synchronized_value(synchronized_value const& rhs); + synchronized_value(synchronized_value&& other); + + // mutation + synchronized_value& operator=(synchronized_value const& rhs); + synchronized_value& operator=(value_type const& val); + void swap(synchronized_value & rhs); + void swap(value_type & rhs); + + //observers + T get() const; + #if ! defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) + explicit operator T() const; + #endif + + strict_lock_ptr operator->(); + const_strict_lock_ptr operator->() const; + strict_lock_ptr synchronize(); + const_strict_lock_ptr synchronize() const; + + deref_value operator*();; + const_deref_value operator*() const; + + private: + T value_; // for exposition only + mutable mutex_type mtx_; // for exposition only + }; + } + +[variablelist + +[[Requires:] [`Lockable` is `Lockable`.]] + +] + + +[section:constructor `synchronized_value()`] + + synchronized_value() noexept(is_nothrow_default_constructible::value); + +[variablelist + +[[Requires:] [`T` is `DefaultConstructible`.]] +[[Effects:] [Default constructs the cloaked value_type]] + +[[Throws:] [Any exception thrown by `value_type()`.]] + +] + +[endsect] + + +[section:constructor_vt `synchronized_value(T const&)`] + + synchronized_value(T const& other) noexept(is_nothrow_copy_constructible::value); + +[variablelist + +[[Requires:] [`T` is `CopyConstructible`.]] +[[Effects:] [Copy constructs the cloaked value_type using the parameter `other`]] + +[[Throws:] [Any exception thrown by `value_type(other)`.]] + +] + +[endsect] + +[section:copy_cons `synchronized_value(synchronized_value const&)`] + + synchronized_value(synchronized_value const& rhs); + +[variablelist + +[[Requires:] [`T` is `DefaultConstructible` and `Assignable`.]] +[[Effects:] [Assigns the value on a scope protected by the mutex of the rhs. The mutex is not copied.]] + +[[Throws:] [Any exception thrown by `value_type()` or `value_type& operator=(value_type&)` or `mtx_.lock()`.]] + +] + +[endsect] + +[section:move_vt `synchronized_value(T&&)`] + + synchronized_value(T&& other) noexept(is_nothrow_move_constructible::value); + +[variablelist + +[[Requires:] [`T` is `CopyMovable `.]] +[[Effects:] [Move constructs the cloaked value_type]] + +[[Throws:] [Any exception thrown by `value_type(value_type&&)`.]] + +] + +[endsect] + +[section:move `synchronized_value(synchronized_value&&)`] + + synchronized_value(synchronized_value&& other); + +[variablelist + +[[Requires:] [`T` is `CopyMovable `.]] +[[Effects:] [Move constructs the cloaked value_type]] + +[[Throws:] [Any exception thrown by `value_type(value_type&&)` or `mtx_.lock()`.]] + +] + +[endsect] + +[section:assign `operator=(synchronized_value const&)`] + + synchronized_value& operator=(synchronized_value const& rhs); + +[variablelist + +[[Requires:] [`T` is `Assignale`.]] +[[Effects:] [Copies the underlying value on a scope protected by the two mutexes. The mutex is not copied. The locks are acquired avoiding deadlock. For example, there is no problem if one thread assigns `a = b` and the other assigns `b = a`.]] +[[Return:] [`*this`]] + +[[Throws:] [Any exception thrown by `value_type& operator(value_type const&)` or `mtx_.lock()`.]] + +] + +[endsect] +[section:assign_vt `operator=(T const&)`] + + synchronized_value& operator=(value_type const& val); + +[variablelist + +[[Requires:] [`T` is `Assignale`.]] +[[Effects:] [Copies the value on a scope protected by the mutex.]] +[[Return:] [`*this`]] + +[[Throws:] [Any exception thrown by `value_type& operator(value_type const&)` or `mtx_.lock()`.]] + +] + +[endsect] + +[section:get `get() const`] + + T get() const; + +[variablelist + +[[Requires:] [`T` is `CopyConstructible`.]] +[[Return:] [`A copy of the protected value obtained on a scope protected by the mutex.`]] + +[[Throws:] [Any exception thrown by `value_type(value_type const&)` or `mtx_.lock()`.]] + +] + +[endsect] + + +[section:T `operator T() const`] + + #if ! defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) + explicit operator T() const; + #endif + +[variablelist + +[[Requires:] [`T` is `CopyConstructible`.]] +[[Return:] [`A copy of the protected value obtained on a scope protected by the mutex.`]] + +[[Throws:] [Any exception thrown by `value_type(value_type const&)` or `mtx_.lock()`.]] + +] + +[endsect] + +[section:swap `swap(synchronized_value&)`] + + void swap(synchronized_value & rhs); + +[variablelist + +[[Requires:] [`T` is `Assignale`.]] +[[Effects:] [Swaps the data on a scope protected by both mutex. Both mutex are acquired to avoid dead-lock. The mutexes are not swapped.]] + +[[Throws:] [Any exception thrown by `swap(value_, rhs.value)` or `mtx_.lock()` or `rhs_.mtx_.lock()`.]] + +] + +[endsect] + +[section:swap_vt `swap(synchronized_value&)`] + + void swap(value_type & rhs); + +[variablelist + +[[Requires:] [`T` is `Swapable`.]] +[[Effects:] [Swaps the data on a scope protected by both mutex. Both mutex are acquired to avoid dead-lock. The mutexes are not swapped.]] + +[[Throws:] [Any exception thrown by `swap(value_, rhs)` or `mtx_.lock()`.]] + +] + +[endsect] +[section:indir `operator->()`] + + strict_lock_ptr operator->(); + + +Essentially calling a method `obj->foo(x, y, z)` calls the method `foo(x, y, z)` inside a critical section as long-lived as the call itself. + +[variablelist + +[[Return:] [`A strict_lock_ptr<>.`]] + +[[Throws:] [Nothing.]] + +] + +[endsect] +[section:indir_const `operator->() const`] + + const_strict_lock_ptr operator->() const; + + +If the `synchronized_value` object involved is const-qualified, then you'll only be able to call const methods +through `operator->`. So, for example, `vec->push_back("xyz")` won't work if `vec` were const-qualified. +The locking mechanism capitalizes on the assumption that const methods don't modify their underlying data. + +[variablelist + +[[Return:] [`A const_strict_lock_ptr <>.`]] + +[[Throws:] [Nothing.]] + +] + +[endsect] +[section:synchronize `synchronize()`] + + strict_lock_ptr synchronize(); + +The synchronize() factory make easier to lock on a scope. As discussed, `operator->` can only lock over the duration of a call, so it is insufficient for complex operations. With `synchronize()` you get to lock the object in a scoped and to directly access the object inside that scope. + +[*Example:] + + void fun(synchronized_value> & vec) { + auto vec2=vec.synchronize(); + vec2.push_back(42); + assert(vec2.back() == 42); + } + +[variablelist + +[[Return:] [`A strict_lock_ptr <>.`]] + +[[Throws:] [Nothing.]] + +] + +[endsect] +[section:synchronize_const `synchronize() const`] + + const_strict_lock_ptr synchronize() const; + +[variablelist + +[[Return:] [`A const_strict_lock_ptr <>.`]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:deref `operator*()`] + + deref_value operator*();; + +[variablelist + +[[Return:] [`A an instance of a class that locks the mutex on construction and unlocks it on destruction and provides implicit conversion to a reference to the protected value.`]] + +[[Throws:] [Nothing.]] + +] + +[endsect] +[section:deref_const `operator*() const`] + + const_deref_value operator*() const; + + +[variablelist + +[[Return:] [`A an instance of a class that locks the mutex on construction and unlocks it on destruction and provides implicit conversion to a constant reference to the protected value.`]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + + + + +[endsect] +[section:synchronize Non-Member Function `synchronize`] + + #include + namespace boost + { + #if ! defined(BOOST_THREAD_NO_SYNCHRONIZE) + template + std::tuple::type ...> synchronize(SV& ...sv); + #endif + } + +[endsect] +[endsect] + diff --git a/doc/thread.qbk b/doc/thread.qbk index 4389cfbf..54f71f82 100644 --- a/doc/thread.qbk +++ b/doc/thread.qbk @@ -8,10 +8,10 @@ [library Thread [quickbook 1.5] - [version 4.0.0] + [version 4.1.0] [authors [Williams, Anthony] [Botet Escriba, Vicente J.]] [copyright 2007-11 Anthony Williams] - [copyright 2011-12 Vicente J. Botet Escriba] + [copyright 2011-13 Vicente J. Botet Escriba] [purpose C++ Library for launching threads and synchronizing data between them] [category text] [license @@ -235,15 +235,21 @@ [include sync_tutorial.qbk] [include mutex_concepts.qbk] [include mutexes.qbk] -[/include synchronized_value_ref.qbk] [include condition_variables.qbk] [include once.qbk] [include barrier.qbk] [include futures.qbk] [endsect] + [include tss.qbk] +[section:sds Synchronized Data Structures] +[include synchronized_value.qbk] +[/include sync_queues_ref.qbk] +[/include sync_streams.qbk] +[endsect] + [include time.qbk] [include emulations.qbk] diff --git a/example/condition.cpp b/example/condition.cpp index 2cde1c02..d7665ac3 100644 --- a/example/condition.cpp +++ b/example/condition.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include "../test/remove_error_code_unused_warning.hpp" class bounded_buffer : private boost::noncopyable diff --git a/example/future_then.cpp b/example/future_then.cpp index 7590f382..4013ac2c 100644 --- a/example/future_then.cpp +++ b/example/future_then.cpp @@ -4,9 +4,9 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) //#define BOOST_THREAD_VERSION 4 -//#define BOOST_THREAD_USES_LOG +#define BOOST_THREAD_USES_LOG #define BOOST_THREAD_USES_LOG_THREAD_ID -//#define BOOST_THREAD_DONT_PROVIDE_FUTURE_INVALID_AFTER_GET +#define BOOST_THREAD_DONT_PROVIDE_FUTURE_INVALID_AFTER_GET #include #include diff --git a/example/monitor.cpp b/example/monitor.cpp index d8855d27..5c9a7421 100644 --- a/example/monitor.cpp +++ b/example/monitor.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include namespace { const int ITERS = 100; diff --git a/example/not_interleaved.cpp b/example/not_interleaved.cpp index 321b50d4..4efded38 100644 --- a/example/not_interleaved.cpp +++ b/example/not_interleaved.cpp @@ -15,7 +15,7 @@ void use_cerr(boost::externally_locked_stream &mcerr) { using namespace boost; - auto tf = chrono::steady_clock::now() + chrono::seconds(10); + chrono::steady_clock::time_point tf = chrono::steady_clock::now() + chrono::seconds(10); while (chrono::steady_clock::now() < tf) { mcerr << "logging data to cerr\n"; @@ -26,7 +26,7 @@ void use_cerr(boost::externally_locked_stream &mcerr) void use_cout(boost::externally_locked_stream &mcout) { using namespace boost; - auto tf = chrono::steady_clock::now() + chrono::seconds(5); + chrono::steady_clock::time_point tf = chrono::steady_clock::now() + chrono::seconds(5); while (chrono::steady_clock::now() < tf) { mcout << "logging data to cout\n"; @@ -50,14 +50,14 @@ int main() std::string nm; { strict_lock lk(terminal_mutex); - auto& gcout = mcout.hold(lk); - auto& gcin = mcin.hold(lk); + std::ostream & gcout = mcout.hold(lk); + //std::istream & gcin = mcin.hold(lk); gcout << "Enter name: "; //gcin >> nm; } t1.join(); t2.join(); mcout << nm << '\n'; - return 1; + return 0; } diff --git a/example/perf_shared_mutex.cpp b/example/perf_shared_mutex.cpp index 4450e4a7..7d8b979f 100644 --- a/example/perf_shared_mutex.cpp +++ b/example/perf_shared_mutex.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include diff --git a/example/producer_consumer.cpp b/example/producer_consumer.cpp new file mode 100644 index 00000000..7b7df149 --- /dev/null +++ b/example/producer_consumer.cpp @@ -0,0 +1,127 @@ +// (C) Copyright 2012 Howard Hinnant +// (C) Copyright 2012 Vicente Botet +// +// 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) + +// adapted from the example given by Howard Hinnant in + +#define BOOST_THREAD_VERSION 4 + +#include +#include +#include +#include + +void producer(boost::externally_locked_stream &mos, boost::sync_queue & sbq) +{ + using namespace boost; + try { + for(int i=0; ;++i) + { + //sbq.push(i); + sbq << i; + mos << "push(" << i << ") "<< sbq.size()<<"\n"; + this_thread::sleep_for(chrono::milliseconds(200)); + } + } + catch(sync_queue_is_closed&) + { + mos << "closed !!!\n"; + } + catch(...) + { + mos << "exception !!!\n"; + } +} + +void consumer(boost::externally_locked_stream &mos, boost::sync_queue & sbq) +{ + using namespace boost; + try { + for(int i=0; ;++i) + { + int r; + //sbq.pull(r); + sbq >> r; + mos << i << " pull(" << r << ") "<< sbq.size()<<"\n"; + this_thread::sleep_for(chrono::milliseconds(250)); + } + } + catch(sync_queue_is_closed&) + { + mos << "closed !!!\n"; + } + catch(...) + { + mos << "exception !!!\n"; + } +} +void consumer2(boost::externally_locked_stream &mos, boost::sync_queue & sbq) +{ + using namespace boost; + try { + bool closed=false; + for(int i=0; ;++i) + { + int r; + sbq.pull(r, closed); + if (closed) break; + mos << i << " pull(" << r << ")\n"; + this_thread::sleep_for(chrono::milliseconds(250)); + } + } + catch(...) + { + mos << "exception !!!\n"; + } +} +//void consumer3(boost::externally_locked_stream &mos, boost::sync_queue & sbq) +//{ +// using namespace boost; +// bool closed=false; +// try { +// for(int i=0; ;++i) +// { +// int r; +// queue_op_status res = sbq.wait_and_pull(r); +// if (res==queue_op_status::closed) break; +// mos << i << " wait_and_pull(" << r << ")\n"; +// this_thread::sleep_for(chrono::milliseconds(250)); +// } +// } +// catch(...) +// { +// mos << "exception !!!\n"; +// } +//} + +int main() +{ + using namespace boost; + + recursive_mutex terminal_mutex; + + externally_locked_stream mcerr(std::cerr, terminal_mutex); + externally_locked_stream mcout(std::cout, terminal_mutex); + externally_locked_stream mcin(std::cin, terminal_mutex); + + sync_queue sbq; + + { + mcout << "begin of main" << std::endl; + scoped_thread<> t11(thread(producer, boost::ref(mcerr), boost::ref(sbq))); + scoped_thread<> t12(thread(producer, boost::ref(mcerr), boost::ref(sbq))); + scoped_thread<> t2(thread(consumer, boost::ref(mcout), boost::ref(sbq))); + + this_thread::sleep_for(chrono::seconds(1)); + + mcout << "closed()" << std::endl; + sbq.close(); + mcout << "closed()" << std::endl; + + } // all threads joined here. + mcout << "end of main" << std::endl; + return 0; +} + diff --git a/example/producer_consumer_bounded.cpp b/example/producer_consumer_bounded.cpp new file mode 100644 index 00000000..c11965d8 --- /dev/null +++ b/example/producer_consumer_bounded.cpp @@ -0,0 +1,126 @@ +// (C) Copyright 2012 Howard Hinnant +// (C) Copyright 2012 Vicente Botet +// +// 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) + +// adapted from the example given by Howard Hinnant in + +#define BOOST_THREAD_VERSION 4 + +#include +#include +#include +#include + +void producer(boost::externally_locked_stream &mos, boost::sync_bounded_queue & sbq) +{ + using namespace boost; + try { + for(int i=0; ;++i) + { + //sbq.push(i); + sbq << i; + mos << "push(" << i << ") "<< sbq.size()<<"\n"; + this_thread::sleep_for(chrono::milliseconds(200)); + } + } + catch(sync_queue_is_closed&) + { + mos << "closed !!!\n"; + } + catch(...) + { + mos << "exception !!!\n"; + } +} + +void consumer(boost::externally_locked_stream &mos, boost::sync_bounded_queue & sbq) +{ + using namespace boost; + try { + for(int i=0; ;++i) + { + int r; + //sbq.pull(r); + sbq >> r; + mos << i << " pull(" << r << ") "<< sbq.size()<<"\n"; + this_thread::sleep_for(chrono::milliseconds(250)); + } + } + catch(sync_queue_is_closed&) + { + mos << "closed !!!\n"; + } + catch(...) + { + mos << "exception !!!\n"; + } +} +void consumer2(boost::externally_locked_stream &mos, boost::sync_bounded_queue & sbq) +{ + using namespace boost; + try { + bool closed=false; + for(int i=0; ;++i) + { + int r; + sbq.pull(r, closed); + if (closed) break; + mos << i << " pull(" << r << ")\n"; + this_thread::sleep_for(chrono::milliseconds(250)); + } + } + catch(...) + { + mos << "exception !!!\n"; + } +} +//void consumer3(boost::externally_locked_stream &mos, boost::sync_bounded_queue & sbq) +//{ +// using namespace boost; +// bool closed=false; +// try { +// for(int i=0; ;++i) +// { +// int r; +// queue_op_status res = sbq.wait_and_pull(r); +// if (res==queue_op_status::closed) break; +// mos << i << " wait_and_pull(" << r << ")\n"; +// this_thread::sleep_for(chrono::milliseconds(250)); +// } +// } +// catch(...) +// { +// mos << "exception !!!\n"; +// } +//} + +int main() +{ + using namespace boost; + + recursive_mutex terminal_mutex; + + externally_locked_stream mcerr(std::cerr, terminal_mutex); + externally_locked_stream mcout(std::cout, terminal_mutex); + externally_locked_stream mcin(std::cin, terminal_mutex); + + sync_bounded_queue sbq(10); + + { + mcout << "begin of main" << std::endl; + scoped_thread<> t11(thread(producer, boost::ref(mcerr), boost::ref(sbq))); + scoped_thread<> t12(thread(producer, boost::ref(mcerr), boost::ref(sbq))); + scoped_thread<> t2(thread(consumer, boost::ref(mcout), boost::ref(sbq))); + + this_thread::sleep_for(chrono::seconds(1)); + + sbq.close(); + mcout << "closed()" << std::endl; + + } // all threads joined here. + mcout << "end of main" << std::endl; + return 0; +} + diff --git a/example/shared_monitor.cpp b/example/shared_monitor.cpp index e8378270..d1996588 100644 --- a/example/shared_monitor.cpp +++ b/example/shared_monitor.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #if defined BOOST_THREAD_DONT_USE_CHRONO #include #endif diff --git a/example/shared_mutex.cpp b/example/shared_mutex.cpp index 13246d96..c0ef7a52 100644 --- a/example/shared_mutex.cpp +++ b/example/shared_mutex.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #if defined BOOST_THREAD_USES_CHRONO diff --git a/example/starvephil.cpp b/example/starvephil.cpp index 37752980..467d0b3c 100644 --- a/example/starvephil.cpp +++ b/example/starvephil.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include #include diff --git a/example/tennis.cpp b/example/tennis.cpp index 93e5a46a..b67f7c59 100644 --- a/example/tennis.cpp +++ b/example/tennis.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include diff --git a/example/thread.cpp b/example/thread.cpp index 02dc4ac4..d6bc6f23 100644 --- a/example/thread.cpp +++ b/example/thread.cpp @@ -6,7 +6,7 @@ #define BOOST_THREAD_VERSION 2 -#include +#include #include #include diff --git a/example/thread_guard.cpp b/example/thread_guard.cpp index 0e83b6c8..b396802d 100644 --- a/example/thread_guard.cpp +++ b/example/thread_guard.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include void do_something(int& i) diff --git a/example/xtime.cpp b/example/xtime.cpp index 58d741ca..a873085b 100644 --- a/example/xtime.cpp +++ b/example/xtime.cpp @@ -6,7 +6,7 @@ #define BOOST_THREAD_VERSION 2 -#include +#include #include int main() diff --git a/include/boost/thread/detail/thread.hpp b/include/boost/thread/detail/thread.hpp index 57e6f368..e65d76b0 100644 --- a/include/boost/thread/detail/thread.hpp +++ b/include/boost/thread/detail/thread.hpp @@ -476,8 +476,13 @@ namespace boost { using namespace chrono; system_clock::time_point s_now = system_clock::now(); - typename Clock::time_point c_now = Clock::now(); - return try_join_until(s_now + ceil(t - c_now)); + bool joined= false; + do { + typename Clock::duration d = ceil(t-Clock::now()); + if (d <= Clock::duration::zero()) return false; // in case the Clock::time_point t is already reached + joined = try_join_until(s_now + d); + } while (! joined); + return true; } template bool try_join_until(const chrono::time_point& t) diff --git a/include/boost/thread/future.hpp b/include/boost/thread/future.hpp index ef9a74f1..93653da3 100644 --- a/include/boost/thread/future.hpp +++ b/include/boost/thread/future.hpp @@ -101,11 +101,6 @@ namespace boost { return ec_; } - const char* what() const BOOST_THREAD_NOEXCEPT_OR_THROW - { - return code().message().c_str(); - } - }; class BOOST_SYMBOL_VISIBLE future_uninitialized: @@ -262,6 +257,7 @@ namespace boost } void set_async() { + is_deferred_ = false; set_launch_policy(launch::async); } void set_launch_policy(launch policy) @@ -1380,6 +1376,7 @@ namespace boost } // detail BOOST_THREAD_DCL_MOVABLE_BEG(R) detail::basic_future BOOST_THREAD_DCL_MOVABLE_END +#if (!defined _MSC_VER || _MSC_VER >= 1400) // _MSC_VER == 1400 on MSVC 2005 namespace detail { template @@ -1390,6 +1387,7 @@ namespace boost BOOST_THREAD_FUTURE make_future_deferred_object(BOOST_THREAD_FWD_REF(Fp) f); } +#endif // #if (!defined _MSC_VER || _MSC_VER >= 1400) template class BOOST_THREAD_FUTURE : public detail::basic_future @@ -1430,7 +1428,7 @@ namespace boost BOOST_THREAD_MOVABLE_ONLY(BOOST_THREAD_FUTURE) typedef future_state::state state; - BOOST_THREAD_FUTURE() {} + BOOST_CONSTEXPR BOOST_THREAD_FUTURE() {} ~BOOST_THREAD_FUTURE() {} @@ -1522,7 +1520,7 @@ namespace boost typedef future_state::state state; - shared_future() + BOOST_CONSTEXPR shared_future() {} ~shared_future() @@ -3255,7 +3253,55 @@ namespace boost #endif //////////////////////////////// - // make_shared_future + // make_ready_future + //////////////////////////////// + template + BOOST_THREAD_FUTURE::type> make_ready_future(BOOST_THREAD_FWD_REF(T) value) + { + typedef typename decay::type future_type; + promise p; + p.set_value(boost::forward(value)); + return BOOST_THREAD_MAKE_RV_REF(p.get_future()); + } + +#if defined BOOST_THREAD_USES_MOVE + inline BOOST_THREAD_FUTURE make_ready_future() + { + promise p; + p.set_value(); + return BOOST_THREAD_MAKE_RV_REF(p.get_future()); + } +#endif + + //////////////////////////////// + // make_exceptional_future + //////////////////////////////// + template + BOOST_THREAD_FUTURE make_exceptional_future(exception_ptr ex) + { + promise p; + p.set_exception(ex); + return BOOST_THREAD_MAKE_RV_REF(p.get_future()); + } + +#if 0 + template + make_future(CLOSURE closure) -> BOOST_THREAD_FUTURE { + typedef decltype(closure() T; + promise p; + try + { + p.set_value(closure()); + } + catch(...) + { + p.set_exception(std::current_exception()); + } + return BOOST_THREAD_MAKE_RV_REF(p.get_future()); + } +#endif + //////////////////////////////// + // make_shared_future deprecated //////////////////////////////// template shared_future::type> make_shared_future(BOOST_THREAD_FWD_REF(T) value) @@ -3274,6 +3320,36 @@ namespace boost } + //////////////////////////////// + // make_ready_shared_future + //////////////////////////////// + template + shared_future::type> make_ready_shared_future(BOOST_THREAD_FWD_REF(T) value) + { + typedef typename decay::type future_type; + promise p; + p.set_value(boost::forward(value)); + return p.get_future().share(); + } + + + inline shared_future make_ready_shared_future() + { + promise p; + return BOOST_THREAD_MAKE_RV_REF(p.get_future().share()); + + } + + //////////////////////////////// + // make_exceptional_shared_future + //////////////////////////////// + template + shared_future make_exceptional_shared_future(exception_ptr ex) + { + promise p; + p.set_exception(ex); + return p.get_future().share(); + } //////////////////////////////// // detail::future_continuation //////////////////////////////// diff --git a/include/boost/thread/pthread/shared_mutex.hpp b/include/boost/thread/pthread/shared_mutex.hpp index 5e9c8fd6..458d6c83 100644 --- a/include/boost/thread/pthread/shared_mutex.hpp +++ b/include/boost/thread/pthread/shared_mutex.hpp @@ -20,6 +20,7 @@ #include #endif #include +#include #include @@ -28,8 +29,125 @@ namespace boost class shared_mutex { private: - struct state_data + class state_data { + public: + state_data () : + shared_count(0), + exclusive(false), + upgrade(false), + exclusive_waiting_blocked(false) + {} + + void assert_free() const + { + BOOST_ASSERT( ! exclusive ); + BOOST_ASSERT( ! upgrade ); + BOOST_ASSERT( shared_count==0 ); + } + + void assert_locked() const + { + BOOST_ASSERT( exclusive ); + BOOST_ASSERT( shared_count==0 ); + BOOST_ASSERT( ! upgrade ); + } + + void assert_lock_shared () const + { + BOOST_ASSERT( ! exclusive ); + BOOST_ASSERT( shared_count>0 ); + //BOOST_ASSERT( (! upgrade) || (shared_count>1)); + // if upgraded there are at least 2 threads sharing the mutex, + // except when unlock_upgrade_and_lock has decreased the number of readers but has not taken yet exclusive ownership. + } + + void assert_lock_upgraded () const + { + BOOST_ASSERT( ! exclusive ); + BOOST_ASSERT( upgrade ); + BOOST_ASSERT( shared_count>0 ); + } + + void assert_lock_not_upgraded () const + { + BOOST_ASSERT( ! upgrade ); + } + + bool can_lock () const + { + return ! (shared_count || exclusive); + } + + void exclusive_blocked (bool blocked) + { + exclusive_waiting_blocked = blocked; + } + + void lock () + { + exclusive = true; + } + + void unlock () + { + exclusive = false; + exclusive_waiting_blocked = false; + } + + bool can_lock_shared () const + { + return ! (exclusive || exclusive_waiting_blocked); + } + + bool more_shared () const + { + return shared_count > 0 ; + } + unsigned get_shared_count () const + { + return shared_count ; + } + unsigned lock_shared () + { + return ++shared_count; + } + + + void unlock_shared () + { + --shared_count; + } + + bool unlock_shared_downgrades() + { + if (upgrade) { + upgrade=false; + exclusive=true; + return true; + } else { + exclusive_waiting_blocked=false; + return false; + } + } + + void lock_upgrade () + { + ++shared_count; + upgrade=true; + } + bool can_lock_upgrade () const + { + return ! (exclusive || exclusive_waiting_blocked || upgrade); + } + + void unlock_upgrade () + { + upgrade=false; + --shared_count; + } + + //private: unsigned shared_count; bool exclusive; bool upgrade; @@ -51,12 +169,11 @@ namespace boost } public: + BOOST_THREAD_NO_COPYABLE(shared_mutex) shared_mutex() { - state_data state_={0,0,0,0}; - state=state_; } ~shared_mutex() @@ -69,27 +186,23 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; #endif boost::unique_lock lk(state_change); - - while(state.exclusive || state.exclusive_waiting_blocked) + while(!state.can_lock_shared()) { shared_cond.wait(lk); } - ++state.shared_count; + state.lock_shared(); } bool try_lock_shared() { boost::unique_lock lk(state_change); - if(state.exclusive || state.exclusive_waiting_blocked) + if(!state.can_lock_shared()) { return false; } - else - { - ++state.shared_count; - return true; - } + state.lock_shared(); + return true; } #if defined BOOST_THREAD_USES_DATETIME @@ -100,14 +213,14 @@ namespace boost #endif boost::unique_lock lk(state_change); - while(state.exclusive || state.exclusive_waiting_blocked) + while(!state.can_lock_shared()) { if(!shared_cond.timed_wait(lk,timeout)) { return false; } } - ++state.shared_count; + state.lock_shared(); return true; } @@ -131,33 +244,38 @@ namespace boost #endif boost::unique_lock lk(state_change); - while(state.exclusive || state.exclusive_waiting_blocked) + while(!state.can_lock_shared()) + //while(state.exclusive || state.exclusive_waiting_blocked) { if(cv_status::timeout==shared_cond.wait_until(lk,abs_time)) { return false; } } - ++state.shared_count; + state.lock_shared(); return true; } #endif void unlock_shared() { boost::unique_lock lk(state_change); - bool const last_reader=!--state.shared_count; - - if(last_reader) + state.assert_lock_shared(); + state.unlock_shared(); + if (! state.more_shared()) { - if(state.upgrade) + if (state.upgrade) { + // As there is a thread doing a unlock_upgrade_and_lock that is waiting for ! state.more_shared() + // avoid other threads to lock, lock_upgrade or lock_shared, so only this thread is notified. state.upgrade=false; state.exclusive=true; + lk.unlock(); upgrade_cond.notify_one(); } else { state.exclusive_waiting_blocked=false; + lk.unlock(); } release_waiters(); } @@ -170,7 +288,7 @@ namespace boost #endif boost::unique_lock lk(state_change); - while(state.shared_count || state.exclusive) + while (state.shared_count || state.exclusive) { state.exclusive_waiting_blocked=true; exclusive_cond.wait(lk); @@ -262,8 +380,10 @@ namespace boost void unlock() { boost::unique_lock lk(state_change); + state.assert_locked(); state.exclusive=false; state.exclusive_waiting_blocked=false; + state.assert_free(); release_waiters(); } @@ -277,7 +397,7 @@ namespace boost { shared_cond.wait(lk); } - ++state.shared_count; + state.lock_shared(); state.upgrade=true; } @@ -299,7 +419,7 @@ namespace boost break; } } - ++state.shared_count; + state.lock_shared(); state.upgrade=true; return true; } @@ -334,7 +454,7 @@ namespace boost break; } } - ++state.shared_count; + state.lock_shared(); state.upgrade=true; return true; } @@ -348,8 +468,9 @@ namespace boost } else { - ++state.shared_count; + state.lock_shared(); state.upgrade=true; + state.assert_lock_upgraded(); return true; } } @@ -357,15 +478,14 @@ namespace boost void unlock_upgrade() { boost::unique_lock lk(state_change); - state.upgrade=false; - bool const last_reader=!--state.shared_count; - - if(last_reader) + //state.upgrade=false; + state.unlock_upgrade(); + if(! state.more_shared() ) { state.exclusive_waiting_blocked=false; release_waiters(); } else { - shared_cond.notify_all(); + shared_cond.notify_all(); } } @@ -376,28 +496,33 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; #endif boost::unique_lock lk(state_change); - --state.shared_count; - while(state.shared_count) + state.assert_lock_upgraded(); + state.unlock_shared(); + while (state.more_shared()) { upgrade_cond.wait(lk); } state.upgrade=false; state.exclusive=true; + state.assert_locked(); } void unlock_and_lock_upgrade() { boost::unique_lock lk(state_change); + state.assert_locked(); state.exclusive=false; state.upgrade=true; - ++state.shared_count; + state.lock_shared(); state.exclusive_waiting_blocked=false; + state.assert_lock_upgraded(); release_waiters(); } bool try_unlock_upgrade_and_lock() { boost::unique_lock lk(state_change); + state.assert_lock_upgraded(); if( !state.exclusive && !state.exclusive_waiting_blocked && state.upgrade @@ -406,6 +531,7 @@ namespace boost state.shared_count=0; state.exclusive=true; state.upgrade=false; + state.assert_locked(); return true; } return false; @@ -428,6 +554,7 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; #endif boost::unique_lock lk(state_change); + state.assert_lock_upgraded(); if (state.shared_count != 1) { for (;;) @@ -451,8 +578,9 @@ namespace boost void unlock_and_lock_shared() { boost::unique_lock lk(state_change); + state.assert_locked(); state.exclusive=false; - ++state.shared_count; + state.lock_shared(); state.exclusive_waiting_blocked=false; release_waiters(); } @@ -461,6 +589,7 @@ namespace boost bool try_unlock_shared_and_lock() { boost::unique_lock lk(state_change); + state.assert_lock_shared(); if( !state.exclusive && !state.exclusive_waiting_blocked && !state.upgrade @@ -490,6 +619,7 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; #endif boost::unique_lock lk(state_change); + state.assert_lock_shared(); if (state.shared_count != 1) { for (;;) @@ -514,6 +644,7 @@ namespace boost void unlock_upgrade_and_lock_shared() { boost::unique_lock lk(state_change); + state.assert_lock_upgraded(); state.upgrade=false; state.exclusive_waiting_blocked=false; release_waiters(); @@ -523,6 +654,7 @@ namespace boost bool try_unlock_shared_and_lock_upgrade() { boost::unique_lock lk(state_change); + state.assert_lock_shared(); if( !state.exclusive && !state.exclusive_waiting_blocked && !state.upgrade @@ -551,6 +683,7 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; #endif boost::unique_lock lk(state_change); + state.assert_lock_shared(); if( state.exclusive || state.exclusive_waiting_blocked || state.upgrade diff --git a/include/boost/thread/scoped_thread.hpp b/include/boost/thread/scoped_thread.hpp index 370c1177..faf4039c 100644 --- a/include/boost/thread/scoped_thread.hpp +++ b/include/boost/thread/scoped_thread.hpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include diff --git a/include/boost/thread/sync_bounded_queue.hpp b/include/boost/thread/sync_bounded_queue.hpp new file mode 100644 index 00000000..ff1928fb --- /dev/null +++ b/include/boost/thread/sync_bounded_queue.hpp @@ -0,0 +1,594 @@ +#ifndef BOOST_THREAD_SYNC_BOUNDED_QUEUE_HPP +#define BOOST_THREAD_SYNC_BOUNDED_QUEUE_HPP + +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Vicente J. Botet Escriba 2013. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/thread for documentation. +// +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace boost +{ + + BOOST_SCOPED_ENUM_DECLARE_BEGIN(queue_op_status) + { success = 0, empty, full, closed, busy } + BOOST_SCOPED_ENUM_DECLARE_END(queue_op_status) + + struct no_block_tag{}; + BOOST_CONSTEXPR_OR_CONST no_block_tag no_block = {}; + + struct sync_queue_is_closed : std::exception {}; + + template + class sync_bounded_queue + { + public: + typedef ValueType value_type; + typedef std::size_t size_type; + + // Constructors/Assignment/Destructors + BOOST_THREAD_NO_COPYABLE(sync_bounded_queue) + explicit sync_bounded_queue(size_type max_elems); + template + sync_bounded_queue(size_type max_elems, Range range); + ~sync_bounded_queue(); + + // Observers + bool empty() const; + bool full() const; + size_type capacity() const; + size_type size() const; + bool closed() const; + + // Modifiers + void close(); + + void push(const value_type& x); + void push(BOOST_THREAD_RV_REF(value_type) x); + bool try_push(const value_type& x); + bool try_push(BOOST_THREAD_RV_REF(value_type) x); + bool try_push(no_block_tag, const value_type& x); + bool try_push(no_block_tag, BOOST_THREAD_RV_REF(value_type) x); + + // Observers/Modifiers + void pull(value_type&); + void pull(ValueType& elem, bool & closed); + // enable_if is_nothrow_copy_movable + value_type pull(); + shared_ptr ptr_pull(); + bool try_pull(value_type&); + bool try_pull(no_block_tag,value_type&); + shared_ptr try_pull(); + + private: + mutable mutex mtx_; + condition_variable not_empty_; + condition_variable not_full_; + size_type waiting_full_; + size_type waiting_empty_; + value_type* data_; + size_type in_; + size_type out_; + size_type capacity_; + bool closed_; + + size_type inc(size_type idx) const BOOST_NOEXCEPT + { + return (idx + 1) % capacity_; + } + + bool empty(unique_lock& ) const BOOST_NOEXCEPT + { + return in_ == out_; + } + bool empty(lock_guard& ) const BOOST_NOEXCEPT + { + return in_ == out_; + } + size_type capacity(lock_guard& ) const BOOST_NOEXCEPT + { + return capacity; + } + size_type size(lock_guard& ) const BOOST_NOEXCEPT + { + return ((out_+capacity_-in_) % capacity_)-1; + } + + void throw_if_closed(unique_lock&); + + bool try_pull(value_type& x, unique_lock& lk); + bool try_push(const value_type& x, unique_lock& lk); + bool try_push(BOOST_THREAD_RV_REF(value_type) x, unique_lock& lk); + shared_ptr try_pull(unique_lock& lk); + + void wait_until_not_empty(unique_lock& lk); + void wait_until_not_empty(unique_lock& lk, bool&); + size_type wait_until_not_full(unique_lock& lk); + size_type wait_until_not_full(unique_lock& lk, bool&); + + + void notify_not_empty_if_needed(unique_lock& lk) + { + if (waiting_empty_ > 0) + { + --waiting_empty_; + lk.unlock(); + not_empty_.notify_one(); + } + } + void notify_not_full_if_needed(unique_lock& lk) + { + if (waiting_full_ > 0) + { + --waiting_full_; + lk.unlock(); + not_full_.notify_one(); + } + } + + void pull(value_type& elem, unique_lock& lk) + { + elem = boost::move(data_[out_]); + out_ = inc(out_); + notify_not_full_if_needed(lk); + } + boost::shared_ptr ptr_pull(unique_lock& lk) + { + shared_ptr res = make_shared(boost::move(data_[out_])); + out_ = inc(out_); + notify_not_full_if_needed(lk); + return res; + } + + + void set_in(size_type in, unique_lock& lk) + { + in_ = in; + notify_not_empty_if_needed(lk); + } + + void push_at(const value_type& elem, size_type in_p_1, unique_lock& lk) + { + data_[in_] = elem; + set_in(in_p_1, lk); + } + + void push_at(BOOST_THREAD_RV_REF(value_type) elem, size_type in_p_1, unique_lock& lk) + { + data_[in_] = boost::move(elem); + set_in(in_p_1, lk); + } + + + }; + + template + sync_bounded_queue::sync_bounded_queue(typename sync_bounded_queue::size_type max_elems) : + waiting_full_(0), waiting_empty_(0), data_(new value_type[max_elems + 1]), in_(0), out_(0), capacity_(max_elems + 1), + closed_(false) + { + BOOST_ASSERT_MSG(max_elems >= 1, "number of elements must be > 1"); + } + +// template +// template +// sync_bounded_queue::sync_bounded_queue(size_type max_elems, Range range) : +// waiting_full_(0), waiting_empty_(0), data_(new value_type[max_elems + 1]), in_(0), out_(0), capacity_(max_elems + 1), +// closed_(false) +// { +// BOOST_ASSERT_MSG(max_elems >= 1, "number of elements must be > 1"); +// BOOST_ASSERT_MSG(max_elems == size(range), "number of elements must match range's size"); +// try +// { +// typedef typename Range::iterator iterator_t; +// iterator_t first = boost::begin(range); +// iterator_t end = boost::end(range); +// size_type in = 0; +// for (iterator_t cur = first; cur != end; ++cur, ++in) +// { +// data_[in] = *cur; +// } +// set_in(in); +// } +// catch (...) +// { +// delete[] data_; +// } +// } + + template + sync_bounded_queue::~sync_bounded_queue() + { + delete[] data_; + } + + template + void sync_bounded_queue::close() + { + { + lock_guard lk(mtx_); + closed_ = true; + } + not_empty_.notify_all(); + not_full_.notify_all(); + } + + template + bool sync_bounded_queue::closed() const + { + lock_guard lk(mtx_); + return closed_; + } + + template + bool sync_bounded_queue::empty() const + { + lock_guard lk(mtx_); + return empty(lk); + } + template + bool sync_bounded_queue::full() const + { + lock_guard lk(mtx_); + return full(lk); + } + + template + typename sync_bounded_queue::size_type sync_bounded_queue::capacity() const + { + lock_guard lk(mtx_); + return capacity(lk); + } + + template + typename sync_bounded_queue::size_type sync_bounded_queue::size() const + { + lock_guard lk(mtx_); + return size(lk); + } + + + template + bool sync_bounded_queue::try_pull(ValueType& elem, unique_lock& lk) + { + if (empty(lk)) + { + throw_if_closed(lk); + return false; + } + pull(elem, lk); + return true; + } + template + shared_ptr sync_bounded_queue::try_pull(unique_lock& lk) + { + if (empty(lk)) + { + throw_if_closed(lk); + return shared_ptr(); + } + return ptr_pull(lk); + } + + template + bool sync_bounded_queue::try_pull(ValueType& elem) + { + try + { + unique_lock lk(mtx_); + return try_pull(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + + template + bool sync_bounded_queue::try_pull(no_block_tag,ValueType& elem) + { + try + { + unique_lock lk(mtx_, try_to_lock); + if (!lk.owns_lock()) + { + return false; + } + return try_pull(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + template + boost::shared_ptr sync_bounded_queue::try_pull() + { + try + { + unique_lock lk(mtx_); + return try_pull(lk); + } + catch (...) + { + close(); + throw; + } + } + + template + void sync_bounded_queue::throw_if_closed(unique_lock&) + { + if (closed_) + { + BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); + } + } + + template + void sync_bounded_queue::wait_until_not_empty(unique_lock& lk) + { + for (;;) + { + if (out_ != in_) break; + throw_if_closed(lk); + ++waiting_empty_; + not_empty_.wait(lk); + } + } + template + void sync_bounded_queue::wait_until_not_empty(unique_lock& lk, bool & closed) + { + for (;;) + { + if (out_ != in_) break; + if (closed_) {closed=true; return;} + ++waiting_empty_; + not_empty_.wait(lk); + } + } + + template + void sync_bounded_queue::pull(ValueType& elem) + { + try + { + unique_lock lk(mtx_); + wait_until_not_empty(lk); + pull(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + template + void sync_bounded_queue::pull(ValueType& elem, bool & closed) + { + try + { + unique_lock lk(mtx_); + wait_until_not_empty(lk, closed); + if (closed) {return;} + pull(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + + // enable if ValueType is nothrow movable + template + ValueType sync_bounded_queue::pull() + { + try + { + value_type elem; + pull(elem); + return boost::move(elem); + } + catch (...) + { + close(); + throw; + } + } + template + boost::shared_ptr sync_bounded_queue::ptr_pull() + { + try + { + unique_lock lk(mtx_); + wait_until_not_empty(lk); + return ptr_pull(lk); + } + catch (...) + { + close(); + throw; + } + } + + template + bool sync_bounded_queue::try_push(const ValueType& elem, unique_lock& lk) + { + throw_if_closed(lk); + size_type in_p_1 = inc(in_); + if (in_p_1 == out_) // full() + { + return false; + } + push_at(elem, in_p_1, lk); + return true; + } + + template + bool sync_bounded_queue::try_push(const ValueType& elem) + { + try + { + unique_lock lk(mtx_); + return try_push(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + + template + bool sync_bounded_queue::try_push(no_block_tag, const ValueType& elem) + { + try + { + unique_lock lk(mtx_, try_to_lock); + if (!lk.owns_lock()) return false; + return try_push(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + + + template + typename sync_bounded_queue::size_type sync_bounded_queue::wait_until_not_full(unique_lock& lk) + { + for (;;) + { + throw_if_closed(lk); + size_type in_p_1 = inc(in_); + if (in_p_1 != out_) // ! full() + { + return in_p_1; + } + ++waiting_full_; + not_full_.wait(lk); + } + } + + template + void sync_bounded_queue::push(const ValueType& elem) + { + try + { + unique_lock lk(mtx_); + push_at(elem, wait_until_not_full(lk), lk); + } + catch (...) + { + close(); + throw; + } + } + + template + bool sync_bounded_queue::try_push(BOOST_THREAD_RV_REF(ValueType) elem, unique_lock& lk) + { + throw_if_closed(lk); + size_type in_p_1 = inc(in_); + if (in_p_1 == out_) // full() + { + return false; + } + push_at(boost::move(elem), in_p_1, lk); + return true; + } + + template + bool sync_bounded_queue::try_push(BOOST_THREAD_RV_REF(ValueType) elem) + { + try + { + unique_lock lk(mtx_); + return try_push(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + + template + bool sync_bounded_queue::try_push(no_block_tag, BOOST_THREAD_RV_REF(ValueType) elem) + { + try + { + unique_lock lk(mtx_, try_to_lock); + if (!lk.owns_lock()) + { + return false; + } + return try_push(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + + template + void sync_bounded_queue::push(BOOST_THREAD_RV_REF(ValueType) elem) + { + try + { + unique_lock lk(mtx_); + push_at(elem, wait_until_not_full(lk), lk); + } + catch (...) + { + close(); + throw; + } + } + + template + sync_bounded_queue& operator<<(sync_bounded_queue& sbq, BOOST_THREAD_RV_REF(ValueType) elem) + { + sbq.push(boost::forward(elem)); + return sbq; + } + + template + sync_bounded_queue& operator<<(sync_bounded_queue& sbq, ValueType const&elem) + { + sbq.push(elem); + return sbq; + } + + template + sync_bounded_queue& operator>>(sync_bounded_queue& sbq, ValueType &elem) + { + sbq.pull(elem); + return sbq; + } + +} + +#include + +#endif diff --git a/include/boost/thread/sync_queue.hpp b/include/boost/thread/sync_queue.hpp new file mode 100644 index 00000000..8a529a64 --- /dev/null +++ b/include/boost/thread/sync_queue.hpp @@ -0,0 +1,516 @@ +#ifndef BOOST_THREAD_SYNC_QUEUE_HPP +#define BOOST_THREAD_SYNC_QUEUE_HPP + +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Vicente J. Botet Escriba 2013. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/thread for documentation. +// +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace boost +{ + + + template + class sync_queue + { + public: + typedef ValueType value_type; + typedef std::size_t size_type; + + // Constructors/Assignment/Destructors + BOOST_THREAD_NO_COPYABLE(sync_queue) + sync_queue(); + template + explicit sync_queue(Range range); + ~sync_queue(); + + // Observers + bool empty() const; + bool full() const; + size_type size() const; + bool closed() const; + + // Modifiers + void close(); + + void push(const value_type& x); + void push(BOOST_THREAD_RV_REF(value_type) x); + bool try_push(const value_type& x); + bool try_push(BOOST_THREAD_RV_REF(value_type) x); + bool try_push(no_block_tag, const value_type& x); + bool try_push(no_block_tag, BOOST_THREAD_RV_REF(value_type) x); + + // Observers/Modifiers + void pull(value_type&); + void pull(ValueType& elem, bool & closed); + // enable_if is_nothrow_copy_movable + value_type pull(); + shared_ptr ptr_pull(); + bool try_pull(value_type&); + bool try_pull(no_block_tag,value_type&); + shared_ptr try_pull(); + + + private: + mutable mutex mtx_; + condition_variable not_empty_; + size_type waiting_empty_; + boost::container::deque data_; + bool closed_; + + bool empty(unique_lock& ) const BOOST_NOEXCEPT + { + return data_.empty(); + } + bool empty(lock_guard& ) const BOOST_NOEXCEPT + { + return data_.empty(); + } + + size_type size(lock_guard& ) const BOOST_NOEXCEPT + { + return data_.size(); + } + + void throw_if_closed(unique_lock&); + + bool try_pull(value_type& x, unique_lock& lk); + bool try_push(const value_type& x, unique_lock& lk); + bool try_push(BOOST_THREAD_RV_REF(value_type) x, unique_lock& lk); + shared_ptr try_pull(unique_lock& lk); + + void wait_until_not_empty(unique_lock& lk); + void wait_until_not_empty(unique_lock& lk, bool&); + + + void notify_not_empty_if_needed(unique_lock& lk) + { + if (waiting_empty_ > 0) + { + --waiting_empty_; + lk.unlock(); + not_empty_.notify_one(); + } + } + + void pull(value_type& elem, unique_lock& ) + { + elem = boost::move(data_.front()); + data_.pop_front(); + } + boost::shared_ptr ptr_pull(unique_lock& ) + { + shared_ptr res = make_shared(boost::move(data_.front())); + data_.pop_front(); + return res; + } + + void push(const value_type& elem, unique_lock& lk) + { + data_.push_back(elem); + notify_not_empty_if_needed(lk); + } + + void push(BOOST_THREAD_RV_REF(value_type) elem, unique_lock& lk) + { + data_.push(boost::move(elem)); + notify_not_empty_if_needed(lk); + } + + + }; + + template + sync_queue::sync_queue() : + waiting_empty_(0), data_(), closed_(false) + { + BOOST_ASSERT(data_.empty()); + } + +// template +// template +// explicit sync_queue::sync_queue(Range range) : +// waiting_empty_(0), data_(), closed_(false) +// { +// try +// { +// typedef typename Range::iterator iterator_t; +// iterator_t first = boost::begin(range); +// iterator_t end = boost::end(range); +// for (iterator_t cur = first; cur != end; ++cur) +// { +// data_.push(boost::move(*cur));; +// } +// notify_not_empty_if_needed(lk); +// } +// catch (...) +// { +// delete[] data_; +// } +// } + + template + sync_queue::~sync_queue() + { + } + + template + void sync_queue::close() + { + { + lock_guard lk(mtx_); + closed_ = true; + } + not_empty_.notify_all(); + } + + template + bool sync_queue::closed() const + { + lock_guard lk(mtx_); + return closed_; + } + + template + bool sync_queue::empty() const + { + lock_guard lk(mtx_); + return empty(lk); + } + template + bool sync_queue::full() const + { + return false; + } + + template + typename sync_queue::size_type sync_queue::size() const + { + lock_guard lk(mtx_); + return size(lk); + } + + + template + bool sync_queue::try_pull(ValueType& elem, unique_lock& lk) + { + if (empty(lk)) + { + throw_if_closed(lk); + return false; + } + pull(elem, lk); + return true; + } + template + shared_ptr sync_queue::try_pull(unique_lock& lk) + { + if (empty(lk)) + { + throw_if_closed(lk); + return shared_ptr(); + } + return ptr_pull(lk); + } + + template + bool sync_queue::try_pull(ValueType& elem) + { + try + { + unique_lock lk(mtx_); + return try_pull(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + + template + bool sync_queue::try_pull(no_block_tag,ValueType& elem) + { + try + { + unique_lock lk(mtx_, try_to_lock); + if (!lk.owns_lock()) + { + return false; + } + return try_pull(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + template + boost::shared_ptr sync_queue::try_pull() + { + try + { + unique_lock lk(mtx_); + return try_pull(lk); + } + catch (...) + { + close(); + throw; + } + } + + template + void sync_queue::throw_if_closed(unique_lock&) + { + if (closed_) + { + BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); + } + } + + template + void sync_queue::wait_until_not_empty(unique_lock& lk) + { + for (;;) + { + if (! empty(lk)) break; + throw_if_closed(lk); + ++waiting_empty_; + not_empty_.wait(lk); + } + } + template + void sync_queue::wait_until_not_empty(unique_lock& lk, bool & closed) + { + for (;;) + { + if (! empty(lk)) break; + if (closed_) {closed=true; return;} + ++waiting_empty_; + not_empty_.wait(lk); + } + } + + template + void sync_queue::pull(ValueType& elem) + { + try + { + unique_lock lk(mtx_); + wait_until_not_empty(lk); + pull(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + template + void sync_queue::pull(ValueType& elem, bool & closed) + { + try + { + unique_lock lk(mtx_); + wait_until_not_empty(lk, closed); + if (closed) {return;} + pull(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + + // enable if ValueType is nothrow movable + template + ValueType sync_queue::pull() + { + try + { + value_type elem; + pull(elem); + return boost::move(elem); + } + catch (...) + { + close(); + throw; + } + } + template + boost::shared_ptr sync_queue::ptr_pull() + { + try + { + unique_lock lk(mtx_); + wait_until_not_empty(lk); + return ptr_pull(lk); + } + catch (...) + { + close(); + throw; + } + } + + template + bool sync_queue::try_push(const ValueType& elem, unique_lock& lk) + { + throw_if_closed(lk); + push(elem, lk); + return true; + } + + template + bool sync_queue::try_push(const ValueType& elem) + { + try + { + unique_lock lk(mtx_); + return try_push(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + + template + bool sync_queue::try_push(no_block_tag, const ValueType& elem) + { + try + { + unique_lock lk(mtx_, try_to_lock); + if (!lk.owns_lock()) return false; + return try_push(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + + template + void sync_queue::push(const ValueType& elem) + { + try + { + unique_lock lk(mtx_); + throw_if_closed(lk); + push(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + + template + bool sync_queue::try_push(BOOST_THREAD_RV_REF(ValueType) elem, unique_lock& lk) + { + throw_if_closed(lk); + push(boost::forward(elem), lk); + return true; + } + + template + bool sync_queue::try_push(BOOST_THREAD_RV_REF(ValueType) elem) + { + try + { + unique_lock lk(mtx_); + return try_push(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + + template + bool sync_queue::try_push(no_block_tag, BOOST_THREAD_RV_REF(ValueType) elem) + { + try + { + unique_lock lk(mtx_, try_to_lock); + if (!lk.owns_lock()) + { + return false; + } + return try_push(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + + template + void sync_queue::push(BOOST_THREAD_RV_REF(ValueType) elem) + { + try + { + unique_lock lk(mtx_); + throw_if_closed(lk); + push(elem, lk); + } + catch (...) + { + close(); + throw; + } + } + + template + sync_queue& operator<<(sync_queue& sbq, BOOST_THREAD_RV_REF(ValueType) elem) + { + sbq.push(boost::forward(elem)); + return sbq; + } + + template + sync_queue& operator<<(sync_queue& sbq, ValueType const&elem) + { + sbq.push(elem); + return sbq; + } + + template + sync_queue& operator>>(sync_queue& sbq, ValueType &elem) + { + sbq.pull(elem); + return sbq; + } + +} + +#include + +#endif diff --git a/include/boost/thread/thread_functors.hpp b/include/boost/thread/thread_functors.hpp index 9aa583d8..f7bd816b 100644 --- a/include/boost/thread/thread_functors.hpp +++ b/include/boost/thread/thread_functors.hpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index ec8fef73..1e7d215c 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -667,6 +667,9 @@ rule thread-compile-fail ( sources : reqs * : name ) [ thread-run2-noit ../example/scoped_thread.cpp : ex_scoped_thread ] [ thread-run2-noit ../example/strict_lock.cpp : ex_strict_lock ] [ thread-run2-noit ../example/ba_externallly_locked.cpp : ex_ba_externallly_locked ] + [ thread-run ../example/producer_consumer_bounded.cpp ] + [ thread-run ../example/producer_consumer.cpp ] + [ thread-run ../example/not_interleaved.cpp ] ; @@ -736,8 +739,6 @@ rule thread-compile-fail ( sources : reqs * : name ) #[ thread-run ../example/unwrap.cpp ] #[ thread-run ../example/perf_condition_variable.cpp ] #[ thread-run ../example/perf_shared_mutex.cpp ] - #[ thread-run ../example/not_interleaved.cpp ] - ; } diff --git a/test/sync/futures/future/then_pass.cpp b/test/sync/futures/future/then_pass.cpp index 8fd36e74..ceeeea7b 100644 --- a/test/sync/futures/future/then_pass.cpp +++ b/test/sync/futures/future/then_pass.cpp @@ -12,7 +12,7 @@ #define BOOST_THREAD_VERSION 4 #define BOOST_THREAD_DONT_PROVIDE_FUTURE_INVALID_AFTER_GET -//#define BOOST_THREAD_USES_LOG +#define BOOST_THREAD_USES_LOG #define BOOST_THREAD_USES_LOG_THREAD_ID #include diff --git a/test/sync/mutual_exclusion/locks/shared_lock/cons/adopt_lock_pass.cpp b/test/sync/mutual_exclusion/locks/shared_lock/cons/adopt_lock_pass.cpp index 8787126b..f9089e7a 100755 --- a/test/sync/mutual_exclusion/locks/shared_lock/cons/adopt_lock_pass.cpp +++ b/test/sync/mutual_exclusion/locks/shared_lock/cons/adopt_lock_pass.cpp @@ -27,7 +27,7 @@ int main() { boost::shared_mutex m; - m.lock(); + m.lock_shared(); boost::shared_lock lk(m, boost::adopt_lock); BOOST_TEST(lk.mutex() == &m); BOOST_TEST(lk.owns_lock() == true); diff --git a/test/sync/mutual_exclusion/locks/shared_lock_guard/adopt_lock_pass.cpp b/test/sync/mutual_exclusion/locks/shared_lock_guard/adopt_lock_pass.cpp index d8532f09..12a0202b 100755 --- a/test/sync/mutual_exclusion/locks/shared_lock_guard/adopt_lock_pass.cpp +++ b/test/sync/mutual_exclusion/locks/shared_lock_guard/adopt_lock_pass.cpp @@ -40,7 +40,7 @@ void f() time_point t0 = Clock::now(); time_point t1; { - m.lock(); + m.lock_shared(); boost::shared_lock_guard lg(m, boost::adopt_lock); t1 = Clock::now(); } @@ -50,7 +50,7 @@ void f() //time_point t0 = Clock::now(); //time_point t1; { - m.lock(); + m.lock_shared(); boost::shared_lock_guard lg(m, boost::adopt_lock); //t1 = Clock::now(); } diff --git a/test/test_2741.cpp b/test/test_2741.cpp index 6bd4a49c..ecc87920 100644 --- a/test/test_2741.cpp +++ b/test/test_2741.cpp @@ -7,7 +7,7 @@ #include -#include +#include #include #include #include diff --git a/test/test_3837.cpp b/test/test_3837.cpp index 97036cd2..fd0056f3 100644 --- a/test/test_3837.cpp +++ b/test/test_3837.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include diff --git a/test/test_4521.cpp b/test/test_4521.cpp index cc0f4089..2b1a21bf 100644 --- a/test/test_4521.cpp +++ b/test/test_4521.cpp @@ -5,7 +5,8 @@ #define BOOST_THREAD_VERSION 2 -#include +#include +#include int calculate_the_answer_to_life_the_universe_and_everything() { diff --git a/test/test_4882.cpp b/test/test_4882.cpp index 1fd1dfbd..8b085598 100644 --- a/test/test_4882.cpp +++ b/test/test_4882.cpp @@ -7,7 +7,7 @@ //#define BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN //#define BOOST_THREAD_USES_LOG -#include +#include #include #include //#include diff --git a/test/test_5351.cpp b/test/test_5351.cpp index 92b2d696..958afb31 100644 --- a/test/test_5351.cpp +++ b/test/test_5351.cpp @@ -6,7 +6,7 @@ #define BOOST_THREAD_PROVIDES_INTERRUPTIONS #include -#include +#include #include #include diff --git a/test/test_5502.cpp b/test/test_5502.cpp index 12bd6490..455e79ee 100644 --- a/test/test_5502.cpp +++ b/test/test_5502.cpp @@ -12,7 +12,7 @@ int XXX = 20; int YYY = 10; -#include +#include #include //#include diff --git a/test/test_5542_1.cpp b/test/test_5542_1.cpp index 9e86e504..08bab885 100644 --- a/test/test_5542_1.cpp +++ b/test/test_5542_1.cpp @@ -6,7 +6,7 @@ #define BOOST_THREAD_VERSION 2 #include -#include +#include class Worker { diff --git a/test/test_5542_2.cpp b/test/test_5542_2.cpp index 9111f909..21256602 100644 --- a/test/test_5542_2.cpp +++ b/test/test_5542_2.cpp @@ -5,7 +5,7 @@ #define BOOST_THREAD_VERSION 2 -#include +#include void run_thread() { return; diff --git a/test/test_5542_3.cpp b/test/test_5542_3.cpp index b1c8ea28..4541243f 100644 --- a/test/test_5542_3.cpp +++ b/test/test_5542_3.cpp @@ -6,7 +6,7 @@ #define BOOST_THREAD_VERSION 2 #include -#include +#include #include void workerFunc() diff --git a/test/test_5891.cpp b/test/test_5891.cpp index bf24b8b0..bb1af3d5 100755 --- a/test/test_5891.cpp +++ b/test/test_5891.cpp @@ -6,7 +6,7 @@ #define BOOST_THREAD_VERSION 2 #include -#include +#include using namespace std; using namespace boost; diff --git a/test/test_6130.cpp b/test/test_6130.cpp index 3a92591e..a351039b 100644 --- a/test/test_6130.cpp +++ b/test/test_6130.cpp @@ -5,7 +5,7 @@ #define BOOST_THREAD_VERSION 2 -#include +#include #include #include #include diff --git a/test/test_6174.cpp b/test/test_6174.cpp index 4775c774..bc28dcfa 100644 --- a/test/test_6174.cpp +++ b/test/test_6174.cpp @@ -6,7 +6,8 @@ #define BOOST_THREAD_VERSION 3 -#include +#include +#include #include #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES diff --git a/test/test_7160.cpp b/test/test_7160.cpp index 757043e1..a1957ae7 100644 --- a/test/test_7160.cpp +++ b/test/test_7160.cpp @@ -6,7 +6,7 @@ #define BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG #include -#include +#include class ThreadClass { diff --git a/test/test_7328.cpp b/test/test_7328.cpp index 6cea415b..d087a7ca 100644 --- a/test/test_7328.cpp +++ b/test/test_7328.cpp @@ -6,7 +6,7 @@ #define BOOST_THREAD_PROVIDES_INTERRUPTIONS #include -#include +#include #include #if defined BOOST_THREAD_USES_CHRONO diff --git a/test/test_7571.cpp b/test/test_7571.cpp index e272c2ad..a336795f 100644 --- a/test/test_7571.cpp +++ b/test/test_7571.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include // Number should be big enough to allow context switch between threads, otherwise the bug doesn't show. diff --git a/test/test_7665.cpp b/test/test_7665.cpp index ed5c58a0..7244a7d3 100644 --- a/test/test_7665.cpp +++ b/test/test_7665.cpp @@ -7,7 +7,7 @@ #define BOOST_THREAD_USES_LOG #include -#include +#include #include void thread() diff --git a/test/test_7666.cpp b/test/test_7666.cpp index b0c8372f..1ec71dff 100644 --- a/test/test_7666.cpp +++ b/test/test_7666.cpp @@ -6,7 +6,7 @@ #define BOOST_CHRONO_VERSION 2 #define BOOST_THREAD_VERSION 2 -#include +#include void myFunc() { diff --git a/test/test_7720.cpp b/test/test_7720.cpp index c746fa1a..42152e04 100644 --- a/test/test_7720.cpp +++ b/test/test_7720.cpp @@ -6,7 +6,7 @@ //////////////////////////////////////////// //#define BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN -#include +#include #include using namespace boost; diff --git a/test/test_7755.cpp b/test/test_7755.cpp index 4b2010cf..5a689cf4 100644 --- a/test/test_7755.cpp +++ b/test/test_7755.cpp @@ -7,7 +7,7 @@ //#define BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN #include -#include +#include #include // shared_mutex_deadlock.cpp : Defines the entry point for the console application. // diff --git a/test/test_condition.cpp b/test/test_condition.cpp index 02d76a37..f37a9555 100644 --- a/test/test_condition.cpp +++ b/test/test_condition.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include diff --git a/test/test_condition_notify_all.cpp b/test/test_condition_notify_all.cpp index 460d16ab..944f9455 100644 --- a/test/test_condition_notify_all.cpp +++ b/test/test_condition_notify_all.cpp @@ -7,7 +7,7 @@ #include -#include +#include #include diff --git a/test/test_condition_notify_one.cpp b/test/test_condition_notify_one.cpp index 6c919fa5..5bd5f7e4 100644 --- a/test/test_condition_notify_one.cpp +++ b/test/test_condition_notify_one.cpp @@ -7,7 +7,7 @@ #include -#include +#include #include diff --git a/test/test_condition_timed_wait_times_out.cpp b/test/test_condition_timed_wait_times_out.cpp index b620131e..3632286e 100644 --- a/test/test_condition_timed_wait_times_out.cpp +++ b/test/test_condition_timed_wait_times_out.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include "./util.inl" diff --git a/test/test_futures.cpp b/test/test_futures.cpp index d33834d9..2d956524 100644 --- a/test/test_futures.cpp +++ b/test/test_futures.cpp @@ -6,7 +6,7 @@ #define BOOST_THREAD_VERSION 2 -#include +#include #include #include #include diff --git a/test/test_generic_locks.cpp b/test/test_generic_locks.cpp index f6af4c8e..f7c56089 100644 --- a/test/test_generic_locks.cpp +++ b/test/test_generic_locks.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include diff --git a/test/test_hardware_concurrency.cpp b/test/test_hardware_concurrency.cpp index dded585d..487aa536 100644 --- a/test/test_hardware_concurrency.cpp +++ b/test/test_hardware_concurrency.cpp @@ -2,7 +2,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) -#include +#include #include #include diff --git a/test/test_lock_concept.cpp b/test/test_lock_concept.cpp index 26b91249..ac152bf6 100644 --- a/test/test_lock_concept.cpp +++ b/test/test_lock_concept.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include @@ -227,6 +227,19 @@ struct test_initially_locked_with_adopt_lock_parameter BOOST_CHECK(lock.owns_lock()); } }; +template +struct test_initially_lock_shared_with_adopt_lock_parameter +{ + void operator()() const + { + Mutex m; + m.lock_shared(); + Lock lock(m,boost::adopt_lock); + + BOOST_CHECK(lock); + BOOST_CHECK(lock.owns_lock()); + } +}; template @@ -531,7 +544,7 @@ void test_shared_lock() 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_initially_lock_shared_with_adopt_lock_parameter()(); test_unlocked_after_unlock_called()(); test_locked_after_lock_called()(); test_locked_after_try_lock_called()(); diff --git a/test/test_move_function.cpp b/test/test_move_function.cpp index c432f96d..6eb14523 100644 --- a/test/test_move_function.cpp +++ b/test/test_move_function.cpp @@ -5,7 +5,7 @@ #define BOOST_THREAD_VERSION 2 -#include +#include #include #include #include diff --git a/test/test_mutex.cpp b/test/test_mutex.cpp index b90e6cb2..c10125a2 100644 --- a/test/test_mutex.cpp +++ b/test/test_mutex.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include diff --git a/test/test_shared_mutex.cpp b/test/test_shared_mutex.cpp index ff05110d..ca10e300 100644 --- a/test/test_shared_mutex.cpp +++ b/test/test_shared_mutex.cpp @@ -20,6 +20,7 @@ void test_multiple_readers() { + std::cout << __LINE__ << std::endl; unsigned const number_of_threads=10; boost::thread_group pool; @@ -67,6 +68,7 @@ void test_multiple_readers() void test_only_one_writer_permitted() { + std::cout << __LINE__ << std::endl; unsigned const number_of_threads=10; boost::thread_group pool; @@ -109,6 +111,7 @@ void test_only_one_writer_permitted() void test_reader_blocks_writer() { + std::cout << __LINE__ << std::endl; boost::thread_group pool; boost::shared_mutex rw_mutex; @@ -155,6 +158,7 @@ void test_reader_blocks_writer() void test_unlocking_writer_unblocks_all_readers() { + std::cout << __LINE__ << std::endl; boost::thread_group pool; boost::shared_mutex rw_mutex; @@ -206,6 +210,7 @@ void test_unlocking_writer_unblocks_all_readers() void test_unlocking_last_reader_only_unblocks_one_writer() { + std::cout << __LINE__ << std::endl; boost::thread_group pool; boost::shared_mutex rw_mutex; diff --git a/test/test_shared_mutex_part_2.cpp b/test/test_shared_mutex_part_2.cpp index 55defb45..3f213b6b 100644 --- a/test/test_shared_mutex_part_2.cpp +++ b/test/test_shared_mutex_part_2.cpp @@ -51,7 +51,7 @@ public: void test_only_one_upgrade_lock_permitted() { - unsigned const number_of_threads=10; + unsigned const number_of_threads=2; boost::thread_group pool; diff --git a/test/test_shared_mutex_timed_locks.cpp b/test/test_shared_mutex_timed_locks.cpp index 225b25c8..5eea1a1a 100644 --- a/test/test_shared_mutex_timed_locks.cpp +++ b/test/test_shared_mutex_timed_locks.cpp @@ -6,7 +6,7 @@ #define BOOST_THREAD_VERSION 2 #include -#include +#include #include #include "./util.inl" #include "./shared_mutex_locking_thread.hpp" diff --git a/test/test_thread.cpp b/test/test_thread.cpp index b70607ec..386a471c 100644 --- a/test/test_thread.cpp +++ b/test/test_thread.cpp @@ -10,7 +10,7 @@ #include -#include +#include #include #include #include diff --git a/test/test_thread_exit.cpp b/test/test_thread_exit.cpp index eb3ee44b..abd18a85 100644 --- a/test/test_thread_exit.cpp +++ b/test/test_thread_exit.cpp @@ -4,7 +4,7 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#include +#include #include #include #include diff --git a/test/test_thread_id.cpp b/test/test_thread_id.cpp index 78adf7cc..0a9725ec 100644 --- a/test/test_thread_id.cpp +++ b/test/test_thread_id.cpp @@ -2,7 +2,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) -#include +#include #include #include diff --git a/test/test_thread_launching.cpp b/test/test_thread_launching.cpp index 30fd17ce..7be8f8cf 100644 --- a/test/test_thread_launching.cpp +++ b/test/test_thread_launching.cpp @@ -5,7 +5,7 @@ #define BOOST_THREAD_VERSION 3 -#include +#include #include #include #include diff --git a/test/test_thread_mf.cpp b/test/test_thread_mf.cpp index 1d9145f7..843008fa 100644 --- a/test/test_thread_mf.cpp +++ b/test/test_thread_mf.cpp @@ -7,7 +7,7 @@ // #define BOOST_THREAD_VERSION 3 -#include +#include #include struct X diff --git a/test/test_thread_move.cpp b/test/test_thread_move.cpp index 0896c728..fe01b62f 100644 --- a/test/test_thread_move.cpp +++ b/test/test_thread_move.cpp @@ -2,7 +2,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) -#include +#include #include void do_nothing(boost::thread::id* my_id) diff --git a/test/test_thread_move_return.cpp b/test/test_thread_move_return.cpp index 77c94895..e9e4512b 100644 --- a/test/test_thread_move_return.cpp +++ b/test/test_thread_move_return.cpp @@ -5,7 +5,7 @@ #define BOOST_THREAD_USES_MOVE -#include +#include #include void do_nothing(boost::thread::id* my_id) diff --git a/test/test_thread_return_local.cpp b/test/test_thread_return_local.cpp index d1142ba4..46e84a7e 100644 --- a/test/test_thread_return_local.cpp +++ b/test/test_thread_return_local.cpp @@ -5,7 +5,7 @@ #define BOOST_THREAD_USES_MOVE -#include +#include #include void do_nothing(boost::thread::id* my_id) diff --git a/test/threads/this_thread/get_id/get_id_pass.cpp b/test/threads/this_thread/get_id/get_id_pass.cpp index 8fb82b4a..f0287cb6 100644 --- a/test/threads/this_thread/get_id/get_id_pass.cpp +++ b/test/threads/this_thread/get_id/get_id_pass.cpp @@ -15,7 +15,7 @@ // thread::id this_thread::get_id(); -#include +#include #include diff --git a/test/threads/this_thread/sleep_for/sleep_for_pass.cpp b/test/threads/this_thread/sleep_for/sleep_for_pass.cpp index ce325dcc..bd467c4a 100644 --- a/test/threads/this_thread/sleep_for/sleep_for_pass.cpp +++ b/test/threads/this_thread/sleep_for/sleep_for_pass.cpp @@ -15,7 +15,7 @@ // thread::id this_thread::get_id(); -#include +#include #include #include diff --git a/test/threads/this_thread/sleep_until/sleep_until_pass.cpp b/test/threads/this_thread/sleep_until/sleep_until_pass.cpp index 4adcd3c9..00817d05 100644 --- a/test/threads/this_thread/sleep_until/sleep_until_pass.cpp +++ b/test/threads/this_thread/sleep_until/sleep_until_pass.cpp @@ -15,7 +15,7 @@ // thread::id this_thread::get_id(); -#include +#include #include #include diff --git a/test/threads/thread/assign/copy_fail.cpp b/test/threads/thread/assign/copy_fail.cpp index a86691f2..5ccac5b3 100644 --- a/test/threads/thread/assign/copy_fail.cpp +++ b/test/threads/thread/assign/copy_fail.cpp @@ -18,7 +18,7 @@ // thread& operator=(thread&& t); -#include +#include #include #include #include diff --git a/test/threads/thread/assign/move_pass.cpp b/test/threads/thread/assign/move_pass.cpp index cf94f592..293c5d83 100644 --- a/test/threads/thread/assign/move_pass.cpp +++ b/test/threads/thread/assign/move_pass.cpp @@ -20,7 +20,7 @@ #define BOOST_THREAD_PROVIDES_THREAD_MOVE_ASSIGN_CALLS_TERMINATE_IF_JOINABLE -#include +#include #include #include #include diff --git a/test/threads/thread/constr/FArgs_pass.cpp b/test/threads/thread/constr/FArgs_pass.cpp index 6883cd0a..c6630760 100644 --- a/test/threads/thread/constr/FArgs_pass.cpp +++ b/test/threads/thread/constr/FArgs_pass.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include unsigned throw_one = 0xFFFF; diff --git a/test/threads/thread/constr/F_pass.cpp b/test/threads/thread/constr/F_pass.cpp index 181e4d2a..35dcb31a 100644 --- a/test/threads/thread/constr/F_pass.cpp +++ b/test/threads/thread/constr/F_pass.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include unsigned throw_one = 0xFFFF; diff --git a/test/threads/thread/constr/FrvalueArgs_pass.cpp b/test/threads/thread/constr/FrvalueArgs_pass.cpp index 881f0a2b..bf511027 100644 --- a/test/threads/thread/constr/FrvalueArgs_pass.cpp +++ b/test/threads/thread/constr/FrvalueArgs_pass.cpp @@ -19,7 +19,7 @@ #define BOOST_THREAD_VERSION 4 -#include +#include #include #include #include diff --git a/test/threads/thread/constr/Frvalue_pass.cpp b/test/threads/thread/constr/Frvalue_pass.cpp index 5d2821fd..38061e0a 100644 --- a/test/threads/thread/constr/Frvalue_pass.cpp +++ b/test/threads/thread/constr/Frvalue_pass.cpp @@ -19,7 +19,7 @@ #define BOOST_THREAD_USES_MOVE -#include +#include #include #include #include diff --git a/test/threads/thread/constr/copy_fail.cpp b/test/threads/thread/constr/copy_fail.cpp index 933b3278..caa72c2d 100644 --- a/test/threads/thread/constr/copy_fail.cpp +++ b/test/threads/thread/constr/copy_fail.cpp @@ -18,7 +18,7 @@ // thread(const thread&) = delete; -#include +#include #include #include #include diff --git a/test/threads/thread/constr/default_pass.cpp b/test/threads/thread/constr/default_pass.cpp index 9270e89c..cea602d1 100644 --- a/test/threads/thread/constr/default_pass.cpp +++ b/test/threads/thread/constr/default_pass.cpp @@ -18,7 +18,7 @@ // thread(); -#include +#include #include #include diff --git a/test/threads/thread/constr/move_pass.cpp b/test/threads/thread/constr/move_pass.cpp index 09ea8888..66f9d754 100644 --- a/test/threads/thread/constr/move_pass.cpp +++ b/test/threads/thread/constr/move_pass.cpp @@ -17,7 +17,7 @@ // thread(thread&& t); -#include +#include #include #include #include diff --git a/test/threads/thread/destr/dtor_pass.cpp b/test/threads/thread/destr/dtor_pass.cpp index edd70040..ea0e6bac 100644 --- a/test/threads/thread/destr/dtor_pass.cpp +++ b/test/threads/thread/destr/dtor_pass.cpp @@ -19,7 +19,7 @@ #define BOOST_THREAD_PROVIDES_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE -#include +#include #include #include #include diff --git a/test/threads/thread/id/hash_pass.cpp b/test/threads/thread/id/hash_pass.cpp index 5202d46b..6ce52c97 100644 --- a/test/threads/thread/id/hash_pass.cpp +++ b/test/threads/thread/id/hash_pass.cpp @@ -23,7 +23,7 @@ // }; -#include +#include #include int main() diff --git a/test/threads/thread/members/detach_pass.cpp b/test/threads/thread/members/detach_pass.cpp index 0c3cc6d6..92717533 100644 --- a/test/threads/thread/members/detach_pass.cpp +++ b/test/threads/thread/members/detach_pass.cpp @@ -17,7 +17,7 @@ // void detach(); -#include +#include #include #include #include diff --git a/test/threads/thread/members/get_id_pass.cpp b/test/threads/thread/members/get_id_pass.cpp index 02c0e34a..dcaa7090 100644 --- a/test/threads/thread/members/get_id_pass.cpp +++ b/test/threads/thread/members/get_id_pass.cpp @@ -17,7 +17,7 @@ // id get_id() const; -#include +#include #include #include #include diff --git a/test/threads/thread/members/join_pass.cpp b/test/threads/thread/members/join_pass.cpp index d5a4fa1b..f2d42b72 100644 --- a/test/threads/thread/members/join_pass.cpp +++ b/test/threads/thread/members/join_pass.cpp @@ -17,7 +17,7 @@ // void join(); #define BOOST_THREAD_VESRION 3 -#include +#include #include #include #include diff --git a/test/threads/thread/members/joinable_pass.cpp b/test/threads/thread/members/joinable_pass.cpp index 25b4fa72..dcf09d1f 100644 --- a/test/threads/thread/members/joinable_pass.cpp +++ b/test/threads/thread/members/joinable_pass.cpp @@ -17,7 +17,7 @@ // bool joinable() const; -#include +#include #include #include #include diff --git a/test/threads/thread/members/native_handle_pass.cpp b/test/threads/thread/members/native_handle_pass.cpp index c35e9a8c..01115da0 100644 --- a/test/threads/thread/members/native_handle_pass.cpp +++ b/test/threads/thread/members/native_handle_pass.cpp @@ -17,7 +17,7 @@ // native_handle_type native_handle(); -#include +#include #include #include #include @@ -61,7 +61,7 @@ int main() { { boost::thread t0( (G())); - // boost::thread::native_handle_type hdl = + // boost::thread::native_handle_type hdl = (void)t0.native_handle(); t0.join(); } diff --git a/test/threads/thread/members/swap_pass.cpp b/test/threads/thread/members/swap_pass.cpp index 6a8d5900..16af2d1b 100644 --- a/test/threads/thread/members/swap_pass.cpp +++ b/test/threads/thread/members/swap_pass.cpp @@ -17,7 +17,7 @@ // native_handle_type native_handle(); -#include +#include #include #include diff --git a/test/threads/thread/members/try_join_for_pass.cpp b/test/threads/thread/members/try_join_for_pass.cpp index a69fae90..acda794d 100644 --- a/test/threads/thread/members/try_join_for_pass.cpp +++ b/test/threads/thread/members/try_join_for_pass.cpp @@ -19,7 +19,7 @@ // bool try_join_for(const chrono::duration& rel_time); #define BOOST_THREAD_VESRION 3 -#include +#include #include #include #include diff --git a/test/threads/thread/members/try_join_until_pass.cpp b/test/threads/thread/members/try_join_until_pass.cpp index 7153d2e5..c2a8a348 100644 --- a/test/threads/thread/members/try_join_until_pass.cpp +++ b/test/threads/thread/members/try_join_until_pass.cpp @@ -19,7 +19,7 @@ // bool try_join_until(const chrono::time_point& t); #define BOOST_THREAD_VESRION 3 -#include +#include #include #include #include diff --git a/test/threads/thread/non_members/swap_pass.cpp b/test/threads/thread/non_members/swap_pass.cpp index 032427fc..eb6faf15 100644 --- a/test/threads/thread/non_members/swap_pass.cpp +++ b/test/threads/thread/non_members/swap_pass.cpp @@ -15,7 +15,7 @@ // void swap(thread& x, thread& y); -#include +#include #include #include #include diff --git a/test/threads/thread/static/hardware_concurrency_pass.cpp b/test/threads/thread/static/hardware_concurrency_pass.cpp index 7d1581b5..2a2a227e 100644 --- a/test/threads/thread/static/hardware_concurrency_pass.cpp +++ b/test/threads/thread/static/hardware_concurrency_pass.cpp @@ -17,7 +17,7 @@ // static unsigned hardware_concurrency(); -#include +#include #include int main()