diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index fd5506678..b730538dd 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -49,8 +49,8 @@ project boost/thread gcc:-Wno-long-long #BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED #BOOST_SYSTEM_NO_DEPRECATED - #BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS - + #BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS + /boost/system//boost_system #-pedantic -ansi -std=gnu++0x -Wextra -fpermissive all @@ -59,12 +59,18 @@ project boost/thread gcc:-Wno-long-long #gcc:-ansi #gcc:-fpermissive + gcc:-Wno-variadic-macros + #gcc:-Wunused-local-typedefs + gcc:-Wunused-function darwin:-Wextra darwin:-pedantic #darwin:-ansi darwin:-fpermissive darwin:-Wno-long-long + darwin:-Wno-variadic-macros + #darwin:-Wunused-local-typedefs + darwin:-Wunused-function #pathscale:-Wextra pathscale:-Wno-long-long @@ -75,6 +81,8 @@ project boost/thread #clang:-ansi #clang:-fpermissive clang:-Wno-long-long + clang:-Wunused-function + clang:-Wno-variadic-macros gcc-mingw-4.4.0:-fdiagnostics-show-option gcc-mingw-4.5.0:-fdiagnostics-show-option @@ -82,6 +90,7 @@ project boost/thread gcc-mingw-4.6.3:-fdiagnostics-show-option gcc-mingw-4.7.0:-fdiagnostics-show-option gcc-mingw-4.8.0:-fdiagnostics-show-option + #gcc:-Wno-missing-field-initializers darwin-4.6.2:-Wno-delete-non-virtual-dtor darwin-4.7.0:-Wno-delete-non-virtual-dtor @@ -93,7 +102,7 @@ project boost/thread clang-3.0:-Wno-delete-non-virtual-dtor #clang-3.0:-Wno-unused-function #clang-3.0:-Wno-unused-variable - + # Note: Some of the remarks from the Intel compiler are disabled # remark #193: zero used for undefined preprocessing identifier "XXX" # remark #304: access control not specified ("public" by default) @@ -104,7 +113,9 @@ project boost/thread intel:-wd193,304,383,444 intel:-wd593,981 intel:-wd1418 - intel:-wd2415 + intel:-wd2415 + + msvc:/wd4512 # : default-build multi @@ -115,7 +126,7 @@ project boost/thread shared:BOOST_THREAD_BUILD_DLL=1 #BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED #BOOST_SYSTEM_NO_DEPRECATED - #BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS + #BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS /boost/system//boost_system ; @@ -225,11 +236,11 @@ rule usage-requirements ( properties * ) } } - if ! vacpp in $(properties) || 11.1 in $(properties) || 12.1.0.1 in $(properties) || 12.1 in $(properties) + if ! vacpp in $(properties) || 11.1 in $(properties) || 12.1.0.1 in $(properties) || 12.1 in $(properties) { result += /boost/chrono//boost_chrono ; } - + return $(result) ; } @@ -254,8 +265,8 @@ rule requirements ( properties * ) } } result += BOOST_THREAD_DONT_USE_CHRONO ; - - if pgi in $(properties) || vacpp in $(properties) + + if pgi in $(properties) || vacpp in $(properties) { result += /boost/atomic//boost_atomic ; } diff --git a/doc/barrier.qbk b/doc/barrier.qbk index 7e7c3e122..e197034aa 100644 --- a/doc/barrier.qbk +++ b/doc/barrier.qbk @@ -63,7 +63,10 @@ are unblocked, and the barrier is reset. ]] [[Returns:] [`true` for exactly one thread from each batch of waiting threads, `false` otherwise.]] -[[Throws:] [__thread_resource_error__ if an error occurs.]] +[[Throws:] [__thread_resource_error__ if an error occurs. __thread_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __thread__ object associated with the current thread of execution.]] + +[[Notes:] [`wait()` is an ['interruption point].]] ] diff --git a/doc/changes.qbk b/doc/changes.qbk index 0043d1956..49d153dd0 100644 --- a/doc/changes.qbk +++ b/doc/changes.qbk @@ -8,8 +8,28 @@ [section:changes History] +[heading Version 4.1.0 - boost 1.54] + +* [@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] +* [@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/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 + + +[*New Features:] + +[*Fixed Bugs:] + [/ [*Breaking changes:] @@ -41,6 +61,7 @@ Provided when BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK is defined (Default See BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK and BOOST_THREAD_DONT_PROVIDE_SIGNATURE_PACKAGED_TASK. * [@http://svn.boost.org/trac/boost/ticket/7282 #7282] C++11 compliance: Add packaged_task::make_ready_at_thread_exit function +* [@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/7412 #7412] C++11 compliance: Add async from movable callable and movable arguments Provided when BOOST_THREAD_PROVIDES_VARIADIC_THREAD and BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK are defined (Default value from Boost 1.55): @@ -63,11 +84,13 @@ See BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK and BOOST_THREAD_DONT_PROVIDE_ [*Fixed Bugs:] +* [@http://svn.boost.org/trac/boost/ticket/5752 #5752] boost::call_once() is unreliable on some platforms * [@http://svn.boost.org/trac/boost/ticket/7464 #7464] BOOST_TEST(n_alive == 1); fails due to race condition in a regression test tool. * [@http://svn.boost.org/trac/boost/ticket/7657 #7657] Serious performance and memory consumption hit if condition_variable methods condition notify_one or notify_all is used repeatedly. * [@http://svn.boost.org/trac/boost/ticket/7665 #7665] this_thread::sleep_for no longer uses steady_clock in thread. * [@http://svn.boost.org/trac/boost/ticket/7668 #7668] thread_group::join_all() should check whether its threads are joinable. * [@http://svn.boost.org/trac/boost/ticket/7669 #7669] thread_group::join_all() should catch resource_deadlock_would_occur. +* [@http://svn.boost.org/trac/boost/ticket/7671 #7671] Error including boost/thread.hpp header on iOS. * [@http://svn.boost.org/trac/boost/ticket/7672 #7672] lockable_traits.hpp syntax error: "defined" token misspelled. * [@http://svn.boost.org/trac/boost/ticket/7798 #7798] boost::future set_wait_callback thread safety issues. * [@http://svn.boost.org/trac/boost/ticket/7808 #7808] Incorrect description of effects for this_thread::sleep_for and this_thread::sleep_until. @@ -75,6 +98,8 @@ See BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK and BOOST_THREAD_DONT_PROVIDE_ * [@http://svn.boost.org/trac/boost/ticket/7874 #7874] compile warning: thread.hpp:342: warning: type attributes are honored only at type definition. * [@http://svn.boost.org/trac/boost/ticket/7875 #7875] BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED should not be enabled by default. * [@http://svn.boost.org/trac/boost/ticket/7882 #7882] wrong exception text from condition_variable::wait(unique_lock&). +* [@http://svn.boost.org/trac/boost/ticket/7890 #7890] thread::do_try_join_until() is missing a return type. + [heading Version 3.1.0 - boost 1.52] diff --git a/doc/compliance.qbk b/doc/compliance.qbk index 999a9d8ca..aea1e5c83 100644 --- a/doc/compliance.qbk +++ b/doc/compliance.qbk @@ -12,7 +12,7 @@ [table C++11 standard Conformance [[Section] [Description] [Status] [Comments] [Ticket]] - [[30] [Thread support library] [Partial] [-] [-]] + [[30] [Thread support library] [Yes] [-] [-]] [[30.1] [General] [-] [-] [-]] [[30.2] [Requirements] [-] [-] [-]] [[30.2.1] [Template parameter names] [-] [-] [-]] @@ -53,16 +53,16 @@ [[30.4.2.2.3] [unique_lock modifiers] [Yes] [-] [-]] [[30.4.2.2.4] [unique_lock observers] [Yes] [] [-]] [[30.4.3] [Generic locking algorithms] [Partial] [variadic] [#6227]] - [[30.4.4] [Call once] [Partial] [call_once] [#7285]] + [[30.4.4] [Call once] [Yes] [-] [-]] [[30.4.4.1] [Struct once_flag] [Yes] [-] [-]] - [[30.4.4.2] [Function call_once] [Partial] [interface] [#7285]] + [[30.4.4.2] [Function call_once] [Yes] [-] [-]] [[30.5] [Condition variables] [Yes] [-] [-]] [[30.5.1] [Class condition_variable] [Yes] [-] [-]] [[30.5.2] [Class condition_variable_any] [Yes] [-] [-]] - [[30.6] [Futures] [Partial] [noexcept] [#7279]] + [[30.6] [Futures] [Yes] [-] [-]] [[30.6.1] [Overview] [Partial] [-] [-]] [[30.6.2] [Error handling] [Yes] [-] [-]] - [[30.6.3] [Class future_error] [Partial] [noexcept] [#7279]] + [[30.6.3] [Class future_error] [-] [-] [-]] [[30.6.4] [Shared state] [-] [-] [-]] [[30.6.5] [Class template promise] [Yes] [-] [-]] [[30.6.6] [Class template future] [Yes] [-] [-]] diff --git a/doc/configuration.qbk b/doc/configuration.qbk index 68b792f31..206eebade 100644 --- a/doc/configuration.qbk +++ b/doc/configuration.qbk @@ -31,7 +31,7 @@ [[PROVIDES_ONCE_CXX11] [DONT_PROVIDE_ONCE_CXX11] [NO] [YES] [YES] ] [[USES_MOVE] [DONT_USE_MOVE] [NO] [YES] [YES] ] - [[USES_DATETIME] [DONT_USE_DATETIME] [YES] [YES] [NO] ] + [[USES_DATETIME] [DONT_USE_DATETIME] [YES] [YES] [YES/NO] ] [[PROVIDES_THREAD_EQ] [DONT_PROVIDE_THREAD_EQ] [YES] [YES] [NO] ] [[PROVIDES_CONDITION] [DONT_PROVIDE_CONDITION] [YES] [YES] [NO] ] [[PROVIDES_NESTED_LOCKS] [DONT_PROVIDE_NESTED_LOCKS] [YES] [YES] [NO] ] @@ -69,8 +69,10 @@ The Boost.DateTime time related functions introduced in Boost 1.35.0, using the * __timed_lock_ref__ -When `BOOST_THREAD_VERSION<=3` define `BOOST_THREAD_DONT_USE_DATETIME ` if you don't want to use Boost.DateTime related interfaces. -When `BOOST_THREAD_VERSION>3` define `BOOST_THREAD_USES_DATETIME ` if you want to use Boost.DateTime related interfaces. +When `BOOST_THREAD_VERSION<=3` && defined BOOST_THREAD_PLATFORM_PTHREAD define `BOOST_THREAD_DONT_USE_DATETIME` if you don't want to use Boost.DateTime related interfaces. +When `BOOST_THREAD_VERSION>3` && defined BOOST_THREAD_PLATFORM_PTHREAD define `BOOST_THREAD_USES_DATETIME` if you want to use Boost.DateTime related interfaces. + +When defined BOOST_THREAD_PLATFORM_WIN32 BOOST_THREAD_USES_DATETIME is defined by default. [endsect] @@ -343,7 +345,7 @@ The user can request the version 3 by defining `BOOST_THREAD_VERSION` to 3. In t * Breaking change `BOOST_THREAD_DONT_PROVIDE_PROMISE_LAZY` -The default value for `BOOST_THREAD_VERSION` will be changed to 3 since Boost 1.54. +[/The default value for `BOOST_THREAD_VERSION` will be changed to 3 since Boost 1.54.] The user can request the version 4 by defining `BOOST_THREAD_VERSION` to 4. In this case the following breaking or extending macros are defined if the opposite is not requested: @@ -354,7 +356,7 @@ The user can request the version 4 by defining `BOOST_THREAD_VERSION` to 4. In t * Breaking change `BOOST_THREAD_DONT_USE_DATETIME` -The default value for `BOOST_THREAD_VERSION` will be changed to 4 since Boost 1.56. +[/The default value for `BOOST_THREAD_VERSION` will be changed to 4 since Boost 1.58.] [endsect] diff --git a/doc/internal_locking.qbk b/doc/internal_locking.qbk index eea97a281..b5def7c46 100644 --- a/doc/internal_locking.qbk +++ b/doc/internal_locking.qbk @@ -55,6 +55,9 @@ The following example includes a bank account of a person (Joe) and two componen From time to time, the `bankAgent` will deposit $500 in `JoesAccount`. Joe will similarly withdraw $100 from his account. These sentences describe that the bankAgent and Joe are executed concurrently. +[endsect] +[section Internal locking] + The above example works well as long as the bankAgent and Joe doesn't access JoesAccount at the same time. There is, however, no guarantee that this will not happen. We may use a mutex to guarantee exclusive access to each bank. class BankAccount { @@ -105,6 +108,9 @@ With the RAII idiom we can simplify a lot this using the scoped locks. In the co The object-level locking idiom doesn't cover the entire richness of a threading model. For example, the model above is quite deadlock-prone when you try to coordinate multi-object transactions. Nonetheless, object-level locking is useful in many cases, and in combination with other mechanisms can provide a satisfactory solution to many threaded access problems in object-oriented programs. +[endsect] +[section Internal and external locking] + The BankAccount class above uses internal locking. Basically, a class that uses internal locking guarantees that any concurrent calls to its public member functions don't corrupt an instance of that class. This is typically ensured by having each public member function acquire a lock on the object upon entry. This way, for any given object of that class, there can be only one member function call active at any moment, so the operations are nicely serialized. This approach is reasonably easy to implement and has an attractive simplicity. Unfortunately, "simple" might sometimes morph into "simplistic." @@ -203,6 +209,24 @@ As `boost::mutex` is not recursive, we need to use its recursive version `boost: // ... }; +The caller-ensured locking approach is more flexible and the most efficient, but very dangerous. In an implementation using caller-ensured locking, BankAccount still holds a mutex, but its member functions don't manipulate it at all. Deposit and Withdraw are not thread-safe anymore. Instead, the client code is responsible for locking BankAccount properly. + + class BankAccount + : public basic_lockable_adapter { + int balance_; + public: + void Deposit(int amount) { + balance_ += amount; + } + void Withdraw(int amount) { + balance_ -= amount; + } + }; + +Obviously, the caller-ensured locking approach has a safety problem. BankAccount's implementation code is finite, and easy to reach and maintain, but there's an unbounded amount of client code that manipulates BankAccount objects. In designing applications, it's important to differentiate between requirements imposed on bounded code and unbounded code. If your class makes undue requirements on unbounded code, that's usually a sign that encapsulation is out the window. + +To conclude, if in designing a multi-threaded class you settle on internal locking, you expose yourself to inefficiency or deadlocks. On the other hand, if you rely on caller-provided locking, you make your class error-prone and difficult to use. Finally, external locking completely avoids the issue by leaving it all to the client code. + [endsect] @@ -440,11 +464,6 @@ Monitors and conditions are useful for describing simple cases of shared objects [endsect] [/Monitors] ] -[section Synchronized variables] -[/include synchronized_value.qbk] -[endsect] [/Synchronized variables] - - [endsect] [/Internal Locking] diff --git a/doc/lockable_adapter.qbk b/doc/lockable_adapter.qbk new file mode 100644 index 000000000..b93f0e057 --- /dev/null +++ b/doc/lockable_adapter.qbk @@ -0,0 +1,117 @@ +[/ + (C) Copyright 2008-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:lockable_adapter_hpp Header ``] +[/==========================================================================================] + + namespace boost { + template class basic_lockable_adapter; + template class lockable_adapter; + template class timed_lockable_adapter; + template class shared_lockable_adapter; + template class upgrade_lockable_adapter; + } + +[section Template Class `basic_lockable_adapter<>`] + + template + class basic_lockable_adapter { + public: + basic_lockable_adapter(basic_lockable_adapter const&) = delete + basic_lockable_adapter& opearator=(basic_lockable_adapter const&) = delete + + typedef Lockable mutex_type; + + basic_lockable_adapter() {} + void lock(); + void unlock(); + bool try_lock(); + }; + +[endsect] +[section Template Class `lockable_adapter<>`] + + template + class lockable_adapter : : public basic_lockable_adapter { + public: + lockable_adapter() {} + + bool try_lock(); + }; + +[endsect] + +[section Template Class `timed_lockable_adapter<>`] + + template + class timed_lockable_adapter : public lockable_adapter { + public: + timed_lockable_adapter() {} + + bool try_lock_until(system_time const & abs_time); + template + bool try_lock_for(TimeDuration const & relative_time); + + void lock_until(system_time const & abs_time); + template + void lock_for(TimeDuration const & relative_time); + }; + +[endsect] +[section Template Class `shared_lockable_adapter<>`] + + template + class shared_lockable_adapter : public timed_lockable_adapter { + public: + shared_lockable_adapter() {} + void lock_shared(); + bool try_lock_shared(); + void unlock_shared(); + + bool try_lock_shared_until(system_time const& t); + template + bool try_lock_shared_for(TimeDuration const& t); + + template + void lock_shared_for(TimeDuration const& t); + void lock_shared_until(system_time const& t); + }; + + +[endsect] +[section Template Class `upgrade_lockable_adapter<>`] + + template + class upgrade_lockable_adapter : public shared_lockable_adapter{ + public: + upgrade_lockable_adapter(); + + void lock_upgrade(); + bool try_lock_upgrade(); + void unlock_upgrade(); + + void unlock_upgrade_and_lock(); + void unlock_and_lock_upgrade(); + void unlock_and_lock_shared(); + void unlock_upgrade_and_lock_shared(); + + bool try_lock_upgrade_until(system_time const&t); + template + bool try_lock_upgrade_for(TimeDuration const&t); + void lock_upgrade_until(system_time const&t); + template + void lock_upgrade_for(TimeDuration const&t); + }; + + + +[endsect] +[endsect] + + + diff --git a/doc/mutex_concepts.qbk b/doc/mutex_concepts.qbk index a303a9697..8f5755982 100644 --- a/doc/mutex_concepts.qbk +++ b/doc/mutex_concepts.qbk @@ -1134,7 +1134,7 @@ As the semantic "ensures that the associated mutex is locked during the lifetime The following classes are models of `StrictLock`: * strict_lock: ensured by construction, -* nested_strict_lock: ensured by construction, +* nested_strict_lock: "sur parolle" as the user could use adopt_lock_t on unique_lock constructor overload without having locked the mutex, * __lock_guard__: "sur parolle" as the user could use adopt_lock_t constructor overload without having locked the mutex. [endsect] [/ Models] @@ -2002,6 +2002,8 @@ __reverse_mutex reverse the operations of a __BasicLockable, that unlocks the lo { public: typedef BasicLockable mutex_type; + strict_lock(strict_lock const& m_) = delete; + strict_lock& operator=(strict_lock const& m_) = delete; explicit strict_lock(mutex_type& m_); ~strict_lock(); @@ -2052,6 +2054,8 @@ object passed to the constructor.]] { public: typedef BasicLockable mutex_type; + nested_strict_lock(nested_strict_lock const& m_) = delete; + nested_strict_lock& operator=(nested_strict_lock const& m_) = delete; explicit nested_strict_lock(Lock& lk), ~nested_strict_lock() noexcept; @@ -2154,6 +2158,235 @@ If the lock doesn't owns the mutex lock it. [endsect] +[section:lock_ptrs Locking pointers] + + // #include + // #include + + namespace boost + { + + template + class strict_lock_ptr; + template + class const_strict_lock_ptr; + } + + +[/ + template + class unique_lock_ptr; + template + class const_unique_lock_ptr; + +] + +[section:const_strict_lock_ptr Class template `const_strict_lock_ptr `] + + // #include + // #include + + + template + class const_strict_lock_ptr + { + public: + typedef T value_type; + typedef Lockable mutex_type; + + const_strict_lock_ptr(const_strict_lock_ptr const& m_) = delete; + const_strict_lock_ptr& operator=(const_strict_lock_ptr const& m_) = delete; + + const_strict_lock_ptr(T const& val, Lockable & mtx); + const_strict_lock_ptr(T const& val, Lockable & mtx, adopt_lock_t tag); + + ~const_strict_lock_ptr(); + + const T* operator->() const; + const T& operator*() const; + + }; + + +[section:constructor `const_strict_lock_ptr(T const&,Lockable&)`] + + + const_strict_lock_ptr(T const& val, Lockable & m); + +[variablelist + +[[Effects:] [Invokes [lock_ref_link `m.lock()`], stores a reference to it and to the value type `val`.]] + +[[Throws:] [Any exception thrown by the call to [lock_ref_link `m.lock()`].]] + +] + +[endsect] +[section:constructor_adopt `const_strict_lock_ptr(T const&,Lockable&,adopt_lock_t)`] + + const_strict_lock_ptr(T const& val, Lockable & m, adopt_lock_t tag); + +[variablelist + +[[Effects:] [Stores a reference to it and to the value type `val`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + + +[section:destructor `~const_strict_lock_ptr()`] + + ~const_strict_lock_ptr(); + +[variablelist + +[[Effects:] [Invokes [unlock_ref_link `m.unlock()`] on the __lockable_concept_type__ +object passed to the constructor.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:indir `operator->() const`] + + const T* operator->() const; + + +[variablelist + +[[Return:] [return a constant pointer to the protected value.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:deref `operator*() const`] + + const T& operator*() const; + + +[variablelist + +[[Return:] [return a constant reference to the protected value.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + + +[endsect] [/ const_strict_lock_ptr ] + +[section:strict_lock_ptr Class template `strict_lock_ptr`] + + // #include + // #include + + template + class strict_lock_ptr : public const_strict_lock_ptr + { + public: + strict_lock_ptr(strict_lock_ptr const& m_) = delete; + strict_lock_ptr& operator=(strict_lock_ptr const& m_) = delete; + + strict_lock_ptr(T & val, Lockable & mtx); + strict_lock_ptr(T & val, Lockable & mtx, adopt_lock_t tag); + ~strict_lock_ptr(); + + T* operator->(); + T& operator*(); + + }; + + +[section:constructor `strict_lock_ptr(T const&,Lockable&)`] + + + strict_lock_ptr(T const& val, Lockable & m); + +[variablelist + +[[Effects:] [Invokes [lock_ref_link `m.lock()`], stores a reference to it and to the value type `val`.]] + +[[Throws:] [Any exception thrown by the call to [lock_ref_link `m.lock()`].]] + +] + +[endsect] +[section:constructor_adopt `strict_lock_ptr(T const&,Lockable&,adopt_lock_t)`] + + strict_lock_ptr(T const& val, Lockable & m, adopt_lock_t tag); + +[variablelist + +[[Effects:] [Stores a reference to it and to the value type `val`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + + +[section:destructor `~strict_lock_ptr()`] + + ~ strict_lock_ptr(); + +[variablelist + +[[Effects:] [Invokes [unlock_ref_link `m.unlock()`] on the __lockable_concept_type__ +object passed to the constructor.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:indir `operator->()`] + + T* operator->(); + + +[variablelist + +[[Return:] [return a pointer to the protected value.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:deref `operator*()`] + + T& operator*(); + + +[variablelist + +[[Return:] [return a reference to the protected value.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + + +[endsect] [/ strict_lock_ptr ] + +[endsect] [/ lock_ptrs ] + + [section Externally Locked] // #include diff --git a/doc/mutexes.qbk b/doc/mutexes.qbk index 0dfd97473..48cba6fab 100644 --- a/doc/mutexes.qbk +++ b/doc/mutexes.qbk @@ -239,3 +239,410 @@ implementation. If no such instance exists, `native_handle()` and `native_handle [include shared_mutex_ref.qbk] [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 dc960999e..d5a398b3d 100644 --- a/doc/once.qbk +++ b/doc/once.qbk @@ -12,8 +12,8 @@ namespace boost { struct once_flag; - template - void call_once(once_flag& flag,Callable func); + template + inline void call_once(once_flag& flag, Function&& f, ArgTypes&&... args); #if defined BOOST_THREAD_PROVIDES_DEPRECATED_FEATURES_SINCE_V3_0_0 void call_once(void (*func)(),once_flag& flag); @@ -45,24 +45,22 @@ Objects of type `boost::once_flag` shall be initialized with `BOOST_ONCE_INIT` i [section:call_once Non-member function `call_once`] - template - void call_once(once_flag& flag,Callable func); + template + inline void call_once(once_flag& flag, Function&& f, ArgTypes&&... args); [variablelist -[[Requires:] [`Callable` is `CopyConstructible`. Copying `func` shall have no side effects, and the effect of calling the copy shall -be equivalent to calling the original. ]] +[[Requires:] [`Function` and each or the `ArgTypes` are `MoveConstructible` and `invoke(decay_copy(boost::forward(f)), decay_copy(boost::forward(args))...)` shall be well formed. ]] [[Effects:] [Calls to `call_once` on the same `once_flag` object are serialized. If there has been no prior effective `call_once` on -the same `once_flag` object, the argument `func` (or a copy thereof) is called as-if by invoking `func()`, and the invocation of -`call_once` is effective if and only if `func()` returns without exception. If an exception is thrown, the exception is -propagated to the caller. If there has been a prior effective `call_once` on the same `once_flag` object, the `call_once` returns +the same `once_flag` object, the argument `func` is called as-if by invoking `invoke(decay_copy(boost::forward(f)), decay_copy(boost::forward(args))...)`, and the invocation of +`call_once` is effective if and only if `invoke(decay_copy(boost::forward(f)), decay_copy(boost::forward(args))...)` returns without exception. If an exception is thrown, the exception is propagated to the caller. If there has been a prior effective `call_once` on the same `once_flag` object, the `call_once` returns without invoking `func`. ]] [[Synchronization:] [The completion of an effective `call_once` invocation on a `once_flag` object, synchronizes with all subsequent `call_once` invocations on the same `once_flag` object. ]] -[[Throws:] [`thread_resource_error` when the effects cannot be achieved. or any exception propagated from `func`.]] +[[Throws:] [`thread_resource_error` when the effects cannot be achieved or any exception propagated from `func`.]] [[Note:] [The function passed to `call_once` must not also call `call_once` passing the same `once_flag` object. This may cause @@ -77,7 +75,7 @@ recursively.]] void call_once(void (*func)(),once_flag& flag); -This second overload is provided for backwards compatibility. The effects of `call_once(func,flag)` shall be the same as those of +This second overload is provided for backwards compatibility and is deprecated. The effects of `call_once(func,flag)` shall be the same as those of `call_once(flag,func)`. [endsect] diff --git a/doc/sync_tutorial.qbk b/doc/sync_tutorial.qbk index 15e4044e8..52e0fe2c0 100644 --- a/doc/sync_tutorial.qbk +++ b/doc/sync_tutorial.qbk @@ -18,6 +18,8 @@ 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 new file mode 100644 index 000000000..b77bfc420 --- /dev/null +++ b/doc/synchronized_value.qbk @@ -0,0 +1,140 @@ +[/ + / 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 Synchronized values] +[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] + +The key problem with protecting shared data with a mutex is that there is no easy way to associate the mutex with the data. It is thus relatively easy to accidentally write code that fails to lock the right mutex - or even locks the wrong mutex - and the compiler will not help you. + + + std::mutex m1; + int value1; + std::mutex m2; + int value2; + + int readValue1() + { + boost::lock_guard lk(m1); + return value1; + } + int readValue2() + { + boost::lock_guard lk(m1); // oops: wrong mutex + return value2; + } + +Moreover, managing the mutex lock also clutters the source code, making it harder to see what is really going on. + +The use of synchronized_value solves both these problems - the mutex is intimately tied to the value, so you cannot access it without a lock, and yet access semantics are still straightforward. For simple accesses, synchronized_value behaves like a pointer-to-T; for example: + + + boost::synchronized_value value3; + std::string readValue3() + { + return *value3; + } + void setValue3(std::string const& newVal) + { + *value3=newVal; + } + void appendToValue3(std::string const& extra) + { + value3->append(extra); + } + +Both forms of pointer dereference return a proxy object rather than a real reference, to ensure that the lock on the mutex is held across the assignment or method call, but this is transparent to the user. + +[endsect] [/The Problem with Mutexes] + +[section Beyond Simple Accesses] + +The pointer-like semantics work very well for simple accesses such as assignment and calls to member functions. However, sometimes you need to perform an operation that requires multiple accesses under protection of the same lock, and that's what the synchronize() method provides. + +By calling synchronize() you obtain an strict_lock_ptr object that holds a lock on the mutex protecting the data, and which can be used to access the protected data. The lock is held until the strict_lock_ptr object is destroyed, so you can safely perform multi-part operations. The strict_lock_ptr object also acts as a pointer-to-T, just like synchronized_value does, but this time the lock is already held. For example, the following function adds a trailing slash to a path held in a synchronized_value. The use of the strict_lock_ptr object ensures that the string hasn't changed in between the query and the update. + + void addTrailingSlashIfMissing(boost::synchronized_value & path) + { + boost::strict_lock_ptr u=path.synchronize(); + + if(u->empty() || (*u->rbegin()!='/')) + { + *u+='/'; + } + } + +[endsect] [/Beyond Simple Accesses] + +[section Operations Across Multiple Objects] + +Though synchronized_value works very well for protecting a single object of type T, nothing that we've seen so far solves the problem of operations that require atomic access to multiple objects unless those objects can be combined within a single structure protected by a single mutex. + +One way to protect access to two synchronized_value objects is to construct a strict_lock_ptr for each object and use those to access the respective protected values; for instance: + + synchronized_value > q1,q2; + void transferMessage() + { + strict_lock_ptr > u1 = q1.synchronize(); + strict_lock_ptr > u2 = q2.synchronize(); + + if(!u1->empty()) + { + u2->push_back(u1->front()); + u1->pop_front(); + } + } + +This works well in some scenarios, but not all -- if the same two objects are updated together in different sections of code then you need to take care to ensure that the strict_lock_ptr objects are constructed in the same sequence in all cases, otherwise you have the potential for deadlock. This is just the same as when acquiring any two mutexes. + +In order to be able to use the dead-lock free lock algorithms we need to use instead unique_lock_ptr, which is Lockable. + + synchronized_value > q1,q2; + void transferMessage() + { + unique_lock_ptr > u1 = q1.unique_synchronize(boost::defer_lock); + unique_lock_ptr > u2 = q2.unique_synchronize(boost::defer_lock); + boost::lock(u1,u2); // dead-lock free algorithm + + if(!u1->empty()) + { + u2->push_back(u1->front()); + u1->pop_front(); + } + } + +While the preceding takes care of dead-lock, the access to the synchronized_value via unique_lock_ptr requires a lock that is not forced by the interface. +An alternative on compilers providing a standard library that supports movable std::tuple is to use the free synchronize function, which will lock all the mutexes associated to the synchronized values and return a tuple os strict_lock_ptr. + + synchronized_value > q1,q2; + void transferMessage() + { + auto lks = synchronize(u1,u2); // dead-lock free algorithm + + if(!std::get<1>(lks)->empty()) + { + std::get<2>(lks)->push_back(u1->front()); + std::get<1>(lks)->pop_front(); + } + } + + +[endsect] [/Operations Across Multiple Objects] + +[section Value semantics] + +synchronized_value has value semantics even if the syntax lets is close to a pointer (this is just because we are unable to define smart references). + +[endsect] [/Value semantics] + + +[endsect] [/Synchronized variables] + + + + diff --git a/doc/thread.qbk b/doc/thread.qbk index efb0553b4..4389cfbff 100644 --- a/doc/thread.qbk +++ b/doc/thread.qbk @@ -235,6 +235,7 @@ [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] diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk index 12a624d0a..9deaabdce 100644 --- a/doc/thread_ref.qbk +++ b/doc/thread_ref.qbk @@ -201,7 +201,7 @@ __thread_interrupted__, `std::terminate()` is called. [endsect] -[section:detac Detaching thread] +[section:detach Detaching thread] A thread can be detached by explicitly invoking the __detach__ member function on the __thread__ object. In this case, the __thread__ object ceases to represent the now-detached thread, and instead represents __not_a_thread__. @@ -1708,7 +1708,6 @@ registered with `at_thread_exit()`]] [section:threadgroup Class `thread_group` EXTENSION] #include - #include class thread_group { diff --git a/example/make_future.cpp b/example/make_future.cpp index e9fd587ed..e89fecec3 100644 --- a/example/make_future.cpp +++ b/example/make_future.cpp @@ -10,6 +10,15 @@ int p1() { return 5; } +void p() { } + +#if defined BOOST_THREAD_USES_MOVE +boost::future void_compute() +{ + return BOOST_THREAD_MAKE_RV_REF(boost::make_future()); +} +#endif + boost::future compute(int x) { if (x == 0) return boost::make_future(0); @@ -30,10 +39,20 @@ boost::shared_future shared_compute(int x) int main() { +#if defined BOOST_THREAD_USES_MOVE + { + boost::future f = void_compute(); + f.get(); + } +#endif { boost::future f = compute(2); std::cout << f.get() << std::endl; } + { + boost::future f = compute(0); + std::cout << f.get() << std::endl; + } { boost::shared_future f = shared_compute(2); std::cout << f.get() << std::endl; diff --git a/example/synchronized_person.cpp b/example/synchronized_person.cpp index c7836854c..7930c0b00 100644 --- a/example/synchronized_person.cpp +++ b/example/synchronized_person.cpp @@ -255,5 +255,28 @@ int main() lk2->SetName("Javier"); lk3->SetName("Matias"); } +#if ! defined BOOST_NO_CXX11_AUTO_DECLARATIONS \ +&& ! defined(BOOST_THREAD_NO_SYNCHRONIZE) + { + Person3_ts p1(1); + Person3_ts p2(2); + Person3_ts p3(3); + + auto t = boost::synchronize(p1,p2,p3); + std::get<0>(t)->SetName("Carmen"); + std::get<1>(t)->SetName("Javier"); + std::get<2>(t)->SetName("Matias"); + } + { + const Person3_ts p1(1); + Person3_ts p2(2); + const Person3_ts p3(3); + + auto t = boost::synchronize(p1,p2,p3); + //std::get<0>(t)->SetName("Carmen"); + std::get<1>(t)->SetName("Javier"); + //std::get<2>(t)->SetName("Matias"); + } +#endif return 0; } diff --git a/example/synchronized_value.cpp b/example/synchronized_value.cpp index 04c7b7353..61480a57c 100644 --- a/example/synchronized_value.cpp +++ b/example/synchronized_value.cpp @@ -10,15 +10,15 @@ #include #include -void addTrailingSlashIfMissing(boost::synchronized_value & path) -{ - boost::strict_lock_ptr u=path.synchronize(); - - if(u->empty() || (*u->rbegin()!='/')) + void addTrailingSlashIfMissing(boost::synchronized_value & path) { - *u+='/'; + boost::strict_lock_ptr u=path.synchronize(); + + if(u->empty() || (*u->rbegin()!='/')) + { + *u+='/'; + } } -} void f(const boost::synchronized_value &v) { std::cout<<"v="<<*v< s2; + s2=s1; + std::cout<<"s1="<< s1 << std::endl; + std::cout<<"s2="<< s2 << std::endl; + } + { + boost::synchronized_value s1("a"); + boost::synchronized_value s2("b"); + std::cout<<"s1="<< s1 << std::endl; + std::cout<<"s2="<< s2 << std::endl; + swap(s1,s2); + std::cout<<"s1="<< s1 << std::endl; + std::cout<<"s2="<< s2 << std::endl; + } +#if ! defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) + { + boost::synchronized_value sts("a"); + std::string s(sts); + std::cout<<"ssts="<< s << std::endl; + } +#endif + { + boost::synchronized_value s1(1); + boost::synchronized_value s2(1); + BOOST_ASSERT(s1==s2); + BOOST_ASSERT(s1<=s2); + BOOST_ASSERT(s1>=s2); + BOOST_ASSERT(s1==1); + BOOST_ASSERT(s1<=1); + BOOST_ASSERT(s1>=1); + } + { + boost::synchronized_value s1(1); + boost::synchronized_value s2(2); + BOOST_ASSERT(s1!=s2); + BOOST_ASSERT(s1!=2); + BOOST_ASSERT(2!=s1); + } + { + boost::synchronized_value s1(1); + boost::synchronized_value s2(2); + BOOST_ASSERT(s1s1); + BOOST_ASSERT(s2>=s1); + BOOST_ASSERT(s1<2); + BOOST_ASSERT(s1<=2); + BOOST_ASSERT(s2>1); + BOOST_ASSERT(s2>=1); + } return 0; } diff --git a/include/boost/thread/detail/config.hpp b/include/boost/thread/detail/config.hpp index 22dfb709a..3d19d3839 100644 --- a/include/boost/thread/detail/config.hpp +++ b/include/boost/thread/detail/config.hpp @@ -1,6 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf -// Copyright (C) 2011-2012 Vicente J. Botet Escriba +// Copyright (C) 2011-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) @@ -8,15 +8,27 @@ #ifndef BOOST_THREAD_CONFIG_WEK01032003_HPP #define BOOST_THREAD_CONFIG_WEK01032003_HPP -// Force SIG_ATOMIC_MAX to be defined -//#ifndef __STDC_LIMIT_MACROS -//#define __STDC_LIMIT_MACROS -//#endif - #include #include #include +//#define BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS + + +// ATTRIBUTE_MAY_ALIAS + +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) > 302 \ + && !defined(__INTEL_COMPILER) + + // GCC since 3.3 has may_alias attribute that helps to alleviate optimizer issues with + // regard to violation of the strict aliasing rules. + + #define BOOST_THREAD_DETAIL_USE_ATTRIBUTE_MAY_ALIAS + #define BOOST_THREAD_ATTRIBUTE_MAY_ALIAS __attribute__((__may_alias__)) +#else + #define BOOST_THREAD_ATTRIBUTE_MAY_ALIAS +#endif + #if ! defined BOOST_THREAD_NOEXCEPT_OR_THROW #ifdef BOOST_NO_CXX11_NOEXCEPT # define BOOST_THREAD_NOEXCEPT_OR_THROW throw() @@ -44,8 +56,8 @@ #if defined __IBMCPP__ && (__IBMCPP__ < 1100) \ && ! defined BOOST_THREAD_DONT_USE_CHRONO #define BOOST_THREAD_DONT_USE_CHRONO -#if ! defined BOOST_THREAD_USE_DATE -#define BOOST_THREAD_USE_DATE +#if ! defined BOOST_THREAD_USES_DATETIME +#define BOOST_THREAD_USES_DATETIME #endif #endif @@ -76,13 +88,14 @@ #if defined(BOOST_NO_CXX11_HDR_TUPLE) || defined(BOOST_NO_CXX11_RVALUE_REFERENCES) #define BOOST_THREAD_NO_MAKE_UNIQUE_LOCKS +#define BOOST_THREAD_NO_SYNCHRONIZE #elif defined _MSC_VER && _MSC_VER <= 1600 // C++ features supported by VC++ 10 (aka 2010) #define BOOST_THREAD_NO_MAKE_UNIQUE_LOCKS +#define BOOST_THREAD_NO_SYNCHRONIZE #endif /// BASIC_THREAD_ID -// todo to be removed for 1.54 #if ! defined BOOST_THREAD_DONT_PROVIDE_BASIC_THREAD_ID \ && ! defined BOOST_THREAD_PROVIDES_BASIC_THREAD_ID #define BOOST_THREAD_PROVIDES_BASIC_THREAD_ID @@ -111,9 +124,7 @@ #if ! defined BOOST_THREAD_DONT_USE_ATOMIC \ && ! defined BOOST_THREAD_USES_ATOMIC -//#if ! defined __PGIC__ #define BOOST_THREAD_USES_ATOMIC -//#endif //#define BOOST_THREAD_DONT_USE_ATOMIC #endif @@ -246,6 +257,16 @@ #endif #endif + +// MAKE_READY_AT_THREAD_EXIT +#if ! defined BOOST_THREAD_PROVIDES_MAKE_READY_AT_THREAD_EXIT \ + && ! defined BOOST_THREAD_DONT_PROVIDE_MAKE_READY_AT_THREAD_EXIT + +//#if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK && defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD) +#define BOOST_THREAD_PROVIDES_MAKE_READY_AT_THREAD_EXIT +//#endif +#endif + // FUTURE_CONTINUATION #if ! defined BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION \ && ! defined BOOST_THREAD_DONT_PROVIDE_FUTURE_CONTINUATION @@ -301,8 +322,12 @@ #endif #endif +#if defined(BOOST_THREAD_PLATFORM_WIN32) && defined BOOST_THREAD_DONT_USE_DATETIME +#undef BOOST_THREAD_DONT_USE_DATETIME +#define BOOST_THREAD_USES_DATETIME +#endif -// BOOST_THREAD_PROVIDES_DEPRECATED_FEATURES_SINCE_V3_0_0 defined by default up to Boost 1.52 +// BOOST_THREAD_PROVIDES_DEPRECATED_FEATURES_SINCE_V3_0_0 defined by default up to Boost 1.55 // BOOST_THREAD_DONT_PROVIDE_DEPRECATED_FEATURES_SINCE_V3_0_0 defined by default up to Boost 1.55 #if defined BOOST_THREAD_PROVIDES_DEPRECATED_FEATURES_SINCE_V3_0_0 diff --git a/include/boost/thread/detail/invoke.hpp b/include/boost/thread/detail/invoke.hpp index bd9622187..f1616cb23 100644 --- a/include/boost/thread/detail/invoke.hpp +++ b/include/boost/thread/detail/invoke.hpp @@ -18,6 +18,7 @@ #include #include +#include #ifndef BOOST_NO_CXX11_HDR_FUNCTIONAL #include #endif @@ -27,12 +28,12 @@ namespace boost namespace detail { +#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + #if ! defined(BOOST_NO_SFINAE_EXPR) && \ - ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && \ ! defined(BOOST_NO_CXX11_DECLTYPE) && \ ! defined(BOOST_NO_CXX11_DECLTYPE_N3276) && \ - ! defined(BOOST_NO_CXX11_AUTO) && \ - ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + ! defined(BOOST_NO_CXX11_AUTO) #define BOOST_THREAD_PROVIDES_INVOKE @@ -41,7 +42,7 @@ namespace boost template inline auto - invoke(Fp&& f, A0&& a0, Args&& ...args) + invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0, BOOST_THREAD_RV_REF(Args) ...args) -> decltype((boost::forward(a0).*f)(boost::forward(args)...)) { return (boost::forward(a0).*f)(boost::forward(args)...); @@ -50,7 +51,7 @@ namespace boost template inline auto - invoke(Fp&& f, A0&& a0, Args&& ...args) + invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0, BOOST_THREAD_RV_REF(Args) ...args) -> decltype(((*boost::forward(a0)).*f)(boost::forward(args)...)) { return ((*boost::forward(a0)).*f)(boost::forward(args)...); @@ -61,7 +62,7 @@ namespace boost template inline auto - invoke(Fp&& f, A0&& a0) + invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0) -> decltype(boost::forward(a0).*f) { return boost::forward(a0).*f; @@ -70,7 +71,7 @@ namespace boost template inline auto - invoke(Fp&& f, A0&& a0) + invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0) -> decltype((*boost::forward(a0)).*f) { return (*boost::forward(a0)).*f; @@ -80,20 +81,17 @@ namespace boost template inline - auto invoke(Fp&& f, Args&& ...args) + auto invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(Args) ...args) -> decltype(boost::forward(f)(boost::forward(args)...)) { return boost::forward(f)(boost::forward(args)...); } -#else -#if ! defined(BOOST_NO_SFINAE_EXPR) && \ - ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && \ - ! defined BOOST_NO_CXX11_HDR_FUNCTIONAL && \ - ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +#elif ! defined(BOOST_NO_SFINAE_EXPR) && \ + ! defined BOOST_NO_CXX11_HDR_FUNCTIONAL template inline - Ret invoke(Fp&& f, Args&& ...args) + Ret invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(Args) ...args) { return std::bind(boost::forward(f), boost::forward(args)...)(); } @@ -102,7 +100,152 @@ namespace boost #endif +#else //! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + +#if ! defined(BOOST_NO_SFINAE_EXPR) && \ + ! defined(BOOST_NO_CXX11_DECLTYPE) && \ + ! defined(BOOST_NO_CXX11_DECLTYPE_N3276) && \ + ! defined(BOOST_NO_CXX11_AUTO) + +#define BOOST_THREAD_PROVIDES_INVOKE + + // // bullets 1 and 2 + + template + inline + auto + invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0) + -> decltype((boost::forward(a0).*f)()) + { + return (boost::forward(a0).*f)(); + } + template + inline + auto + invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0, BOOST_THREAD_RV_REF(A1) a1) + -> decltype((boost::forward(a0).*f)(boost::forward(a1))) + { + return (boost::forward(a0).*f)(boost::forward(a1)); + } + template + inline + auto + invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0, BOOST_THREAD_RV_REF(A1) a1, BOOST_THREAD_RV_REF(A2) a2) + -> decltype((boost::forward(a0).*f)(boost::forward(a1), boost::forward(a2))) + { + return (boost::forward(a0).*f)(boost::forward(a1), boost::forward(a2)); + } + + template + inline + auto + invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0) + -> decltype(((*boost::forward(a0)).*f)()) + { + return ((*boost::forward(a0)).*f)(); + } + template + inline + auto + invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0, BOOST_THREAD_RV_REF(A1) a1) + -> decltype(((*boost::forward(a0)).*f)(boost::forward(a1))) + { + return ((*boost::forward(a0)).*f)(boost::forward(a1)); + } + template + inline + auto + invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0, BOOST_THREAD_RV_REF(A1) a1, BOOST_THREAD_RV_REF(A2) a2) + -> decltype(((*boost::forward(a0)).*f)(boost::forward(a1), boost::forward(a2))) + { + return ((*boost::forward(a0)).*f)(boost::forward(a1), boost::forward(a2)); + } + + // bullets 3 and 4 + + template + inline + auto + invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0) + -> decltype(boost::forward(a0).*f) + { + return boost::forward(a0).*f; + } + + template + inline + auto + invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0) + -> decltype((*boost::forward(a0)).*f) + { + return (*boost::forward(a0)).*f; + } + + // bullet 5 + + template + inline + auto invoke(BOOST_THREAD_RV_REF(Fp) f) + -> decltype(boost::forward(f)()) + { + return boost::forward(f)(); + } + template + inline + auto invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(Args) a1) + -> decltype(boost::forward(f)(boost::forward(a1))) + { + return boost::forward(f)(boost::forward(a1)); + } template + inline + auto invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A1) a1, BOOST_THREAD_RV_REF(A2) a2) + -> decltype(boost::forward(f)(boost::forward(a1), boost::forward(a2))) + { + return boost::forward(f)(boost::forward(a1), boost::forward(a2)); + } + template + inline + auto invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A1) a1, BOOST_THREAD_RV_REF(A2) a2, BOOST_THREAD_RV_REF(A3) a3) + -> decltype(boost::forward(f)(boost::forward(a1), boost::forward(a2), boost::forward(a3))) + { + return boost::forward(f)(boost::forward(a1), boost::forward(a2), boost::forward(a3)); + } + + +#elif ! defined(BOOST_NO_SFINAE_EXPR) && \ + ! defined BOOST_NO_CXX11_HDR_FUNCTIONAL + + template + inline + Ret invoke(BOOST_THREAD_RV_REF(Fp) f) + { + return f(); + } + template + inline + Ret invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A1) a1) + { + return std::bind(boost::forward(f), boost::forward(a1))(); + } + template + inline + Ret invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A1) a1, BOOST_THREAD_RV_REF(A2) a2) + { + return std::bind(boost::forward(f), boost::forward(a1), boost::forward(a2))(); + } + template + inline + Ret invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A1) a1, BOOST_THREAD_RV_REF(A2) a2, BOOST_THREAD_RV_REF(A3) a3) + { + return std::bind(boost::forward(f), boost::forward(a1), boost::forward(a2), boost::forward(a3))(); + } + +#define BOOST_THREAD_PROVIDES_INVOKE_RET + #endif + + +#endif // ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) } } diff --git a/include/boost/thread/future.hpp b/include/boost/thread/future.hpp index 7ce8789bc..ef9a74f1d 100644 --- a/include/boost/thread/future.hpp +++ b/include/boost/thread/future.hpp @@ -1677,7 +1677,11 @@ namespace boost boost::throw_exception(future_already_retrieved()); } future_obtained=true; - return BOOST_THREAD_FUTURE(future_); + return + //BOOST_THREAD_MAKE_RV_REF( + BOOST_THREAD_FUTURE(future_) + //) + ; } void set_value(typename detail::future_traits::source_reference_type r) @@ -1845,6 +1849,7 @@ namespace boost } future_obtained=true; return BOOST_THREAD_FUTURE(future_); + //return BOOST_THREAD_MAKE_RV_REF(BOOST_THREAD_FUTURE(future_)); } void set_value(R& r) @@ -1990,6 +1995,9 @@ namespace boost } future_obtained=true; return BOOST_THREAD_FUTURE(future_); + //return BOOST_THREAD_MAKE_RV_REF(BOOST_THREAD_FUTURE(future_)); + //BOOST_THREAD_FUTURE res; + //return boost::move(res); } void set_value() @@ -2938,8 +2946,8 @@ namespace boost else if(!future_obtained) { future_obtained=true; - //return BOOST_THREAD_MAKE_RV_REF(BOOST_THREAD_FUTURE(task)); return BOOST_THREAD_FUTURE(task); + //return BOOST_THREAD_MAKE_RV_REF(BOOST_THREAD_FUTURE(task)); } else { @@ -3139,12 +3147,12 @@ namespace boost if (int(policy) & int(launch::async)) { #if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK && defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD) - return boost::detail::make_future_async_object( + return BOOST_THREAD_MAKE_RV_REF(boost::detail::make_future_async_object( BF( thread_detail::decay_copy(boost::forward(f)) , thread_detail::decay_copy(boost::forward(args))... ) - ); + )); #else packaged_task_type pt( boost::forward(f) ); @@ -3160,12 +3168,12 @@ namespace boost else if (int(policy) & int(launch::deferred)) { #if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK && defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD) - return boost::detail::make_future_deferred_object( + return BOOST_THREAD_MAKE_RV_REF(boost::detail::make_future_deferred_object( BF( thread_detail::decay_copy(boost::forward(f)) , thread_detail::decay_copy(boost::forward(args))... ) - ); + )); #else BOOST_THREAD_FUTURE ret; return ::boost::move(ret); @@ -3194,14 +3202,14 @@ namespace boost BOOST_THREAD_FUTURE async(R(*f)(BOOST_THREAD_FWD_REF(ArgTypes)...), BOOST_THREAD_FWD_REF(ArgTypes)... args) { - return async(launch(launch::any), f, boost::forward(args)...); + return BOOST_THREAD_MAKE_RV_REF(async(launch(launch::any), f, boost::forward(args)...)); } #else template BOOST_THREAD_FUTURE async(R(*f)()) { - return async(launch(launch::any), f); + return BOOST_THREAD_MAKE_RV_REF(async(launch(launch::any), f)); } #endif #endif @@ -3213,14 +3221,14 @@ namespace boost )>::type> async(BOOST_THREAD_FWD_REF(F) f, BOOST_THREAD_FWD_REF(ArgTypes)... args) { - return async(launch(launch::any), boost::forward(f), boost::forward(args)...); + return BOOST_THREAD_MAKE_RV_REF(async(launch(launch::any), boost::forward(f), boost::forward(args)...)); } #else template BOOST_THREAD_FUTURE::type> async(BOOST_THREAD_RV_REF(F) f) { - return async(launch(launch::any), boost::forward(f)); + return BOOST_THREAD_MAKE_RV_REF(async(launch(launch::any), boost::forward(f))); } #endif @@ -3234,16 +3242,17 @@ namespace boost typedef typename decay::type future_type; promise p; p.set_value(boost::forward(value)); - return p.get_future(); + return BOOST_THREAD_MAKE_RV_REF(p.get_future()); } - +#if defined BOOST_THREAD_USES_MOVE inline BOOST_THREAD_FUTURE make_future() { promise p; - return p.get_future(); - + p.set_value(); + return BOOST_THREAD_MAKE_RV_REF(p.get_future()); } +#endif //////////////////////////////// // make_shared_future @@ -3254,14 +3263,14 @@ namespace boost typedef typename decay::type future_type; promise p; p.set_value(boost::forward(value)); - return p.get_future().share(); + return BOOST_THREAD_MAKE_RV_REF(p.get_future().share()); } inline shared_future make_shared_future() { promise p; - return p.get_future().share(); + return BOOST_THREAD_MAKE_RV_REF(p.get_future().share()); } @@ -3397,10 +3406,10 @@ namespace boost new detail::future_continuation, future_type, F>(*this, boost::forward(func), policy); if (ptr==0) { - return BOOST_THREAD_FUTURE(); + return BOOST_THREAD_MAKE_RV_REF(BOOST_THREAD_FUTURE()); } this->future_->set_continuation_ptr(ptr, lock); - return ptr->next.get_future(); + return BOOST_THREAD_MAKE_RV_REF(ptr->next.get_future()); } else { @@ -3421,16 +3430,20 @@ namespace boost { boost::unique_lock lock(this->future_->mutex); detail::future_continuation, future_type, F > *ptr = - new detail::future_continuation, future_type, F>(*this, boost::forward(func)); + new + //BOOST_THREAD_MAKE_RV_REF(( + detail::future_continuation, future_type, F>(*this, boost::forward(func)) + //)) + ; if (ptr==0) { - return BOOST_THREAD_FUTURE(); + return BOOST_THREAD_MAKE_RV_REF(BOOST_THREAD_FUTURE()); } this->future_->set_continuation_ptr(ptr, lock); return ptr->next.get_future(); } else { // fixme what to do when the future has no associated state? - return BOOST_THREAD_FUTURE(); + return BOOST_THREAD_MAKE_RV_REF(BOOST_THREAD_FUTURE()); } } @@ -3447,16 +3460,20 @@ namespace boost { boost::unique_lock lock(this->future_->mutex); detail::future_continuation, future_type, RF(*)(BOOST_THREAD_FUTURE&) > *ptr = - new detail::future_continuation, future_type, RF(*)(BOOST_THREAD_FUTURE&)>(*this, func); + new + //BOOST_THREAD_MAKE_RV_REF(( + detail::future_continuation, future_type, RF(*)(BOOST_THREAD_FUTURE&)>(*this, func) + // )) + ; if (ptr==0) { - return BOOST_THREAD_FUTURE(); + return BOOST_THREAD_MAKE_RV_REF(BOOST_THREAD_FUTURE()); } this->future_->set_continuation_ptr(ptr, lock); return ptr->next.get_future(); } else { // fixme what to do when the future has no associated state? - return BOOST_THREAD_FUTURE(); + return BOOST_THREAD_MAKE_RV_REF(BOOST_THREAD_FUTURE()); } } @@ -3472,16 +3489,20 @@ namespace boost { boost::unique_lock lock(this->future_->mutex); detail::future_continuation, future_type, RF(*)(BOOST_THREAD_FUTURE&) > *ptr = - new detail::future_continuation, future_type, RF(*)(BOOST_THREAD_FUTURE&)>(*this, func, policy); + new + //BOOST_THREAD_MAKE_RV_REF(( + detail::future_continuation, future_type, RF(*)(BOOST_THREAD_FUTURE&)>(*this, func, policy) + // )) + ; if (ptr==0) { - return BOOST_THREAD_FUTURE(); + return BOOST_THREAD_MAKE_RV_REF(BOOST_THREAD_FUTURE()); } this->future_->set_continuation_ptr(ptr, lock); return ptr->next.get_future(); } else { // fixme what to do when the future has no associated state? - return BOOST_THREAD_FUTURE(); + return BOOST_THREAD_MAKE_RV_REF(BOOST_THREAD_FUTURE()); } } diff --git a/include/boost/thread/is_locked_by_this_thread.hpp b/include/boost/thread/is_locked_by_this_thread.hpp index f1427356c..6344c0ff6 100644 --- a/include/boost/thread/is_locked_by_this_thread.hpp +++ b/include/boost/thread/is_locked_by_this_thread.hpp @@ -25,7 +25,7 @@ namespace boost template bool is_locked_by_this_thread(testable_mutex const& mtx) { - return mtx.is_locked(); + return mtx.is_locked_by_this_thread(); } template bool is_locked_by_this_thread(Lockable const&) diff --git a/include/boost/thread/lock_concepts.hpp b/include/boost/thread/lock_concepts.hpp index a5bada3a0..d96c3dc1b 100644 --- a/include/boost/thread/lock_concepts.hpp +++ b/include/boost/thread/lock_concepts.hpp @@ -30,7 +30,7 @@ namespace boost struct BasicLock { typedef typename Lk::mutex_type mutex_type; - void cvt_mutex_ptr(mutex_type*); + void cvt_mutex_ptr(mutex_type*) {} BOOST_CONCEPT_ASSERT(( BasicLockable )); BOOST_CONCEPT_USAGE(BasicLock) diff --git a/include/boost/thread/pthread/once.hpp b/include/boost/thread/pthread/once.hpp index b42c65904..f0c99ba5c 100644 --- a/include/boost/thread/pthread/once.hpp +++ b/include/boost/thread/pthread/once.hpp @@ -94,6 +94,18 @@ namespace boost #define BOOST_ONCE_INIT {BOOST_ONCE_INITIAL_FLAG_VALUE} #endif // BOOST_THREAD_PROVIDES_ONCE_CXX11 + +#if defined BOOST_THREAD_PROVIDES_INVOKE +#define BOOST_THREAD_INVOKE_RET_VOID detail::invoke +#define BOOST_THREAD_INVOKE_RET_VOID_CALL +#elif defined BOOST_THREAD_PROVIDES_INVOKE_RET +#define BOOST_THREAD_INVOKE_RET_VOID detail::invoke +#define BOOST_THREAD_INVOKE_RET_VOID_CALL +#else +#define BOOST_THREAD_INVOKE_RET_VOID boost::bind +#define BOOST_THREAD_INVOKE_RET_VOID_CALL () +#endif + namespace thread_detail { BOOST_THREAD_DECL uintmax_atomic_t& get_once_per_thread_epoch(); @@ -129,28 +141,10 @@ namespace boost BOOST_TRY { pthread::pthread_mutex_scoped_unlock relocker(&thread_detail::once_epoch_mutex); -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 -#if defined BOOST_THREAD_PROVIDES_INVOKE - detail::invoke( - thread_detail::decay_copy(boost::forward(f)), - thread_detail::decay_copy(boost::forward(args))... - ); -#elif defined BOOST_THREAD_PROVIDES_INVOKE_RET - detail::invoke( - thread_detail::decay_copy(boost::forward(f)), - thread_detail::decay_copy(boost::forward(args))... - ); -#else - boost::bind( + BOOST_THREAD_INVOKE_RET_VOID( thread_detail::decay_copy(boost::forward(f)), thread_detail::decay_copy(boost::forward(args))... - )(); -#endif -#else - f( - thread_detail::decay_copy(boost::forward(args))... - ); -#endif + ) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH (...) { @@ -239,11 +233,7 @@ namespace boost BOOST_TRY { pthread::pthread_mutex_scoped_unlock relocker(&thread_detail::once_epoch_mutex); -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind(f,p1)(); -#else - f(p1); -#endif + BOOST_THREAD_INVOKE_RET_VOID(f,p1) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH (...) { @@ -286,11 +276,7 @@ namespace boost BOOST_TRY { pthread::pthread_mutex_scoped_unlock relocker(&thread_detail::once_epoch_mutex); -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind(f,p1,p2)(); -#else - f(p1,p2); -#endif + BOOST_THREAD_INVOKE_RET_VOID(f,p1, p2) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH (...) { @@ -334,11 +320,7 @@ namespace boost BOOST_TRY { pthread::pthread_mutex_scoped_unlock relocker(&thread_detail::once_epoch_mutex); -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind(f,p1,p2,p3)(); -#else - f(p1,p2,p3); -#endif + BOOST_THREAD_INVOKE_RET_VOID(f,p1, p2, p3) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH (...) { @@ -426,16 +408,10 @@ namespace boost BOOST_TRY { pthread::pthread_mutex_scoped_unlock relocker(&thread_detail::once_epoch_mutex); -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind( - thread_detail::decay_copy(boost::forward(f)), - thread_detail::decay_copy(boost::forward(p1)) - )(); -#else - f( - thread_detail::decay_copy(boost::forward(p1)) - ); -#endif + BOOST_THREAD_INVOKE_RET_VOID( + thread_detail::decay_copy(boost::forward(f)), + thread_detail::decay_copy(boost::forward(p1)) + ) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH (...) { @@ -478,18 +454,11 @@ namespace boost BOOST_TRY { pthread::pthread_mutex_scoped_unlock relocker(&thread_detail::once_epoch_mutex); -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind( - thread_detail::decay_copy(boost::forward(f)), - thread_detail::decay_copy(boost::forward(p1)), - thread_detail::decay_copy(boost::forward(p2)) - )(); -#else - f( - thread_detail::decay_copy(boost::forward(p1)), - thread_detail::decay_copy(boost::forward(p2)) - ); -#endif + BOOST_THREAD_INVOKE_RET_VOID( + thread_detail::decay_copy(boost::forward(f)), + thread_detail::decay_copy(boost::forward(p1)), + thread_detail::decay_copy(boost::forward(p2)) + ) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH (...) { @@ -533,20 +502,12 @@ namespace boost BOOST_TRY { pthread::pthread_mutex_scoped_unlock relocker(&thread_detail::once_epoch_mutex); -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind( - thread_detail::decay_copy(boost::forward(f)), - thread_detail::decay_copy(boost::forward(p1)), - thread_detail::decay_copy(boost::forward(p2)), - thread_detail::decay_copy(boost::forward(p3)) - )(); -#else - f( - thread_detail::decay_copy(boost::forward(p1)), - thread_detail::decay_copy(boost::forward(p2)), - thread_detail::decay_copy(boost::forward(p3)) - ); -#endif + BOOST_THREAD_INVOKE_RET_VOID( + thread_detail::decay_copy(boost::forward(f)), + thread_detail::decay_copy(boost::forward(p1)), + thread_detail::decay_copy(boost::forward(p2)), + thread_detail::decay_copy(boost::forward(p3)) + ) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH (...) { diff --git a/include/boost/thread/pthread/once_atomic.hpp b/include/boost/thread/pthread/once_atomic.hpp index ce51e69c8..0ec1b4c92 100644 --- a/include/boost/thread/pthread/once_atomic.hpp +++ b/include/boost/thread/pthread/once_atomic.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -27,9 +28,28 @@ namespace boost namespace thread_detail { + +#if BOOST_ATOMIC_INT_LOCK_FREE == 2 + typedef unsigned int atomic_int_type; +#elif BOOST_ATOMIC_SHORT_LOCK_FREE == 2 + typedef unsigned short atomic_int_type; +#elif BOOST_ATOMIC_CHAR_LOCK_FREE == 2 + typedef unsigned char atomic_int_type; +#elif BOOST_ATOMIC_LONG_LOCK_FREE == 2 + typedef unsigned long atomic_int_type; +#elif defined(BOOST_HAS_LONG_LONG) && BOOST_ATOMIC_LLONG_LOCK_FREE == 2 + typedef ulong_long_type atomic_int_type; +#else + // All tested integer types are not atomic, the spinlock pool will be used + typedef unsigned int atomic_int_type; +#endif + + typedef boost::atomic atomic_type; + BOOST_THREAD_DECL bool enter_once_region(once_flag& flag) BOOST_NOEXCEPT; BOOST_THREAD_DECL void commit_once_region(once_flag& flag) BOOST_NOEXCEPT; BOOST_THREAD_DECL void rollback_once_region(once_flag& flag) BOOST_NOEXCEPT; + inline atomic_type& get_atomic_storage(once_flag& flag) BOOST_NOEXCEPT; } #ifdef BOOST_THREAD_PROVIDES_ONCE_CXX11 @@ -42,31 +62,59 @@ namespace boost } private: - #if defined(__GNUC__) - __attribute__((may_alias)) - #endif - uintmax_t storage; + thread_detail::atomic_type storage; friend BOOST_THREAD_DECL bool thread_detail::enter_once_region(once_flag& flag) BOOST_NOEXCEPT; friend BOOST_THREAD_DECL void thread_detail::commit_once_region(once_flag& flag) BOOST_NOEXCEPT; friend BOOST_THREAD_DECL void thread_detail::rollback_once_region(once_flag& flag) BOOST_NOEXCEPT; + friend thread_detail::atomic_type& thread_detail::get_atomic_storage(once_flag& flag) BOOST_NOEXCEPT; }; #define BOOST_ONCE_INIT boost::once_flag() + namespace thread_detail + { + inline atomic_type& get_atomic_storage(once_flag& flag) BOOST_NOEXCEPT + { + //return reinterpret_cast< atomic_type& >(flag.storage); + return flag.storage; + } + } + #else // BOOST_THREAD_PROVIDES_ONCE_CXX11 struct once_flag { - #if defined(__GNUC__) - __attribute__((may_alias)) - #endif - uintmax_t storage; + // The thread_detail::atomic_int_type storage is marked + // with this attribute in order to let the compiler know that it will alias this member + // and silence compilation warnings. + BOOST_THREAD_ATTRIBUTE_MAY_ALIAS thread_detail::atomic_int_type storage; }; #define BOOST_ONCE_INIT {0} + namespace thread_detail + { + inline atomic_type& get_atomic_storage(once_flag& flag) BOOST_NOEXCEPT + { + return reinterpret_cast< atomic_type& >(flag.storage); + } + + } + #endif // BOOST_THREAD_PROVIDES_ONCE_CXX11 +#if defined BOOST_THREAD_PROVIDES_INVOKE +#define BOOST_THREAD_INVOKE_RET_VOID detail::invoke +#define BOOST_THREAD_INVOKE_RET_VOID_CALL +#elif defined BOOST_THREAD_PROVIDES_INVOKE_RET +#define BOOST_THREAD_INVOKE_RET_VOID detail::invoke +#define BOOST_THREAD_INVOKE_RET_VOID_CALL +#else +#define BOOST_THREAD_INVOKE_RET_VOID boost::bind +#define BOOST_THREAD_INVOKE_RET_VOID_CALL () +#endif + + #ifndef BOOST_NO_CXX11_VARIADIC_TEMPLATES template @@ -76,28 +124,10 @@ namespace boost { BOOST_TRY { -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 -#if defined BOOST_THREAD_PROVIDES_INVOKE - detail::invoke( - thread_detail::decay_copy(boost::forward(f)), - thread_detail::decay_copy(boost::forward(args))... - ); -#elif defined BOOST_THREAD_PROVIDES_INVOKE_RET - detail::invoke( - thread_detail::decay_copy(boost::forward(f)), - thread_detail::decay_copy(boost::forward(args))... - ); -#else - boost::bind( + BOOST_THREAD_INVOKE_RET_VOID( thread_detail::decay_copy(boost::forward(f)), thread_detail::decay_copy(boost::forward(args))... - )(); -#endif -#else - f( - thread_detail::decay_copy(boost::forward(args))... - ); -#endif + ) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH (...) { @@ -135,11 +165,7 @@ namespace boost { BOOST_TRY { -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind(f,p1)(); -#else - f(p1); -#endif + BOOST_THREAD_INVOKE_RET_VOID(f, p1) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH (...) { @@ -158,11 +184,7 @@ namespace boost { BOOST_TRY { -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind(f,p1,p2)(); -#else - f(p1,p2); -#endif + BOOST_THREAD_INVOKE_RET_VOID(f, p1, p2) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH (...) { @@ -181,11 +203,7 @@ namespace boost { BOOST_TRY { -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind(f,p1,p2,p3)(); -#else - f(p1,p2,p3); -#endif + BOOST_THREAD_INVOKE_RET_VOID(f, p1, p2, p3) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH (...) { @@ -223,17 +241,10 @@ namespace boost { BOOST_TRY { -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind( + BOOST_THREAD_INVOKE_RET_VOID( thread_detail::decay_copy(boost::forward(f)), thread_detail::decay_copy(boost::forward(p1)) - )(); -#else - f( - thread_detail::decay_copy(boost::forward(p1)) - ); -#endif - + ) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH (...) { @@ -251,18 +262,11 @@ namespace boost { BOOST_TRY { -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind( + BOOST_THREAD_INVOKE_RET_VOID( thread_detail::decay_copy(boost::forward(f)), thread_detail::decay_copy(boost::forward(p1)), thread_detail::decay_copy(boost::forward(p2)) - )(); -#else - f( - thread_detail::decay_copy(boost::forward(p1)), - thread_detail::decay_copy(boost::forward(p2)) - ); -#endif + ) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH (...) { @@ -280,20 +284,12 @@ namespace boost { BOOST_TRY { -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind( + BOOST_THREAD_INVOKE_RET_VOID( thread_detail::decay_copy(boost::forward(f)), thread_detail::decay_copy(boost::forward(p1)), thread_detail::decay_copy(boost::forward(p2)), thread_detail::decay_copy(boost::forward(p3)) - )(); -#else - f( - thread_detail::decay_copy(boost::forward(p1)), - thread_detail::decay_copy(boost::forward(p2)), - thread_detail::decay_copy(boost::forward(p3)) - ); -#endif + ) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH (...) diff --git a/include/boost/thread/shared_mutex.hpp b/include/boost/thread/shared_mutex.hpp index 9e12a8f3c..b968f2ac1 100644 --- a/include/boost/thread/shared_mutex.hpp +++ b/include/boost/thread/shared_mutex.hpp @@ -18,6 +18,7 @@ #include #endif #elif defined(BOOST_THREAD_PLATFORM_PTHREAD) +//#include #include #else #error "Boost threads unavailable on this platform" diff --git a/include/boost/thread/synchronized_value.hpp b/include/boost/thread/synchronized_value.hpp index 514c2348a..bff5a9d72 100644 --- a/include/boost/thread/synchronized_value.hpp +++ b/include/boost/thread/synchronized_value.hpp @@ -18,6 +18,17 @@ #include #include #include +#include +//#include +//#include +//#if ! defined BOOST_NO_CXX11_HDR_TYPE_TRAITS +//#include +//#endif + +#if ! defined(BOOST_THREAD_NO_SYNCHRONIZE) +#include // todo change to once Boost.Tuple or Boost.Fusion provides Move semantics. +#include +#endif #include @@ -25,29 +36,44 @@ namespace boost { /** + * strict lock providing a const pointer access to the synchronized value type. * + * @param T the value type. + * @param Lockable the mutex type protecting the value type. */ template class const_strict_lock_ptr { public: typedef T value_type; - typedef Lockable lockable_type; + typedef Lockable mutex_type; protected: - // this should be a strict_lock, but we need to be able to return it. - boost::unique_lock lk_; + // this should be a strict_lock, but unique_lock is needed to be able to return it. + boost::unique_lock lk_; T const& value_; public: BOOST_THREAD_MOVABLE_ONLY( const_strict_lock_ptr ) - const_strict_lock_ptr(T const& value, Lockable & mtx) : - lk_(mtx), value_(value) + /** + * @param value constant reference of the value to protect. + * @param mtx reference to the mutex used to protect the value. + * @effects locks the mutex @c mtx, stores a reference to it and to the value type @c value. + */ + const_strict_lock_ptr(T const& val, Lockable & mtx) : + lk_(mtx), value_(val) { } - - const_strict_lock_ptr(BOOST_THREAD_RV_REF(const_strict_lock_ptr) other) + const_strict_lock_ptr(T const& val, Lockable & mtx, adopt_lock_t tag) BOOST_NOEXCEPT : + lk_(mtx, tag), value_(val) + { + } + /** + * Move constructor. + * @effects takes ownership of the mutex owned by @c other, stores a reference to the mutex and the value type of @c other. + */ + const_strict_lock_ptr(BOOST_THREAD_RV_REF(const_strict_lock_ptr) other) BOOST_NOEXCEPT : lk_(boost::move(BOOST_THREAD_RV(other).lk_)),value_(BOOST_THREAD_RV(other).value_) { } @@ -56,11 +82,17 @@ namespace boost { } + /** + * @return a constant pointer to the protected value + */ const T* operator->() const { return &value_; } + /** + * @return a constant reference to the protected value + */ const T& operator*() const { return value_; @@ -69,7 +101,10 @@ namespace boost }; /** + * strict lock providing a pointer access to the synchronized value type. * + * @param T the value type. + * @param Lockable the mutex type protecting the value type. */ template class strict_lock_ptr : public const_strict_lock_ptr @@ -78,11 +113,24 @@ namespace boost public: BOOST_THREAD_MOVABLE_ONLY( strict_lock_ptr ) - strict_lock_ptr(T & value, Lockable & mtx) : - base_type(value, mtx) + /** + * @param value reference of the value to protect. + * @param mtx reference to the mutex used to protect the value. + * @effects locks the mutex @c mtx, stores a reference to it and to the value type @c value. + */ + strict_lock_ptr(T & val, Lockable & mtx) : + base_type(val, mtx) + { + } + strict_lock_ptr(T & val, Lockable & mtx, adopt_lock_t tag) : + base_type(val, mtx, tag) { } + /** + * Move constructor. + * @effects takes ownership of the mutex owned by @c other, stores a reference to the mutex and the value type of @c other. + */ strict_lock_ptr(BOOST_THREAD_RV_REF(strict_lock_ptr) other) : base_type(boost::move(static_cast(other))) { @@ -92,11 +140,17 @@ namespace boost { } + /** + * @return a pointer to the protected value + */ T* operator->() { return const_cast(&this->value_); } + /** + * @return a reference to the protected value + */ T& operator*() { return const_cast(this->value_); @@ -104,8 +158,35 @@ namespace boost }; + template + struct synchronized_value_strict_lock_ptr + { + typedef strict_lock_ptr type; + }; + + template + struct synchronized_value_strict_lock_ptr + { + typedef const_strict_lock_ptr type; + }; /** + * unique_lock providing a const pointer access to the synchronized value type. + * + * An object of type const_unique_lock_ptr is a unique_lock that provides a const pointer access to the synchronized value type. + * As unique_lock controls the ownership of a lockable object within a scope. + * Ownership of the lockable object may be acquired at construction or after construction, + * and may be transferred, after acquisition, to another const_unique_lock_ptr object. + * Objects of type const_unique_lock_ptr are not copyable but are movable. + * The behavior of a program is undefined if the mutex and the value type + * pointed do not exist for the entire remaining lifetime of the const_unique_lock_ptr object. + * The supplied Mutex type shall meet the BasicLockable requirements. * + * @note const_unique_lock_ptr meets the Lockable requirements. + * If Lockable meets the TimedLockable requirements, const_unique_lock_ptr + * also meets the TimedLockable requirements. + * + * @param T the value type. + * @param Lockable the mutex type protecting the value type. */ template class const_unique_lock_ptr : public unique_lock @@ -113,44 +194,85 @@ namespace boost typedef unique_lock base_type; public: typedef T value_type; - typedef Lockable lockable_type; + typedef Lockable mutex_type; protected: T const& value_; public: BOOST_THREAD_MOVABLE_ONLY(const_unique_lock_ptr) - const_unique_lock_ptr(T const& value, Lockable & mtx) - : base_type(mtx), value_(value) + /** + * @param value reference of the value to protect. + * @param mtx reference to the mutex used to protect the value. + * + * @requires If mutex_type is not a recursive mutex the calling thread does not own the mutex. + * + * @effects locks the mutex @c mtx, stores a reference to it and to the value type @c value. + */ + const_unique_lock_ptr(T const& val, Lockable & mtx) + : base_type(mtx), value_(val) { } - const_unique_lock_ptr(T const& value, Lockable & mtx, adopt_lock_t) - : base_type(mtx, adopt_lock), value_(value) + /** + * @param value reference of the value to protect. + * @param mtx reference to the mutex used to protect the value. + * @param tag of type adopt_lock_t used to differentiate the constructor. + * @requires The calling thread own the mutex. + * @effects stores a reference to it and to the value type @c value taking ownership. + */ + const_unique_lock_ptr(T const& val, Lockable & mtx, adopt_lock_t) BOOST_NOEXCEPT + : base_type(mtx, adopt_lock), value_(val) { } - const_unique_lock_ptr(T const& value, Lockable & mtx, defer_lock_t) - : base_type(mtx, defer_lock), value_(value) + /** + * @param value reference of the value to protect. + * @param mtx reference to the mutex used to protect the value. + * @param tag of type defer_lock_t used to differentiate the constructor. + * @effects stores a reference to it and to the value type @c value c. + */ + const_unique_lock_ptr(T const& val, Lockable & mtx, defer_lock_t) BOOST_NOEXCEPT + : base_type(mtx, defer_lock), value_(val) { } - const_unique_lock_ptr(T const& value, Lockable & mtx, try_to_lock_t) - : base_type(mtx, try_to_lock), value_(value) + /** + * @param value reference of the value to protect. + * @param mtx reference to the mutex used to protect the value. + * @param tag of type try_to_lock_t used to differentiate the constructor. + * @requires If mutex_type is not a recursive mutex the calling thread does not own the mutex. + * @effects try to lock the mutex @c mtx, stores a reference to it and to the value type @c value. + */ + const_unique_lock_ptr(T const& val, Lockable & mtx, try_to_lock_t) BOOST_NOEXCEPT + : base_type(mtx, try_to_lock), value_(val) { } - const_unique_lock_ptr(BOOST_THREAD_RV_REF(const_unique_lock_ptr) other) + /** + * Move constructor. + * @effects takes ownership of the mutex owned by @c other, stores a reference to the mutex and the value type of @c other. + */ + const_unique_lock_ptr(BOOST_THREAD_RV_REF(const_unique_lock_ptr) other) BOOST_NOEXCEPT : base_type(boost::move(static_cast(other))), value_(BOOST_THREAD_RV(other).value_) { } + /** + * @effects If owns calls unlock() on the owned mutex. + */ ~const_unique_lock_ptr() { } + /** + * @return a constant pointer to the protected value + */ const T* operator->() const { BOOST_ASSERT (this->owns_lock()); return &value_; } + /** + * @return a constant reference to the protected value + */ const T& operator*() const { BOOST_ASSERT (this->owns_lock()); @@ -160,7 +282,10 @@ namespace boost }; /** + * unique lock providing a pointer access to the synchronized value type. * + * @param T the value type. + * @param Lockable the mutex type protecting the value type. */ template class unique_lock_ptr : public const_unique_lock_ptr @@ -168,27 +293,54 @@ namespace boost typedef const_unique_lock_ptr base_type; public: typedef T value_type; - typedef Lockable lockable_type; + typedef Lockable mutex_type; BOOST_THREAD_MOVABLE_ONLY(unique_lock_ptr) - unique_lock_ptr(T & value, Lockable & mtx) - : base_type(value, mtx) + /** + * @param value reference of the value to protect. + * @param mtx reference to the mutex used to protect the value. + * @effects locks the mutex @c mtx, stores a reference to it and to the value type @c value. + */ + unique_lock_ptr(T & val, Lockable & mtx) + : base_type(val, mtx) { } - unique_lock_ptr(T & value, Lockable & mtx, adopt_lock_t) + /** + * @param value reference of the value to protect. + * @param mtx reference to the mutex used to protect the value. + * @param tag of type adopt_lock_t used to differentiate the constructor. + * @effects stores a reference to it and to the value type @c value taking ownership. + */ + unique_lock_ptr(T & value, Lockable & mtx, adopt_lock_t) BOOST_NOEXCEPT : base_type(value, mtx, adopt_lock) { } - unique_lock_ptr(T & value, Lockable & mtx, defer_lock_t) + /** + * @param value reference of the value to protect. + * @param mtx reference to the mutex used to protect the value. + * @param tag of type defer_lock_t used to differentiate the constructor. + * @effects stores a reference to it and to the value type @c value c. + */ + unique_lock_ptr(T & value, Lockable & mtx, defer_lock_t) BOOST_NOEXCEPT : base_type(value, mtx, defer_lock) { } - unique_lock_ptr(T & value, Lockable & mtx, try_to_lock_t) + /** + * @param value reference of the value to protect. + * @param mtx reference to the mutex used to protect the value. + * @param tag of type try_to_lock_t used to differentiate the constructor. + * @effects try to lock the mutex @c mtx, stores a reference to it and to the value type @c value. + */ + unique_lock_ptr(T & value, Lockable & mtx, try_to_lock_t) BOOST_NOEXCEPT : base_type(value, mtx, try_to_lock) { } - unique_lock_ptr(BOOST_THREAD_RV_REF(unique_lock_ptr) other) + /** + * Move constructor. + * @effects takes ownership of the mutex owned by @c other, stores a reference to the mutex and the value type of @c other. + */ + unique_lock_ptr(BOOST_THREAD_RV_REF(unique_lock_ptr) other) BOOST_NOEXCEPT : base_type(boost::move(static_cast(other))) { } @@ -197,12 +349,18 @@ namespace boost { } + /** + * @return a pointer to the protected value + */ T* operator->() { BOOST_ASSERT (this->owns_lock()); return const_cast(&this->value_); } + /** + * @return a reference to the protected value + */ T& operator*() { BOOST_ASSERT (this->owns_lock()); @@ -212,25 +370,62 @@ namespace boost }; + template + struct synchronized_value_unique_lock_ptr + { + typedef unique_lock_ptr type; + }; + + template + struct synchronized_value_unique_lock_ptr + { + typedef const_unique_lock_ptr type; + }; /** - * + * cloaks a value type and the mutex used to protect it together. + * @param T the value type. + * @param Lockable the mutex type protecting the value type. */ template class synchronized_value { + +#if ! defined(BOOST_THREAD_NO_MAKE_UNIQUE_LOCKS) +#if ! defined BOOST_NO_CXX11_VARIADIC_TEMPLATES + template + friend std::tuple::type ...> synchronize(SV& ...sv); +#else + template + friend std::tuple< + typename synchronized_value_strict_lock_ptr::type, + typename synchronized_value_strict_lock_ptr::type + > + synchronize(SV1& sv1, SV2& sv2); + template + friend std::tuple< + typename synchronized_value_strict_lock_ptr::type, + typename synchronized_value_strict_lock_ptr::type, + typename synchronized_value_strict_lock_ptr::type + > + synchronize(SV1& sv1, SV2& sv2, SV3& sv3); +#endif +#endif + public: typedef T value_type; - typedef Lockable lockable_type; + typedef Lockable mutex_type; private: T value_; - mutable lockable_type mtx_; + mutable mutex_type mtx_; public: + // construction/destruction /** * Default constructor. * - * Requires: T is DefaultConstructible + * @Requires: T is DefaultConstructible */ synchronized_value() + //BOOST_NOEXCEPT_IF(is_nothrow_default_constructible::value) : value_() { } @@ -241,47 +436,50 @@ namespace boost * Requires: T is CopyConstructible */ synchronized_value(T const& other) + //BOOST_NOEXCEPT_IF(is_nothrow_copy_constructible::value) : value_(other) { } /** - * Move Constructor from movable value. + * Move Constructor. * - * Requires: T is Movable + * Requires: T is CopyMovable */ synchronized_value(BOOST_THREAD_RV_REF(T) other) + //BOOST_NOEXCEPT_IF(is_nothrow_move_constructible::value) : value_(boost::move(other)) { } /** - * Copy Constructor. + * Constructor from value type. * * 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. */ synchronized_value(synchronized_value const& rhs) { - strict_lock lk(rhs.mtx_); + strict_lock lk(rhs.mtx_); value_ = rhs.value_; } /** - * Move Constructor. + * Move Constructor from movable value type * */ synchronized_value(BOOST_THREAD_RV_REF(synchronized_value) other) { - strict_lock lk(other.mtx_); - value_= boost::move(other.value); + strict_lock lk(other.mtx_); + value_= boost::move(other.value_); } + // mutation /** * Assignment operator. * * Effects: Copies the underlying value on a scope protected by the two mutexes. - * The mutexes are not copied. The locks are acquired using lock, so deadlock is avoided. + * The mutex is not copied. The locks are acquired using lock, so deadlock is avoided. * For example, there is no problem if one thread assigns a = b and the other assigns b = a. * * Return: *this @@ -292,8 +490,8 @@ namespace boost if(&rhs != this) { // auto _ = make_unique_locks(mtx_, rhs.mtx_); - unique_lock lk1(mtx_, defer_lock); - unique_lock lk2(rhs.mtx_, defer_lock); + unique_lock lk1(mtx_, defer_lock); + unique_lock lk2(rhs.mtx_, defer_lock); lock(lk1,lk2); value_ = rhs.value_; @@ -305,15 +503,16 @@ namespace boost * Effects: The operator copies the value on a scope protected by the mutex. * Return: *this */ - synchronized_value& operator=(value_type const& value) + synchronized_value& operator=(value_type const& val) { { - strict_lock lk(mtx_); - value_ = value; + strict_lock lk(mtx_); + value_ = val; } return *this; } + //observers /** * Explicit conversion to value type. * @@ -323,7 +522,7 @@ namespace boost */ T get() const { - strict_lock lk(mtx_); + strict_lock lk(mtx_); return value_; } /** @@ -340,6 +539,30 @@ namespace boost } #endif + /** + * value type getter. + * + * Return: A constant reference to the protected value. + * + * Note: Not thread safe + * + */ + T const& value() const + { + return value_; + } + /** + * mutex getter. + * + * Return: A constant reference to the protecting mutex. + * + * Note: Not thread safe + * + */ + mutex_type const& mutex() const + { + return mtx_; + } /** * Swap * @@ -354,20 +577,20 @@ namespace boost return; } // auto _ = make_unique_locks(mtx_, rhs.mtx_); - unique_lock lk1(mtx_, defer_lock); - unique_lock lk2(rhs.mtx_, defer_lock); + unique_lock lk1(mtx_, defer_lock); + unique_lock lk2(rhs.mtx_, defer_lock); lock(lk1,lk2); boost::swap(value_, rhs.value_); } /** - * Swap with the underlying type + * Swap with the underlying value type * * Effects: Swaps the data on a scope protected by the mutex. */ void swap(value_type & rhs) { - strict_lock lk(mtx_); - boost::swap(value_, rhs.value_); + strict_lock lk(mtx_); + boost::swap(value_, rhs); } @@ -415,27 +638,53 @@ namespace boost { return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr(value_, mtx_))); } - unique_lock_ptr unique_synchronize(defer_lock_t tag) - { - return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr(value_, mtx_, tag))); - } const_unique_lock_ptr unique_synchronize() const { return BOOST_THREAD_MAKE_RV_REF((const_unique_lock_ptr(value_, mtx_))); } + unique_lock_ptr unique_synchronize(defer_lock_t tag) + { + return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr(value_, mtx_, tag))); + } const_unique_lock_ptr unique_synchronize(defer_lock_t tag) const { return BOOST_THREAD_MAKE_RV_REF((const_unique_lock_ptr(value_, mtx_, tag))); } + unique_lock_ptr defer_synchronize() BOOST_NOEXCEPT + { + return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr(value_, mtx_, defer_lock))); + } + const_unique_lock_ptr defer_synchronize() const BOOST_NOEXCEPT + { + return BOOST_THREAD_MAKE_RV_REF((const_unique_lock_ptr(value_, mtx_, defer_lock))); + } + unique_lock_ptr try_to_synchronize() BOOST_NOEXCEPT + { + return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr(value_, mtx_, try_to_lock))); + } + const_unique_lock_ptr try_to_synchronize() const BOOST_NOEXCEPT + { + return BOOST_THREAD_MAKE_RV_REF((const_unique_lock_ptr(value_, mtx_, try_to_lock))); + } + unique_lock_ptr adopt_synchronize() BOOST_NOEXCEPT + { + return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr(value_, mtx_, adopt_lock))); + } + const_unique_lock_ptr adopt_synchronize() const BOOST_NOEXCEPT + { + return BOOST_THREAD_MAKE_RV_REF((const_unique_lock_ptr(value_, mtx_, adopt_lock))); + } - private: +#if ! defined __IBMCPP__ + private: +#endif class deref_value { private: friend class synchronized_value; - boost::unique_lock lk_; + boost::unique_lock lk_; T& value_; explicit deref_value(synchronized_value& outer): @@ -448,7 +697,7 @@ namespace boost deref_value(BOOST_THREAD_RV_REF(deref_value) other): lk_(boost::move(BOOST_THREAD_RV(other).lk_)),value_(BOOST_THREAD_RV(other).value_) {} - operator T() + operator T&() { return value_; } @@ -464,7 +713,7 @@ namespace boost private: friend class synchronized_value; - boost::unique_lock lk_; + boost::unique_lock lk_; const T& value_; explicit const_deref_value(synchronized_value const& outer): @@ -478,7 +727,7 @@ namespace boost lk_(boost::move(BOOST_THREAD_RV(other).lk_)), value_(BOOST_THREAD_RV(other).value_) {} - operator T() + operator const T&() { return value_; } @@ -495,8 +744,121 @@ namespace boost return BOOST_THREAD_MAKE_RV_REF(const_deref_value(*this)); } + // io functions + /** + * @requires T is OutputStreamable + * @effects saves the value type on the output stream @c os. + */ + template + void save(OStream& os) const + { + strict_lock lk(mtx_); + os << value_; + } + /** + * @requires T is InputStreamable + * @effects loads the value type from the input stream @c is. + */ + template + void load(IStream& is) const + { + strict_lock lk(mtx_); + is >> value_; + } + + // relational operators + /** + * @requires T is EqualityComparable + * + */ + bool operator==(synchronized_value const& rhs) const + { + unique_lock lk1(mtx_, defer_lock); + unique_lock lk2(rhs.mtx_, defer_lock); + lock(lk1,lk2); + + return value_ == rhs.value_; + } + /** + * @requires T is LessThanComparable + * + */ + bool operator<(synchronized_value const& rhs) const + { + unique_lock lk1(mtx_, defer_lock); + unique_lock lk2(rhs.mtx_, defer_lock); + lock(lk1,lk2); + + return value_ < rhs.value_; + } + /** + * @requires T is GreaterThanComparable + * + */ + bool operator>(synchronized_value const& rhs) const + { + unique_lock lk1(mtx_, defer_lock); + unique_lock lk2(rhs.mtx_, defer_lock); + lock(lk1,lk2); + + return value_ > rhs.value_; + } + bool operator<=(synchronized_value const& rhs) const + { + unique_lock lk1(mtx_, defer_lock); + unique_lock lk2(rhs.mtx_, defer_lock); + lock(lk1,lk2); + + return value_ <= rhs.value_; + } + bool operator>=(synchronized_value const& rhs) const + { + unique_lock lk1(mtx_, defer_lock); + unique_lock lk2(rhs.mtx_, defer_lock); + lock(lk1,lk2); + + return value_ >= rhs.value_; + } + bool operator==(value_type const& rhs) const + { + unique_lock lk1(mtx_); + + return value_ == rhs; + } + bool operator!=(value_type const& rhs) const + { + unique_lock lk1(mtx_); + + return value_ != rhs; + } + bool operator<(value_type const& rhs) const + { + unique_lock lk1(mtx_); + + return value_ < rhs; + } + bool operator<=(value_type const& rhs) const + { + unique_lock lk1(mtx_); + + return value_ <= rhs; + } + bool operator>(value_type const& rhs) const + { + unique_lock lk1(mtx_); + + return value_ > rhs; + } + bool operator>=(value_type const& rhs) const + { + unique_lock lk1(mtx_); + + return value_ >= rhs; + } + }; + // Specialized algorithms /** * */ @@ -505,7 +867,133 @@ namespace boost { lhs.swap(rhs); } + template + inline void swap(synchronized_value & lhs, T & rhs) + { + lhs.swap(rhs); + } + template + inline void swap(T & lhs, synchronized_value & rhs) + { + rhs.swap(lhs); + } + + //Hash support + + template struct hash; + template + struct hash >; + + // Comparison with T + template + bool operator!=(synchronized_value const&lhs, synchronized_value const& rhs) + { + return ! (lhs==rhs); + } + template + bool operator==(T const& lhs, synchronized_value const&rhs) + { + return rhs==lhs; + } + template + bool operator!=(T const& lhs, synchronized_value const&rhs) + { + return rhs!=lhs; + } + template + bool operator<(T const& lhs, synchronized_value const&rhs) + { + return rhs>=lhs; + } + template + bool operator<=(T const& lhs, synchronized_value const&rhs) + { + return rhs>lhs; + } + template + bool operator>(T const& lhs, synchronized_value const&rhs) + { + return rhs<=lhs; + } + template + bool operator>=(T const& lhs, synchronized_value const&rhs) + { + return rhs + inline OStream& operator<<(OStream& os, synchronized_value const& rhs) + { + rhs.save(os); + return os; + } + template + inline IStream& operator>>(IStream& is, synchronized_value const& rhs) + { + rhs.load(is); + return is; + } + +#if ! defined(BOOST_THREAD_NO_SYNCHRONIZE) +#if ! defined BOOST_NO_CXX11_VARIADIC_TEMPLATES + + template + std::tuple::type ...> synchronize(SV& ...sv) + { + boost::lock(sv.mtx_ ...); + typedef std::tuple::type ...> t_type; + + return t_type(typename synchronized_value_strict_lock_ptr::type(sv.value_, sv.mtx_, adopt_lock) ...); + } +#else + + template + std::tuple< + typename synchronized_value_strict_lock_ptr::type, + typename synchronized_value_strict_lock_ptr::type + > + synchronize(SV1& sv1, SV2& sv2) + { + boost::lock(sv1.mtx_, sv2.mtx_); + typedef std::tuple< + typename synchronized_value_strict_lock_ptr::type, + typename synchronized_value_strict_lock_ptr::type + > t_type; + + return t_type( + typename synchronized_value_strict_lock_ptr::type(sv1.value_, sv1.mtx_, adopt_lock), + typename synchronized_value_strict_lock_ptr::type(sv2.value_, sv2.mtx_, adopt_lock) + ); + + } + template + std::tuple< + typename synchronized_value_strict_lock_ptr::type, + typename synchronized_value_strict_lock_ptr::type, + typename synchronized_value_strict_lock_ptr::type + > + synchronize(SV1& sv1, SV2& sv2, SV3& sv3) + { + boost::lock(sv1.mtx_, sv2.mtx_); + typedef std::tuple< + typename synchronized_value_strict_lock_ptr::type, + typename synchronized_value_strict_lock_ptr::type, + typename synchronized_value_strict_lock_ptr::type + > t_type; + + return t_type( + typename synchronized_value_strict_lock_ptr::type(sv1.value_, sv1.mtx_, adopt_lock), + typename synchronized_value_strict_lock_ptr::type(sv2.value_, sv2.mtx_, adopt_lock), + typename synchronized_value_strict_lock_ptr::type(sv3.value_, sv3.mtx_, adopt_lock) + ); + + } +#endif +#endif } #include diff --git a/include/boost/thread/testable_mutex.hpp b/include/boost/thread/testable_mutex.hpp index b08292136..bd9ccbd78 100644 --- a/include/boost/thread/testable_mutex.hpp +++ b/include/boost/thread/testable_mutex.hpp @@ -9,7 +9,7 @@ #include -#include +#include #include #include @@ -26,7 +26,7 @@ namespace boost * * Many mutex services (including boost::mutex) don't provide a way to ask, * "Do I already hold a lock on this mutex?" - * Sometimes it is needed to know if a method like is_held to be available. + * Sometimes it is needed to know if a method like is_locked to be available. * This wrapper associates an arbitrary lockable type with a thread id that stores the ID of the thread that * currently holds the lockable. The thread id initially holds an invalid value that means no threads own the mutex. * When we acquire a lock, we set the thread id; and when we release a lock, we reset it back to its default no id state. @@ -44,6 +44,8 @@ namespace boost /// Non copyable BOOST_THREAD_NO_COPYABLE(testable_mutex) + testable_mutex() : id_(thread::id()) {} + void lock() { mtx_.lock(); @@ -52,7 +54,7 @@ namespace boost void unlock() { - BOOST_ASSERT(is_locked(mtx_)); + BOOST_ASSERT(is_locked_by_this_thread()); id_ = thread::id(); mtx_.unlock(); } @@ -70,40 +72,44 @@ namespace boost } } #ifdef BOOST_THREAD_USES_CHRONO - template - bool try_lock_for(const chrono::duration& rel_time) - { - if (mtx_.try_lock_for(rel_time)) - { - id_ = this_thread::get_id(); - return true; - } - else - { - return false; - } - } - template - bool try_lock_until(const chrono::time_point& abs_time) - { - if (mtx_.try_lock_until(abs_time)) - { - id_ = this_thread::get_id(); - return true; - } - else - { - return false; - } - } + template + bool try_lock_for(const chrono::duration& rel_time) + { + if (mtx_.try_lock_for(rel_time)) + { + id_ = this_thread::get_id(); + return true; + } + else + { + return false; + } + } + template + bool try_lock_until(const chrono::time_point& abs_time) + { + if (mtx_.try_lock_until(abs_time)) + { + id_ = this_thread::get_id(); + return true; + } + else + { + return false; + } + } #endif - bool is_locked_by_this_thread() + bool is_locked_by_this_thread() const { return this_thread::get_id() == id_; } + bool is_locked() const + { + return ! (thread::id() == id_); + } - bool get_id() + thread::id get_id() const { return id_; } diff --git a/include/boost/thread/v2/thread.hpp b/include/boost/thread/v2/thread.hpp index f528c8fa8..0d5088fc9 100644 --- a/include/boost/thread/v2/thread.hpp +++ b/include/boost/thread/v2/thread.hpp @@ -68,11 +68,8 @@ namespace boost using namespace chrono; if (d > duration::zero()) { - steady_clock::time_point c_now = steady_clock::now(); - do - { - sleep_until(system_clock::now() + ceil(d)); - } while (steady_clock::now() - c_now < d ); + steady_clock::time_point c_timeout = steady_clock::now() + ceil(d); + sleep_until(c_timeout); } } diff --git a/include/boost/thread/win32/basic_timed_mutex.hpp b/include/boost/thread/win32/basic_timed_mutex.hpp index 5d85c70dc..73bbcc8d0 100644 --- a/include/boost/thread/win32/basic_timed_mutex.hpp +++ b/include/boost/thread/win32/basic_timed_mutex.hpp @@ -93,10 +93,13 @@ namespace boost { for(;;) { - long const new_count=(old_count&lock_flag_value)?(old_count+1):(old_count|lock_flag_value); + bool const was_locked=(old_count&lock_flag_value) ? true : false; + long const new_count=was_locked?(old_count+1):(old_count|lock_flag_value); long const current=BOOST_INTERLOCKED_COMPARE_EXCHANGE(&active_count,new_count,old_count); if(current==old_count) { + if(was_locked) + old_count=new_count; break; } old_count=current; diff --git a/include/boost/thread/win32/once.hpp b/include/boost/thread/win32/once.hpp index ca1af2616..689b2c11b 100644 --- a/include/boost/thread/win32/once.hpp +++ b/include/boost/thread/win32/once.hpp @@ -5,7 +5,7 @@ // // (C) Copyright 2005-7 Anthony Williams // (C) Copyright 2005 John Maddock -// (C) Copyright 2011-2012 Vicente J. Botet Escriba +// (C) Copyright 2011-2013 Vicente J. Botet Escriba // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -74,6 +74,17 @@ namespace boost #define BOOST_ONCE_INIT {0,0} #endif // BOOST_THREAD_PROVIDES_ONCE_CXX11 +#if defined BOOST_THREAD_PROVIDES_INVOKE +#define BOOST_THREAD_INVOKE_RET_VOID detail::invoke +#define BOOST_THREAD_INVOKE_RET_VOID_CALL +#elif defined BOOST_THREAD_PROVIDES_INVOKE_RET +#define BOOST_THREAD_INVOKE_RET_VOID detail::invoke +#define BOOST_THREAD_INVOKE_RET_VOID_CALL +#else +#define BOOST_THREAD_INVOKE_RET_VOID boost::bind +#define BOOST_THREAD_INVOKE_RET_VOID_CALL () +#endif + namespace detail { #ifdef BOOST_NO_ANSI_APIS @@ -261,8 +272,8 @@ namespace boost } } //#endif - template - inline void call_once(once_flag& flag, BOOST_THREAD_RV_REF(Function) f, BOOST_THREAD_RV_REF(ArgTypes)... args) + template + inline void call_once(once_flag& flag, BOOST_THREAD_RV_REF(Function) f) { // Try for a quick win: if the procedure has already been called // just skip through: @@ -274,28 +285,54 @@ namespace boost { BOOST_TRY { -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 -#if defined BOOST_THREAD_PROVIDES_INVOKE - detail::invoke( - thread_detail::decay_copy(boost::forward(f)), - thread_detail::decay_copy(boost::forward(args))... - ); -#elif defined BOOST_THREAD_PROVIDES_INVOKE_RET - detail::invoke( - thread_detail::decay_copy(boost::forward(f)), - thread_detail::decay_copy(boost::forward(args))... - ); -#else - boost::bind( + f(); + } + BOOST_CATCH(...) + { + detail::rollback_once_region(flag, ctx); + BOOST_RETHROW + } + BOOST_CATCH_END + detail::commit_once_region(flag, ctx); + break; + } + if(!ctx.counted) + { + BOOST_INTERLOCKED_INCREMENT(&flag.count); + ctx.counted=true; + long status=::boost::detail::interlocked_read_acquire(&flag.status); + if(status==ctx.function_complete_flag_value) + { + break; + } + if(!ctx.event_handle) + { + ctx.event_handle=detail::create_once_event(ctx.mutex_name,&flag); + continue; + } + } + BOOST_VERIFY(!::boost::detail::win32::WaitForSingleObject( + ctx.event_handle,::boost::detail::win32::infinite)); + } + } + template + inline void call_once(once_flag& flag, BOOST_THREAD_RV_REF(Function) f, BOOST_THREAD_RV_REF(A) a, BOOST_THREAD_RV_REF(ArgTypes)... args) + { + // Try for a quick win: if the procedure has already been called + // just skip through: + detail::once_context ctx; + while(::boost::detail::interlocked_read_acquire(&flag.status) + !=ctx.function_complete_flag_value) + { + if(detail::enter_once_region(flag, ctx)) + { + BOOST_TRY + { + BOOST_THREAD_INVOKE_RET_VOID( thread_detail::decay_copy(boost::forward(f)), + thread_detail::decay_copy(boost::forward(a)), thread_detail::decay_copy(boost::forward(args))... - )(); -#endif -#else - f( - thread_detail::decay_copy(boost::forward(args))... - ); -#endif + ) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH(...) { @@ -327,7 +364,6 @@ namespace boost } #else #ifndef BOOST_MSVC - template void call_once(once_flag& flag,Function f) { @@ -384,11 +420,7 @@ namespace boost { BOOST_TRY { -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind(f,p1)(); -#else - f(p1); -#endif + BOOST_THREAD_INVOKE_RET_VOID(f,p1) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH(...) { @@ -431,11 +463,7 @@ namespace boost { BOOST_TRY { -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind(f,p1,p2)(); -#else - f(p1,p2); -#endif + BOOST_THREAD_INVOKE_RET_VOID(f,p1,p2) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH(...) { @@ -478,11 +506,181 @@ namespace boost { BOOST_TRY { -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind(f,p1,p2,p3)(); -#else - f(p1,p2,p3); -#endif + BOOST_THREAD_INVOKE_RET_VOID(f,p1,p2,p3) BOOST_THREAD_INVOKE_RET_VOID_CALL; + } + BOOST_CATCH(...) + { + detail::rollback_once_region(flag, ctx); + BOOST_RETHROW + } + BOOST_CATCH_END + detail::commit_once_region(flag, ctx); + break; + } + if(!ctx.counted) + { + BOOST_INTERLOCKED_INCREMENT(&flag.count); + ctx.counted=true; + long status=::boost::detail::interlocked_read_acquire(&flag.status); + if(status==ctx.function_complete_flag_value) + { + break; + } + if(!ctx.event_handle) + { + ctx.event_handle=detail::create_once_event(ctx.mutex_name,&flag); + continue; + } + } + BOOST_VERIFY(!::boost::detail::win32::WaitForSingleObject( + ctx.event_handle,::boost::detail::win32::infinite)); + } + } +#elif defined BOOST_NO_CXX11_RVALUE_REFERENCES + + template + void call_once(once_flag& flag,Function const&f) + { + // Try for a quick win: if the procedure has already been called + // just skip through: + detail::once_context ctx; + while(::boost::detail::interlocked_read_acquire(&flag.status) + !=ctx.function_complete_flag_value) + { + if(detail::enter_once_region(flag, ctx)) + { + BOOST_TRY + { + f(); + } + BOOST_CATCH(...) + { + detail::rollback_once_region(flag, ctx); + BOOST_RETHROW + } + BOOST_CATCH_END + detail::commit_once_region(flag, ctx); + break; + } + if(!ctx.counted) + { + BOOST_INTERLOCKED_INCREMENT(&flag.count); + ctx.counted=true; + long status=::boost::detail::interlocked_read_acquire(&flag.status); + if(status==ctx.function_complete_flag_value) + { + break; + } + if(!ctx.event_handle) + { + ctx.event_handle=detail::create_once_event(ctx.mutex_name,&flag); + continue; + } + } + BOOST_VERIFY(!::boost::detail::win32::WaitForSingleObject( + ctx.event_handle,::boost::detail::win32::infinite)); + } + } + template + void call_once(once_flag& flag,Function const&f, T1 const&p1) + { + // Try for a quick win: if the procedure has already been called + // just skip through: + detail::once_context ctx; + while(::boost::detail::interlocked_read_acquire(&flag.status) + !=ctx.function_complete_flag_value) + { + if(detail::enter_once_region(flag, ctx)) + { + BOOST_TRY + { + BOOST_THREAD_INVOKE_RET_VOID(f,p1) BOOST_THREAD_INVOKE_RET_VOID_CALL; + } + BOOST_CATCH(...) + { + detail::rollback_once_region(flag, ctx); + BOOST_RETHROW + } + BOOST_CATCH_END + detail::commit_once_region(flag, ctx); + break; + } + if(!ctx.counted) + { + BOOST_INTERLOCKED_INCREMENT(&flag.count); + ctx.counted=true; + long status=::boost::detail::interlocked_read_acquire(&flag.status); + if(status==ctx.function_complete_flag_value) + { + break; + } + if(!ctx.event_handle) + { + ctx.event_handle=detail::create_once_event(ctx.mutex_name,&flag); + continue; + } + } + BOOST_VERIFY(!::boost::detail::win32::WaitForSingleObject( + ctx.event_handle,::boost::detail::win32::infinite)); + } + } + template + void call_once(once_flag& flag,Function const&f, T1 const&p1, T2 const&p2) + { + // Try for a quick win: if the procedure has already been called + // just skip through: + detail::once_context ctx; + while(::boost::detail::interlocked_read_acquire(&flag.status) + !=ctx.function_complete_flag_value) + { + if(detail::enter_once_region(flag, ctx)) + { + BOOST_TRY + { + BOOST_THREAD_INVOKE_RET_VOID(f,p1,p2) BOOST_THREAD_INVOKE_RET_VOID_CALL; + } + BOOST_CATCH(...) + { + detail::rollback_once_region(flag, ctx); + BOOST_RETHROW + } + BOOST_CATCH_END + detail::commit_once_region(flag, ctx); + break; + } + if(!ctx.counted) + { + BOOST_INTERLOCKED_INCREMENT(&flag.count); + ctx.counted=true; + long status=::boost::detail::interlocked_read_acquire(&flag.status); + if(status==ctx.function_complete_flag_value) + { + break; + } + if(!ctx.event_handle) + { + ctx.event_handle=detail::create_once_event(ctx.mutex_name,&flag); + continue; + } + } + BOOST_VERIFY(!::boost::detail::win32::WaitForSingleObject( + ctx.event_handle,::boost::detail::win32::infinite)); + } + } + template + void call_once(once_flag& flag,Function const&f, T1 const&p1, T2 const&p2, T3 const&p3) + { + // Try for a quick win: if the procedure has already been called + // just skip through: + detail::once_context ctx; + while(::boost::detail::interlocked_read_acquire(&flag.status) + !=ctx.function_complete_flag_value) + { + if(detail::enter_once_region(flag, ctx)) + { + BOOST_TRY + { + BOOST_THREAD_INVOKE_RET_VOID(f,p1,p2,p3) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH(...) { @@ -617,7 +815,54 @@ namespace boost { f( thread_detail::decay_copy(boost::forward(p1)), - thread_detail::decay_copy(boost::forward(p2)) + thread_detail::decay_copy(boost::forward(p2)) + ); + } + BOOST_CATCH(...) + { + detail::rollback_once_region(flag, ctx); + BOOST_RETHROW + } + BOOST_CATCH_END + detail::commit_once_region(flag, ctx); + break; + } + if(!ctx.counted) + { + BOOST_INTERLOCKED_INCREMENT(&flag.count); + ctx.counted=true; + long status=::boost::detail::interlocked_read_acquire(&flag.status); + if(status==ctx.function_complete_flag_value) + { + break; + } + if(!ctx.event_handle) + { + ctx.event_handle=detail::create_once_event(ctx.mutex_name,&flag); + continue; + } + } + BOOST_VERIFY(!::boost::detail::win32::WaitForSingleObject( + ctx.event_handle,::boost::detail::win32::infinite)); + } + } + template + void call_once(once_flag& flag,void (*f)(BOOST_THREAD_RV_REF(T1),BOOST_THREAD_RV_REF(T2)), BOOST_THREAD_RV_REF(T1) p1, BOOST_THREAD_RV_REF(T2) p2, BOOST_THREAD_RV_REF(T3) p3) + { + // Try for a quick win: if the procedure has already been called + // just skip through: + detail::once_context ctx; + while(::boost::detail::interlocked_read_acquire(&flag.status) + !=ctx.function_complete_flag_value) + { + if(detail::enter_once_region(flag, ctx)) + { + BOOST_TRY + { + f( + thread_detail::decay_copy(boost::forward(p1)), + thread_detail::decay_copy(boost::forward(p2)), + thread_detail::decay_copy(boost::forward(p3)) ); } BOOST_CATCH(...) @@ -706,16 +951,10 @@ namespace boost { BOOST_TRY { -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind( - thread_detail::decay_copy(boost::forward(f)), - thread_detail::decay_copy(boost::forward(p1)) - )(); -#else - f( - thread_detail::decay_copy(boost::forward(p1)) - ); -#endif + BOOST_THREAD_INVOKE_RET_VOID( + thread_detail::decay_copy(boost::forward(f)), + thread_detail::decay_copy(boost::forward(p1)) + ) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH(...) { @@ -758,18 +997,11 @@ namespace boost { BOOST_TRY { -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind( - thread_detail::decay_copy(boost::forward(f)), - thread_detail::decay_copy(boost::forward(p1)), - thread_detail::decay_copy(boost::forward(p2)) - )(); -#else - f( - thread_detail::decay_copy(boost::forward(p1)), - thread_detail::decay_copy(boost::forward(p2)) - ); -#endif + BOOST_THREAD_INVOKE_RET_VOID( + thread_detail::decay_copy(boost::forward(f)), + thread_detail::decay_copy(boost::forward(p1)), + thread_detail::decay_copy(boost::forward(p2)) + ) BOOST_THREAD_INVOKE_RET_VOID_CALL; } BOOST_CATCH(...) { @@ -812,20 +1044,13 @@ namespace boost { BOOST_TRY { -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 - boost::bind( - thread_detail::decay_copy(boost::forward(f)), - thread_detail::decay_copy(boost::forward(p1)), - thread_detail::decay_copy(boost::forward(p2)), - thread_detail::decay_copy(boost::forward(p3)) - )(); -#else - f( - thread_detail::decay_copy(boost::forward(p1)), - thread_detail::decay_copy(boost::forward(p2)), - thread_detail::decay_copy(boost::forward(p3)) - ); -#endif + BOOST_THREAD_INVOKE_RET_VOID( + thread_detail::decay_copy(boost::forward(f)), + thread_detail::decay_copy(boost::forward(p1)), + thread_detail::decay_copy(boost::forward(p2)), + thread_detail::decay_copy(boost::forward(p3)) + ) BOOST_THREAD_INVOKE_RET_VOID_CALL; + } BOOST_CATCH(...) { diff --git a/include/boost/thread/win32/shared_mutex.hpp b/include/boost/thread/win32/shared_mutex.hpp index 4f3ca0e21..252174f69 100644 --- a/include/boost/thread/win32/shared_mutex.hpp +++ b/include/boost/thread/win32/shared_mutex.hpp @@ -95,7 +95,7 @@ namespace boost detail::win32::release_semaphore(semaphores[exclusive_sem],LONG_MAX); boost::throw_exception(thread_resource_error()); } - state_data state_={0}; + state_data state_={0,0,0,0,0,0}; state=state_; } @@ -471,6 +471,7 @@ namespace boost { for(;;) { + bool must_notify = false; state_data new_state=old_state; if(new_state.shared_count || new_state.exclusive) { @@ -479,6 +480,7 @@ namespace boost if(!--new_state.exclusive_waiting) { new_state.exclusive_waiting_blocked=false; + must_notify = true; } } } @@ -488,6 +490,11 @@ namespace boost } state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if (must_notify) + { + BOOST_VERIFY(detail::win32::ReleaseSemaphore(semaphores[unlock_sem],1,0)!=0); + } + if(current_state==old_state) { break; @@ -580,6 +587,7 @@ namespace boost { for(;;) { + bool must_notify = false; state_data new_state=old_state; if(new_state.shared_count || new_state.exclusive) { @@ -588,6 +596,7 @@ namespace boost if(!--new_state.exclusive_waiting) { new_state.exclusive_waiting_blocked=false; + must_notify = true; } } } @@ -597,6 +606,10 @@ namespace boost } state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if (must_notify) + { + BOOST_VERIFY(detail::win32::ReleaseSemaphore(semaphores[unlock_sem],1,0)!=0); + } if(current_state==old_state) { break; @@ -736,9 +749,11 @@ namespace boost if(last_reader) { release_waiters(old_state); - } else { - release_waiters(old_state); } + // #7720 + //else { + // release_waiters(old_state); + //} break; } old_state=current_state; diff --git a/include/boost/thread/win32/thread_data.hpp b/include/boost/thread/win32/thread_data.hpp index f1ececa8d..09faff9d0 100644 --- a/include/boost/thread/win32/thread_data.hpp +++ b/include/boost/thread/win32/thread_data.hpp @@ -165,7 +165,7 @@ namespace boost struct BOOST_SYMBOL_VISIBLE timeout { - unsigned long start; + win32::ticks_type start; uintmax_t milliseconds; bool relative; boost::system_time abs_time; @@ -173,14 +173,14 @@ namespace boost static unsigned long const max_non_infinite_wait=0xfffffffe; timeout(uintmax_t milliseconds_): - start(win32::GetTickCount()), + start(win32::GetTickCount64()), milliseconds(milliseconds_), relative(true), abs_time(boost::get_system_time()) {} timeout(boost::system_time const& abs_time_): - start(win32::GetTickCount()), + start(win32::GetTickCount64()), milliseconds(0), relative(false), abs_time(abs_time_) @@ -205,8 +205,8 @@ namespace boost } else if(relative) { - unsigned long const now=win32::GetTickCount(); - unsigned long const elapsed=now-start; + win32::ticks_type const now=win32::GetTickCount64(); + win32::ticks_type const elapsed=now-start; return remaining_time((elapsed #include +#ifndef BOOST_THREAD_WIN32_HAS_GET_TICK_COUNT_64 +#if _WIN32_WINNT >= 0x0600 +#define BOOST_THREAD_WIN32_HAS_GET_TICK_COUNT_64 +#endif +#endif + #if defined( BOOST_USE_WINDOWS_H ) # include @@ -26,6 +32,11 @@ namespace boost { namespace win32 { +#ifdef BOOST_THREAD_WIN32_HAS_GET_TICK_COUNT_64 + typedef unsigned long long ticks_type; +#else + typedef unsigned long ticks_type; +#endif typedef ULONG_PTR ulong_ptr; typedef HANDLE handle; unsigned const infinite=INFINITE; @@ -61,6 +72,11 @@ namespace boost using ::Sleep; using ::QueueUserAPC; using ::GetTickCount; +#ifdef BOOST_THREAD_WIN32_HAS_GET_TICK_COUNT_64 + using ::GetTickCount64; +#else + inline ticks_type GetTickCount64() { return GetTickCount(); } +#endif } } } @@ -88,13 +104,18 @@ typedef void* HANDLE; # endif # endif + namespace boost { namespace detail { namespace win32 { - +#ifdef BOOST_THREAD_WIN32_HAS_GET_TICK_COUNT_64 + typedef unsigned long long ticks_type; +#else + typedef unsigned long ticks_type; +#endif # ifdef _WIN64 typedef unsigned __int64 ulong_ptr; # else @@ -133,7 +154,9 @@ namespace boost __declspec(dllimport) unsigned long __stdcall QueueUserAPC(queue_user_apc_callback_function,void*,ulong_ptr); __declspec(dllimport) unsigned long __stdcall GetTickCount(); - +# ifdef BOOST_THREAD_WIN32_HAS_GET_TICK_COUNT_64 + __declspec(dllimport) ticks_type __stdcall GetTickCount64(); +# endif # ifndef UNDER_CE __declspec(dllimport) unsigned long __stdcall GetCurrentProcessId(); __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(); @@ -150,6 +173,9 @@ namespace boost using ::ResetEvent; # endif } +# ifndef BOOST_THREAD_WIN32_HAS_GET_TICK_COUNT_64 + inline ticks_type GetTickCount64() { return GetTickCount(); } +# endif } } } diff --git a/src/future.cpp b/src/future.cpp index d04f791dc..a9d88de3a 100755 --- a/src/future.cpp +++ b/src/future.cpp @@ -48,13 +48,13 @@ namespace boost } return std::string("unspecified future_errc value\n"); } + future_error_category future_error_category_var; } const system::error_category& future_category() BOOST_NOEXCEPT { - static thread_detail::future_error_category f; - return f; + return thread_detail::future_error_category_var; } } diff --git a/src/pthread/once_atomic.cpp b/src/pthread/once_atomic.cpp index c700c83bd..b7ee1dc5b 100644 --- a/src/pthread/once_atomic.cpp +++ b/src/pthread/once_atomic.cpp @@ -4,7 +4,7 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#define __STDC_CONSTANT_MACROS +//#define __STDC_CONSTANT_MACROS #include #include #include @@ -24,34 +24,17 @@ namespace boost uninitialized, in_progress, initialized }; -#if BOOST_ATOMIC_INT_LOCK_FREE == 2 - typedef unsigned int atomic_int_type; -#elif BOOST_ATOMIC_SHORT_LOCK_FREE == 2 - typedef unsigned short atomic_int_type; -#elif BOOST_ATOMIC_CHAR_LOCK_FREE == 2 - typedef unsigned char atomic_int_type; -#elif BOOST_ATOMIC_LONG_LOCK_FREE == 2 - typedef unsigned long atomic_int_type; -#elif defined(BOOST_HAS_LONG_LONG) && BOOST_ATOMIC_LLONG_LOCK_FREE == 2 - typedef ulong_long_type atomic_int_type; -#else - // All tested integer types are not atomic, the spinlock pool will be used - typedef unsigned int atomic_int_type; -#endif - typedef boost::atomic atomic_type - //#if defined(__GNUC__) - // __attribute__((may_alias)) - //#endif - ; - BOOST_STATIC_ASSERT_MSG(sizeof(once_flag) >= sizeof(atomic_type), "Boost.Thread: unsupported platform"); +#ifndef BOOST_THREAD_PROVIDES_ONCE_CXX11 + BOOST_STATIC_ASSERT_MSG(sizeof(atomic_int_type) == sizeof(atomic_type), "Boost.Thread: unsupported platform"); +#endif static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t once_cv = PTHREAD_COND_INITIALIZER; BOOST_THREAD_DECL bool enter_once_region(once_flag& flag) BOOST_NOEXCEPT { - atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); + atomic_type& f = get_atomic_storage(flag); if (f.load(memory_order_acquire) != initialized) { pthread::pthread_mutex_scoped_lock lk(&once_mutex); @@ -84,7 +67,7 @@ namespace boost BOOST_THREAD_DECL void commit_once_region(once_flag& flag) BOOST_NOEXCEPT { - atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); + atomic_type& f = get_atomic_storage(flag); { pthread::pthread_mutex_scoped_lock lk(&once_mutex); f.store(initialized, memory_order_release); @@ -94,7 +77,7 @@ namespace boost BOOST_THREAD_DECL void rollback_once_region(once_flag& flag) BOOST_NOEXCEPT { - atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); + atomic_type& f = get_atomic_storage(flag); { pthread::pthread_mutex_scoped_lock lk(&once_mutex); f.store(uninitialized, memory_order_release); diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index 7aa8afcec..28dd8d410 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -402,7 +402,8 @@ namespace boost unsigned thread::hardware_concurrency() BOOST_NOEXCEPT { - SYSTEM_INFO info={{0}}; + //SYSTEM_INFO info={{0}}; + SYSTEM_INFO info; GetSystemInfo(&info); return info.dwNumberOfProcessors; } @@ -425,7 +426,7 @@ namespace boost { LARGE_INTEGER get_due_time(detail::timeout const& target_time) { - LARGE_INTEGER due_time={{0}}; + LARGE_INTEGER due_time={{0,0}}; if(target_time.relative) { unsigned long const elapsed_milliseconds=GetTickCount()-target_time.start; @@ -439,7 +440,7 @@ namespace boost } else { - SYSTEMTIME target_system_time={0}; + SYSTEMTIME target_system_time={0,0,0,0,0,0,0,0}; target_system_time.wYear=target_time.abs_time.date().year(); target_system_time.wMonth=target_time.abs_time.date().month(); target_system_time.wDay=target_time.abs_time.date().day(); @@ -748,4 +749,3 @@ namespace boost } } - diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 2d4121535..ec8fef738 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -31,12 +31,18 @@ project gcc:-Wno-long-long #gcc:-ansi #gcc:-fpermissive + gcc:-Wno-variadic-macros + #gcc:-Wunused-local-typedefs + gcc:-Wunused-function darwin:-Wextra darwin:-pedantic darwin:-Wno-long-long #darwin:-ansi # doesn't work for 4.1.2 darwin:-fpermissive + darwin:-Wno-variadic-macros + #darwin:-Wunused-local-typedefs + darwin:-Wunused-function #pathscale:-Wextra pathscale:-Wno-long-long @@ -47,6 +53,7 @@ project clang:-Wno-long-long #clang:-ansi #clang:-fpermissive # doesn't work + clang:-Wunused-function gcc-mingw-4.4.0:-fdiagnostics-show-option gcc-mingw-4.5.0:-fdiagnostics-show-option @@ -127,7 +134,7 @@ rule thread-run2-noit ( sources : name ) : : : : $(name)_lib ] #[ run $(sources) ../build//boost_thread : : : - # BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS + # BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS # : $(name)_noit ] ; } @@ -141,7 +148,7 @@ rule thread-run2-noit-pthread ( sources : name ) : : : win32:no : $(name)_lib ] #[ run $(sources) ../build//boost_thread : : : - # BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS + # BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS # : $(name)_noit ] ; } @@ -149,10 +156,10 @@ rule thread-run2-noit-pthread ( sources : name ) rule thread-run2-h ( sources : name ) { return - [ run $(sources) : : : + [ run $(sources) : : : /boost/system//boost_system - BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS - BOOST_THREAD_VERSION=3 + BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS + BOOST_THREAD_VERSION=3 : $(name)_h ] ; } @@ -695,6 +702,26 @@ rule thread-compile-fail ( sources : reqs * : name ) [ thread-run2-noit ./sync/mutual_exclusion/locks/reverse_lock/types_pass.cpp : reverse_lock__types_p ] ; + + #explicit ts_synchronized_value ; + test-suite ts_synchronized_value + : + [ thread-run2-noit ./sync/mutual_exclusion/synchronized_value/copy_assign_pass.cpp : synchronized_value__copy_assign_p ] + [ thread-run2-noit ./sync/mutual_exclusion/synchronized_value/copy_ctor_pass.cpp : synchronized_value__copy_ctor_p ] + [ thread-run2-noit ./sync/mutual_exclusion/synchronized_value/copy_T_assign_pass.cpp : synchronized_value__copy_T_assign_p ] + [ thread-run2-noit ./sync/mutual_exclusion/synchronized_value/copy_T_ctor_pass.cpp : synchronized_value__copy_T_ctor_p ] + [ thread-run2-noit ./sync/mutual_exclusion/synchronized_value/default_ctor_pass.cpp : synchronized_value__default_ctor_p ] + [ thread-run2-noit ./sync/mutual_exclusion/synchronized_value/indirect_pass.cpp : synchronized_value__indirect_p ] + [ thread-run2-noit ./sync/mutual_exclusion/synchronized_value/move_assign_pass.cpp : synchronized_value__move_assign_p ] + [ thread-run2-noit ./sync/mutual_exclusion/synchronized_value/move_ctor_pass.cpp : synchronized_value__move_ctor_p ] + [ thread-run2-noit ./sync/mutual_exclusion/synchronized_value/move_T_assign_pass.cpp : synchronized_value__move_T_assign_p ] + [ thread-run2-noit ./sync/mutual_exclusion/synchronized_value/move_T_ctor_pass.cpp : synchronized_value__move_T_ctor_p ] + [ thread-run2-noit ./sync/mutual_exclusion/synchronized_value/swap_pass.cpp : synchronized_value__swap_p ] + [ thread-run2-noit ./sync/mutual_exclusion/synchronized_value/swap_T_pass.cpp : synchronized_value__swap_T_p ] + [ thread-run2-noit ./sync/mutual_exclusion/synchronized_value/synchronize_pass.cpp : synchronized_value__synchronize_p ] + + ; + explicit ts_ ; test-suite ts_ : @@ -703,11 +730,14 @@ rule thread-compile-fail ( sources : reqs * : name ) #[ thread-run ../example/test_so2.cpp ] #[ compile virtual_noexcept.cpp ] - #[ thread-run test_7665.cpp ] + #[ thread-run test_7720.cpp ] #[ thread-run test_7666.cpp ] + #[ thread-run test_7755.cpp ] #[ thread-run ../example/unwrap.cpp ] - [ thread-run ../example/perf_condition_variable.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/packaged_task/make_ready_at_thread_exit_pass.cpp b/test/sync/futures/packaged_task/make_ready_at_thread_exit_pass.cpp index 9c15d0e37..08db7bbf2 100644 --- a/test/sync/futures/packaged_task/make_ready_at_thread_exit_pass.cpp +++ b/test/sync/futures/packaged_task/make_ready_at_thread_exit_pass.cpp @@ -57,12 +57,17 @@ class A } }; +void func0_mv(BOOST_THREAD_RV_REF(boost::packaged_task) p) +//void func0(boost::packaged_task p) +{ + boost::this_thread::sleep_for(boost::chrono::milliseconds(500)); + p.make_ready_at_thread_exit(3, 'a'); +} void func0(boost::packaged_task *p) { boost::this_thread::sleep_for(boost::chrono::milliseconds(500)); p->make_ready_at_thread_exit(3, 'a'); } - void func1(boost::packaged_task *p) { boost::this_thread::sleep_for(boost::chrono::milliseconds(500)); @@ -99,8 +104,11 @@ int main() { boost::packaged_task p(A(5)); boost::future f = p.get_future(); - // fixme BUG boost::thread(func0, boost::move(p)).detach(); +#if defined BOOST_THREAD_PROVIDES_VARIADIC_THREAD + boost::thread(func0_mv, boost::move(p)).detach(); +#else boost::thread(func0, &p).detach(); +#endif BOOST_TEST(f.get() == 105.0); } { diff --git a/test/sync/futures/promise/set_value_at_thread_exit_void_pass.cpp b/test/sync/futures/promise/set_value_at_thread_exit_void_pass.cpp index 4d9cb2766..74667d1cd 100644 --- a/test/sync/futures/promise/set_value_at_thread_exit_void_pass.cpp +++ b/test/sync/futures/promise/set_value_at_thread_exit_void_pass.cpp @@ -19,11 +19,8 @@ // void promise::set_value_at_thread_exit(); #define BOOST_THREAD_VERSION 4 -#define BOOST_THREAD_USES_LOG -#define BOOST_THREAD_USES_LOG_THREAD_ID #include -#include #include int i = 0; @@ -35,12 +32,17 @@ void func() i = 1; } -void func2(boost::promise p2) +void func2_mv(BOOST_THREAD_RV_REF(boost::promise) p2) { p2.set_value_at_thread_exit(); i = 2; } +void func2(boost::promise *p2) +{ + p2->set_value_at_thread_exit(); + i = 2; +} int main() { try @@ -51,7 +53,7 @@ int main() BOOST_TEST(i == 1); } - catch(std::exception ex) + catch(std::exception& ex) { BOOST_TEST(false); } @@ -70,7 +72,29 @@ int main() BOOST_TEST(i == 1); } - catch(std::exception ex) + catch(std::exception& ex) + { + std::cout << __FILE__ << ":" << __LINE__ << " " << ex.what() << std::endl; + BOOST_TEST(false); + } + catch(...) + { + BOOST_TEST(false); + } + + try + { + boost::promise p2; + boost::future f = p2.get_future(); +#if defined BOOST_THREAD_PROVIDES_VARIADIC_THREAD + boost::thread(func2_mv, boost::move(p2)).detach(); +#else + boost::thread(func2, &p2).detach(); +#endif + f.wait(); + BOOST_TEST(i == 2); + } + catch(std::exception& ex) { std::cout << __FILE__ << ":" << __LINE__ << " " << ex.what() << std::endl; BOOST_TEST(false); @@ -79,31 +103,6 @@ int main() { BOOST_TEST(false); } - // BUG when moving promise. fixme -// try -// { -// BOOST_THREAD_LOG << BOOST_THREAD_END_LOG -// boost::promise p2; // BUG -// BOOST_THREAD_LOG << BOOST_THREAD_END_LOG -// boost::future f = p2.get_future(); -// BOOST_THREAD_LOG << BOOST_THREAD_END_LOG -// boost::thread(func2, boost::move(p2)).detach(); // BUG -// BOOST_THREAD_LOG << BOOST_THREAD_END_LOG -// f.get(); -// BOOST_THREAD_LOG << BOOST_THREAD_END_LOG -// BOOST_TEST(i == 2); -// BOOST_THREAD_LOG << BOOST_THREAD_END_LOG -// -// } -// catch(std::exception ex) -// { -// std::cout << __FILE__ << ":" << __LINE__ << " " << ex.what() << std::endl; -// BOOST_TEST(false); -// } -// catch(...) -// { -// BOOST_TEST(false); -// } return boost::report_errors(); } diff --git a/test/sync/mutual_exclusion/once/call_once/call_once_pass.cpp b/test/sync/mutual_exclusion/once/call_once/call_once_pass.cpp index 0eb957d35..ddd1e0db8 100755 --- a/test/sync/mutual_exclusion/once/call_once/call_once_pass.cpp +++ b/test/sync/mutual_exclusion/once/call_once/call_once_pass.cpp @@ -109,11 +109,11 @@ int init1_member::called = 0; void f1_member() { init1_member o; -#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 +//#if defined BOOST_THREAD_PROVIDES_ONCE_CXX11 boost::call_once(flg1_member, &init1_member::call, o, 1); -#else - boost::call_once(flg1_member, boost::bind(&init1_member::call, o, 1)); -#endif +//#else +// boost::call_once(flg1_member, boost::bind(&init1_member::call, boost::ref(o), 1)); +//#endif } //#endif struct init2 @@ -248,12 +248,12 @@ int main() boost::once_flag f BOOST_INIT_ONCE_INIT; boost::call_once(f, MoveOnly()); } +#endif +#if defined BOOST_THREAD_PROVIDES_INVOKE { boost::once_flag f BOOST_INIT_ONCE_INIT; boost::call_once(f, MoveOnly(), 1); } -#endif -#if defined BOOST_THREAD_PROVIDES_INVOKE { boost::once_flag f BOOST_INIT_ONCE_INIT; boost::call_once(f, MoveOnly(), MoveOnly()); diff --git a/test/sync/mutual_exclusion/synchronized_value/copy_T_assign_pass.cpp b/test/sync/mutual_exclusion/synchronized_value/copy_T_assign_pass.cpp new file mode 100644 index 000000000..3b43f8f90 --- /dev/null +++ b/test/sync/mutual_exclusion/synchronized_value/copy_T_assign_pass.cpp @@ -0,0 +1,30 @@ +// 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) + +// + +// class synchronized_value + +// synchronized_value& operator=(T const&); + +#define BOOST_THREAD_VERSION 4 + +#include + +#include + +int main() +{ + + { + int i = 1; + boost::synchronized_value v; + v = i; + BOOST_TEST(v.value() == 1); + } + + return boost::report_errors(); +} + diff --git a/test/sync/mutual_exclusion/synchronized_value/copy_T_ctor_pass.cpp b/test/sync/mutual_exclusion/synchronized_value/copy_T_ctor_pass.cpp new file mode 100644 index 000000000..e395c57aa --- /dev/null +++ b/test/sync/mutual_exclusion/synchronized_value/copy_T_ctor_pass.cpp @@ -0,0 +1,29 @@ +// 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) + +// + +// class synchronized_value + +// synchronized_value(T const&); + +#define BOOST_THREAD_VERSION 4 + +#include + +#include + +int main() +{ + + { + int i = 1; + boost::synchronized_value v(i); + BOOST_TEST(v.value() == 1); + } + + return boost::report_errors(); +} + diff --git a/test/sync/mutual_exclusion/synchronized_value/copy_assign_pass.cpp b/test/sync/mutual_exclusion/synchronized_value/copy_assign_pass.cpp new file mode 100644 index 000000000..d4e80b0ce --- /dev/null +++ b/test/sync/mutual_exclusion/synchronized_value/copy_assign_pass.cpp @@ -0,0 +1,31 @@ +// 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) + +// + +// class synchronized_value + +// synchronized_value& operator=(synchronized_value const&); + +#define BOOST_THREAD_VERSION 4 + +#include + +#include + +int main() +{ + + { + int i = 1; + boost::synchronized_value v1(i); + boost::synchronized_value v2; + v2 = v1; + BOOST_TEST(v2.value() == 1); + } + + return boost::report_errors(); +} + diff --git a/test/sync/mutual_exclusion/synchronized_value/copy_ctor_pass.cpp b/test/sync/mutual_exclusion/synchronized_value/copy_ctor_pass.cpp new file mode 100644 index 000000000..13fcf6d80 --- /dev/null +++ b/test/sync/mutual_exclusion/synchronized_value/copy_ctor_pass.cpp @@ -0,0 +1,30 @@ +// 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) + +// + +// class synchronized_value + +// synchronized_value(synchronized_value const&); + +#define BOOST_THREAD_VERSION 4 + +#include + +#include + +int main() +{ + + { + int i = 1; + boost::synchronized_value v1(i); + boost::synchronized_value v2(v1); + BOOST_TEST(v2.value() == 1); + } + + return boost::report_errors(); +} + diff --git a/test/sync/mutual_exclusion/synchronized_value/default_ctor_pass.cpp b/test/sync/mutual_exclusion/synchronized_value/default_ctor_pass.cpp new file mode 100644 index 000000000..ef1df247b --- /dev/null +++ b/test/sync/mutual_exclusion/synchronized_value/default_ctor_pass.cpp @@ -0,0 +1,33 @@ +// 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) + +// + +// class synchronized_value + +// synchronized_value(); + +#define BOOST_THREAD_VERSION 4 + +#include +#include + +#include + +int main() +{ + + { + boost::synchronized_value > f; + BOOST_TEST(! f.mutex().is_locked()); + } + { + boost::synchronized_value > f; + BOOST_TEST(! f.mutex().is_locked()); + } + + return boost::report_errors(); +} + diff --git a/test/sync/mutual_exclusion/synchronized_value/indirect_pass.cpp b/test/sync/mutual_exclusion/synchronized_value/indirect_pass.cpp new file mode 100644 index 000000000..09b8e89d2 --- /dev/null +++ b/test/sync/mutual_exclusion/synchronized_value/indirect_pass.cpp @@ -0,0 +1,38 @@ +// 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) + +// + +// class synchronized_value + +// strict_lock_ptr operator->(); +// const_strict_lock_ptr operator->() const; + +#define BOOST_THREAD_VERSION 4 + +#include + +#include + +struct S { + int f() const {return 1;} + int g() {return 1;} +}; + +int main() +{ + { + boost::synchronized_value v; + BOOST_TEST(v->f()==1); + BOOST_TEST(v->g()==1); + } + { + const boost::synchronized_value v; + BOOST_TEST(v->f()==1); + } + + return boost::report_errors(); +} + diff --git a/test/sync/mutual_exclusion/synchronized_value/move_T_assign_pass.cpp b/test/sync/mutual_exclusion/synchronized_value/move_T_assign_pass.cpp new file mode 100644 index 000000000..146ed2e51 --- /dev/null +++ b/test/sync/mutual_exclusion/synchronized_value/move_T_assign_pass.cpp @@ -0,0 +1,29 @@ +// 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) + +// + +// class synchronized_value + +// synchronized_value& operator=(T &&); + +#define BOOST_THREAD_VERSION 4 + +#include + +#include + +int main() +{ + + { + boost::synchronized_value v; + v = 1; + BOOST_TEST(v.value() == 1); + } + + return boost::report_errors(); +} + diff --git a/test/sync/mutual_exclusion/synchronized_value/move_T_ctor_pass.cpp b/test/sync/mutual_exclusion/synchronized_value/move_T_ctor_pass.cpp new file mode 100644 index 000000000..564d57da8 --- /dev/null +++ b/test/sync/mutual_exclusion/synchronized_value/move_T_ctor_pass.cpp @@ -0,0 +1,28 @@ +// 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) + +// + +// class synchronized_value + +// synchronized_value(T &&); + +#define BOOST_THREAD_VERSION 4 + +#include + +#include + +int main() +{ + + { + boost::synchronized_value v(1); + BOOST_TEST(v.value() == 1); + } + + return boost::report_errors(); +} + diff --git a/test/sync/mutual_exclusion/synchronized_value/move_assign_pass.cpp b/test/sync/mutual_exclusion/synchronized_value/move_assign_pass.cpp new file mode 100644 index 000000000..818216382 --- /dev/null +++ b/test/sync/mutual_exclusion/synchronized_value/move_assign_pass.cpp @@ -0,0 +1,30 @@ +// 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) + +// + +// class synchronized_value + +// synchronized_value& operator=(synchronized_value &&); + +#define BOOST_THREAD_VERSION 4 + +#include + +#include + +int main() +{ + + { + boost::synchronized_value v1(1); + boost::synchronized_value v2; + v2 = boost::move(v1); + BOOST_TEST(v2.value() == 1); + } + + return boost::report_errors(); +} + diff --git a/test/sync/mutual_exclusion/synchronized_value/move_ctor_pass.cpp b/test/sync/mutual_exclusion/synchronized_value/move_ctor_pass.cpp new file mode 100644 index 000000000..7250e896f --- /dev/null +++ b/test/sync/mutual_exclusion/synchronized_value/move_ctor_pass.cpp @@ -0,0 +1,29 @@ +// 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) + +// + +// class synchronized_value + +// synchronized_value(synchronized_value &&); + +#define BOOST_THREAD_VERSION 4 + +#include + +#include + +int main() +{ + + { + boost::synchronized_value v1(1); + boost::synchronized_value v2(boost::move(v1)); + BOOST_TEST(v2.value() == 1); + } + + return boost::report_errors(); +} + diff --git a/test/sync/mutual_exclusion/synchronized_value/swap_T_pass.cpp b/test/sync/mutual_exclusion/synchronized_value/swap_T_pass.cpp new file mode 100644 index 000000000..801b514d6 --- /dev/null +++ b/test/sync/mutual_exclusion/synchronized_value/swap_T_pass.cpp @@ -0,0 +1,38 @@ +// 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) + +// + +// class synchronized_value + +// void swap(synchronized_value&,synchronized_value&); + +#define BOOST_THREAD_VERSION 4 + +#include + +#include + +int main() +{ + + { + boost::synchronized_value v1(1); + int v2(2); + boost::swap(v1,v2); + BOOST_TEST(v1.value() == 2); + BOOST_TEST(v2 == 1); + } + { + boost::synchronized_value v1(1); + int v2(2); + boost::swap(v2,v1); + BOOST_TEST(v1.value() == 2); + BOOST_TEST(v2 == 1); + } + + return boost::report_errors(); +} + diff --git a/test/sync/mutual_exclusion/synchronized_value/swap_pass.cpp b/test/sync/mutual_exclusion/synchronized_value/swap_pass.cpp new file mode 100644 index 000000000..c52069ff3 --- /dev/null +++ b/test/sync/mutual_exclusion/synchronized_value/swap_pass.cpp @@ -0,0 +1,31 @@ +// 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) + +// + +// class synchronized_value + +// void swap(synchronized_value&,synchronized_value&); + +#define BOOST_THREAD_VERSION 4 + +#include + +#include + +int main() +{ + + { + boost::synchronized_value v1(1); + boost::synchronized_value v2(2); + swap(v1,v2); + BOOST_TEST(v1.value() == 2); + BOOST_TEST(v2.value() == 1); + } + + return boost::report_errors(); +} + diff --git a/test/sync/mutual_exclusion/synchronized_value/synchronize_pass.cpp b/test/sync/mutual_exclusion/synchronized_value/synchronize_pass.cpp new file mode 100644 index 000000000..af24d79b6 --- /dev/null +++ b/test/sync/mutual_exclusion/synchronized_value/synchronize_pass.cpp @@ -0,0 +1,41 @@ +// 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) + +// + +// class synchronized_value + +// strict_lock_ptr synchronize(); +// const_strict_lock_ptr synchronize() const; + + +#define BOOST_THREAD_VERSION 4 + +#include + +#include + +struct S { + int f() const {return 1;} + int g() {return 1;} +}; + +int main() +{ + { + boost::synchronized_value v; + boost::strict_lock_ptr ptr = v.synchronize(); + BOOST_TEST(ptr->f()==1); + BOOST_TEST(ptr->g()==1); + } + { + const boost::synchronized_value v; + boost::const_strict_lock_ptr ptr = v.synchronize(); + BOOST_TEST(ptr->f()==1); + } + + return boost::report_errors(); +} + diff --git a/test/test_7720.cpp b/test/test_7720.cpp new file mode 100644 index 000000000..c746fa1a0 --- /dev/null +++ b/test/test_7720.cpp @@ -0,0 +1,52 @@ +// Copyright (C) 2013 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) + +//////////////////////////////////////////// + +//#define BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN +#include +#include +using namespace boost; + +shared_mutex mtx; + +void f() +{ + while (true) + { + upgrade_lock lock(mtx); + } +} + +void g() +{ + while (true) + { + shared_lock lock(mtx); + } +} + +void h() +{ + while (true) + { + unique_lock lock(mtx); + } +} + +int main() +{ + thread t0(f); + thread t1(g); + thread t2(h); + + t0.join(); + t1.join(); + t2.join(); + + return 0; +} + + diff --git a/test/test_7755.cpp b/test/test_7755.cpp new file mode 100644 index 000000000..4b2010cfb --- /dev/null +++ b/test/test_7755.cpp @@ -0,0 +1,94 @@ +// Copyright (C) 2013 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) + +//////////////////////////////////////////// + +//#define BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN +#include +#include +#include +// shared_mutex_deadlock.cpp : Defines the entry point for the console application. +// + + +boost::shared_mutex mutex; + +void thread2_func() +{ + int i (0); + while (true) + { + if (mutex.timed_lock(boost::posix_time::milliseconds(500))) + { + std::cout << "Unique lock acquired" << std::endl + << "Test successful" << std::endl; + mutex.unlock(); + break; + } + ++i; + if (i == 100) + { + std::cout << "Test failed. App is deadlocked" << std::endl; + break; + } + boost::this_thread::sleep(boost::posix_time::seconds(1)); + } +} + +void thread3_func() +{ + boost::shared_lock lock (mutex); + std::cout << "Shared lock acquired" << std::endl + << "Test successful" << std::endl; +} + +void thread3_func_workaround() +{ + while (true) + { + if (mutex.timed_lock_shared(boost::posix_time::milliseconds(200))) + { + std::cout << "Shared lock acquired" << std::endl + << "Test successful" << std::endl; + mutex.unlock_shared(); + break; + } + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } +} + +int main() +{ + std::cout << "Starting" << std::endl; + + // 1 - lock the mutex + boost::shared_lock lock (mutex); + + // 2 - start thread#2 + boost::thread thread2(&thread2_func); + + // 3 - sleep + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + + std::cout << "Thread#2 is waiting" << std::endl; + + // - start thread3 + boost::thread thread3(&thread3_func); + //boost::thread thread3(&thread3_func_workaround); + + std::cout << "Thread#3 is started and blocked. It never will waked" << std::endl; + + thread3.join(); // will never return + + lock.unlock(); // release shared ownership. thread#2 will take unique ownership + + thread2.join(); + + std::cout << "Finished" << std::endl; + return 0; +} + + + diff --git a/test/test_barrier.cpp b/test/test_barrier.cpp index e0692f53b..675992995 100644 --- a/test/test_barrier.cpp +++ b/test/test_barrier.cpp @@ -11,7 +11,8 @@ #include #include -#include +#include +//#include #include namespace { @@ -54,16 +55,21 @@ void test_barrier() throw; } - BOOST_CHECK_EQUAL(global_parameter,5); + //BOOST_CHECK_EQUAL(global_parameter,5); + BOOST_TEST(global_parameter==5); + } -boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +//boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +int main() { - boost::unit_test::test_suite* test = - BOOST_TEST_SUITE("Boost.Threads: barrier test suite"); - - test->add(BOOST_TEST_CASE(&test_barrier)); +// boost::unit_test::test_suite* test = +// BOOST_TEST_SUITE("Boost.Threads: barrier test suite"); +// +// test->add(BOOST_TEST_CASE(&test_barrier)); - return test; + test_barrier(); + return boost::report_errors(); + //return test; }