diff --git a/build/Jamfile.v2 b/build/Jamfile.v2
index 97c38969..9a7e7fb6 100644
--- a/build/Jamfile.v2
+++ b/build/Jamfile.v2
@@ -115,6 +115,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/system//boost_system
;
diff --git a/doc/condition_variables.qbk b/doc/condition_variables.qbk
index 88deee1c..f606ea5a 100644
--- a/doc/condition_variables.qbk
+++ b/doc/condition_variables.qbk
@@ -346,8 +346,8 @@ the thread is unblocked (for whatever reason), the lock is reacquired by
invoking `lock.lock()` before the call to `wait` returns. The lock is also
reacquired by invoking `lock.lock()` if the function exits with an exception.]]
-[[Returns:] [`cv_status::no_timeout` if the call is returning because the time specified by
-`abs_time` was reached, `cv_status::timeout` otherwise.]]
+[[Returns:] [`cv_status::timeout` if the call is returning because the time specified by
+`abs_time` was reached, `cv_status::no_timeout` otherwise.]]
[[Postcondition:] [`lock` is locked by the current thread.]]
@@ -378,8 +378,8 @@ reason), the lock is reacquired by invoking `lock.lock()` before the call to
`wait` returns. The lock is also reacquired by invoking `lock.lock()` if the
function exits with an exception.]]
-[[Returns:] [`cv_status::no_timeout ` if the call is returning because the time period specified
-by `rel_time` has elapsed, `cv_status::timeout ` otherwise.]]
+[[Returns:] [`cv_status::timeout ` if the call is returning because the time period specified
+by `rel_time` has elapsed, `cv_status::no_timeout ` otherwise.]]
[[Postcondition:] [`lock` is locked by the current thread.]]
diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk
index 7458448c..a54aa382 100644
--- a/doc/thread_ref.qbk
+++ b/doc/thread_ref.qbk
@@ -1501,8 +1501,7 @@ specified by `rel_time` has elapsed or the time point specified by
[variablelist
-[[Effects:] [Suspends the current thread until the time period
-specified by `rel_time` has elapsed or the time point specified by
+[[Effects:] [Suspends the current thread until the time point specified by
`abs_time` has been reached.]]
[[Throws:] [Nothing if Clock satisfies the TrivialClock requirements and operations of Duration
@@ -1526,8 +1525,8 @@ do not throw exceptions. __thread_interrupted__ if the current thread of executi
[variablelist
-[[Effects:] [Suspends the current thread until the time point specified by
-`abs_time` has been reached.]]
+[[Effects:] [Suspends the current thread until the duration specified by
+by `rel_time` has elapsed.]]
[[Throws:] [Nothing if operations of chrono::duration do not throw exceptions. __thread_interrupted__ if the current thread of execution is interrupted.]]
diff --git a/example/perf_condition_variable.cpp b/example/perf_condition_variable.cpp
new file mode 100644
index 00000000..81816dca
--- /dev/null
+++ b/example/perf_condition_variable.cpp
@@ -0,0 +1,237 @@
+// (C) Copyright 2012 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)
+//
+// This performance test is based on the performance test provided by maxim.yegorushkin
+// at https://svn.boost.org/trac/boost/ticket/7422
+
+#define BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace
+{
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+// class Stopwatch
+// {
+// public:
+// typedef long long rep;
+//
+// static rep now()
+// {
+// timespec ts;
+// if (clock_gettime(CLOCK_MONOTONIC, &ts)) abort();
+// return ts.tv_sec * rep(1000000000) + ts.tv_nsec;
+// }
+//
+// Stopwatch() :
+// start_(now())
+// {
+// }
+//
+// rep elapsed() const
+// {
+// return now() - start_;
+// }
+//
+// private:
+// rep start_;
+// };
+
+ typedef boost::chrono::simple_stopwatch<> Stopwatch;
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ struct BoostTypes
+ {
+ typedef boost::condition_variable condition_variable;
+ typedef boost::mutex mutex;
+ typedef boost::mutex::scoped_lock scoped_lock;
+ };
+
+ struct StdTypes
+ {
+ typedef std::condition_variable condition_variable;
+ typedef std::mutex mutex;
+ typedef std::unique_lock scoped_lock;
+ };
+
+ template
+ struct SharedData: Types
+ {
+ unsigned const iterations;
+ unsigned counter;
+ unsigned semaphore;
+ typename Types::condition_variable cnd;
+ typename Types::mutex mtx;
+ Stopwatch::rep producer_time;
+
+ SharedData(unsigned iterations, unsigned consumers) :
+ iterations(iterations), counter(), semaphore(consumers) // Initialize to the number of consumers. (*)
+ , producer_time()
+ {
+ }
+ };
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ template
+ void producer_thread(S* shared_data)
+ {
+ Stopwatch sw;
+
+ unsigned const consumers = shared_data->semaphore; // (*)
+ for (unsigned i = shared_data->iterations; i--;)
+ {
+ {
+ typename S::scoped_lock lock(shared_data->mtx);
+ // Wait till all consumers signal.
+ while (consumers != shared_data->semaphore)
+ {
+ shared_data->cnd.wait(lock);
+ }
+ shared_data->semaphore = 0;
+ // Signal consumers.
+ ++shared_data->counter;
+ }
+ shared_data->cnd.notify_all();
+ }
+
+ shared_data->producer_time = sw.elapsed().count();
+ }
+
+ template
+ void consumer_thread(S* shared_data)
+ {
+ unsigned counter = 0;
+ while (counter != shared_data->iterations)
+ {
+ {
+ typename S::scoped_lock lock(shared_data->mtx);
+ // Wait till the producer signals.
+ while (counter == shared_data->counter)
+ {
+ shared_data->cnd.wait(lock);
+ }
+ counter = shared_data->counter;
+ // Signal the producer.
+ ++shared_data->semaphore;
+ }
+ shared_data->cnd.notify_all();
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ template
+ Stopwatch::rep benchmark_ping_pong(unsigned consumer_count)
+ {
+ typedef SharedData S;
+
+ auto best_producer_time = std::numeric_limits::max();
+
+ std::vector consumers
+ { consumer_count };
+
+ // Run the benchmark 10 times and report the best time.
+ for (int times = 10; times--;)
+ {
+ S shared_data
+ { 100000, consumer_count };
+
+ // Start the consumers.
+ for (unsigned i = 0; i < consumer_count; ++i)
+ consumers[i] = std::thread
+ { consumer_thread , &shared_data };
+ // Start the producer and wait till it finishes.
+ std::thread
+ { producer_thread , &shared_data }.join();
+ // Wait till consumers finish.
+ for (unsigned i = 0; i < consumer_count; ++i)
+ consumers[i].join();
+
+ best_producer_time = std::min(best_producer_time, shared_data.producer_time);
+
+ }
+
+ return best_producer_time;
+ }
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+// sudo chrt -f 99 /usr/bin/time -f "\n***\ntime: %E\ncontext switches: %c\nwaits: %w" /home/max/otsquant/build/Linux-x86_64-64.g++-release/test/test
+
+/*
+
+ Producer-consumer ping-pong tests. It aims to benchmark condition variables with and without
+ thread cancellation support by comparing the time it took to complete the benchmark.
+
+ Condition variable with thread cancellation support is boost::condition_variable from
+ boost-1.51. Without - std::condition_variable that comes with gcc-4.7.2.
+
+ One producer, one to CONSUMER_MAX consumers. The benchmark calls
+ condition_variable::notify_all() without holding a mutex to maximize contention within this
+ function. Each benchmark for a number of consumers is run three times and the best time is
+ picked to get rid of outliers.
+
+ The results are reported for each benchmark for a number of consumers. The most important number
+ is (std - boost) / std * 100. Positive numbers are when boost::condition_variable is faster,
+ negative it is slower.
+
+ */
+
+int main()
+{
+ std::printf("MAIN\n");
+ enum
+ {
+ CONSUMER_MAX = 2
+ };
+
+ struct
+ {
+ Stopwatch::rep boost, std;
+ } best_times[CONSUMER_MAX] = {};
+
+ for (unsigned i = 1; i <= CONSUMER_MAX; ++i)
+ {
+ auto& b = best_times[i - 1];
+ std::printf("STD: %d\n", i);
+ b.std = benchmark_ping_pong (i);
+ std::printf("BOOST: %d\n", i);
+ b.boost = benchmark_ping_pong (i);
+
+ std::printf("consumers: %4d\n", i);
+ std::printf("best std producer time: %15.9fsec\n", b.std * 1e-9);
+ std::printf("best boost producer time: %15.9fsec\n", b.boost * 1e-9);
+ std::printf("(std - boost) / std: %7.2f%%\n", (b.std - b.boost) * 100. / b.std);
+ }
+
+ printf("\ncsv:\n\n");
+ printf("consumers,(std-boost)/std,std,boost\n");
+ for (unsigned i = 1; i <= CONSUMER_MAX; ++i)
+ {
+ auto& b = best_times[i - 1];
+ printf("%d,%f,%lld,%lld\n", i, (b.std - b.boost) * 100. / b.std, b.std, b.boost);
+ }
+ return 1;
+}
diff --git a/include/boost/thread/pthread/condition_variable.hpp b/include/boost/thread/pthread/condition_variable.hpp
index 79c75373..5edf1b93 100644
--- a/include/boost/thread/pthread/condition_variable.hpp
+++ b/include/boost/thread/pthread/condition_variable.hpp
@@ -57,18 +57,28 @@ namespace boost
inline void condition_variable::wait(unique_lock& m)
{
+#if defined BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
+ if(! m.owns_lock())
+ {
+ boost::throw_exception(condition_error(-1, "boost::condition_variable::wait precondition"));
+ }
+#endif
int res=0;
{
- thread_cv_detail::lock_on_exit > guard;
#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
+ thread_cv_detail::lock_on_exit > guard;
detail::interruption_checker check_for_interruption(&internal_mutex,&cond);
-#else
- boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex);
-#endif
guard.activate(m);
do {
res = pthread_cond_wait(&cond,&internal_mutex);
} while (res == EINTR);
+#else
+ //boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex);
+ pthread_mutex_t* the_mutex = m.mutex()->native_handle();
+ do {
+ res = pthread_cond_wait(&cond,the_mutex);
+ } while (res == EINTR);
+#endif
}
#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
this_thread::interruption_point();
@@ -83,21 +93,24 @@ namespace boost
unique_lock& m,
struct timespec const &timeout)
{
+#if defined BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
if (!m.owns_lock())
{
boost::throw_exception(condition_error(EPERM, "condition_variable do_wait_until: mutex not locked"));
}
-
+#endif
thread_cv_detail::lock_on_exit > guard;
int cond_res;
{
#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
detail::interruption_checker check_for_interruption(&internal_mutex,&cond);
-#else
- boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex);
-#endif
guard.activate(m);
cond_res=pthread_cond_timedwait(&cond,&internal_mutex,&timeout);
+#else
+ //boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex);
+ pthread_mutex_t* the_mutex = m.mutex()->native_handle();
+ cond_res=pthread_cond_timedwait(&cond,the_mutex,&timeout);
+#endif
}
#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
this_thread::interruption_point();
@@ -115,13 +128,17 @@ namespace boost
inline void condition_variable::notify_one() BOOST_NOEXCEPT
{
+#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex);
+#endif
BOOST_VERIFY(!pthread_cond_signal(&cond));
}
inline void condition_variable::notify_all() BOOST_NOEXCEPT
{
+#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex);
+#endif
BOOST_VERIFY(!pthread_cond_broadcast(&cond));
}
diff --git a/include/boost/thread/pthread/condition_variable_fwd.hpp b/include/boost/thread/pthread/condition_variable_fwd.hpp
index 13de9983..f67dd5fe 100644
--- a/include/boost/thread/pthread/condition_variable_fwd.hpp
+++ b/include/boost/thread/pthread/condition_variable_fwd.hpp
@@ -32,7 +32,9 @@ namespace boost
class condition_variable
{
private:
+#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
pthread_mutex_t internal_mutex;
+#endif
pthread_cond_t cond;
public:
@@ -53,25 +55,31 @@ namespace boost
BOOST_THREAD_NO_COPYABLE(condition_variable)
condition_variable()
{
+#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
int const res=pthread_mutex_init(&internal_mutex,NULL);
if(res)
{
boost::throw_exception(thread_resource_error(res, "boost:: condition_variable constructor failed in pthread_mutex_init"));
}
+#endif
int const res2=pthread_cond_init(&cond,NULL);
if(res2)
{
+#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
BOOST_VERIFY(!pthread_mutex_destroy(&internal_mutex));
+#endif
boost::throw_exception(thread_resource_error(res2, "boost:: condition_variable constructor failed in pthread_cond_init"));
}
}
~condition_variable()
{
int ret;
+#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
do {
ret = pthread_mutex_destroy(&internal_mutex);
} while (ret == EINTR);
BOOST_ASSERT(!ret);
+#endif
do {
ret = pthread_cond_destroy(&cond);
} while (ret == EINTR);
diff --git a/test/Jamfile.v2 b/test/Jamfile.v2
index ad26a991..9137cbcd 100644
--- a/test/Jamfile.v2
+++ b/test/Jamfile.v2
@@ -126,9 +126,9 @@ rule thread-run2-noit ( sources : name )
[ run $(sources) ../src/tss_null.cpp ../build//boost_thread/static
: : :
: $(name)_lib ]
- [ run $(sources) ../build//boost_thread : : :
- BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS
- : $(name)_noit ]
+ #[ run $(sources) ../build//boost_thread : : :
+ # BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS
+ # : $(name)_noit ]
;
}
@@ -313,7 +313,7 @@ rule thread-compile-fail ( sources : reqs * : name )
[ thread-run2-noit ./sync/futures/future/move_ctor_pass.cpp : future__move_ctor_p ]
[ thread-run2-noit ./sync/futures/future/move_assign_pass.cpp : future__move_asign_p ]
[ thread-run2-noit ./sync/futures/future/share_pass.cpp : future__share_p ]
- [ thread-run2-noit ./sync/futures/future/then_pass.cpp : future__then_p ]
+ #[ thread-run2-noit ./sync/futures/future/then_pass.cpp : future__then_p ]
;
#explicit ts_shared_future ;
@@ -617,9 +617,9 @@ rule thread-compile-fail ( sources : reqs * : name )
#[ thread-run ../example/vhh_shared_monitor.cpp ]
#[ thread-run ../example/vhh_shared_mutex.cpp ]
[ thread-run ../example/make_future.cpp ]
- [ thread-run ../example/future_then.cpp ]
- [ thread-run2-noit ../example/synchronized_value.cpp : ex_synchronized_value ]
- [ thread-run2-noit ../example/synchronized_person.cpp : ex_synchronized_person ]
+ #[ thread-run ../example/future_then.cpp ]
+ #[ thread-run2-noit ../example/synchronized_value.cpp : ex_synchronized_value ]
+ #[ thread-run2-noit ../example/synchronized_person.cpp : ex_synchronized_person ]
[ thread-run2-noit ../example/thread_guard.cpp : ex_thread_guard ]
[ thread-run2-noit ../example/scoped_thread.cpp : ex_scoped_thread ]
[ thread-run2-noit ../example/strict_lock.cpp : ex_strict_lock ]
@@ -662,6 +662,7 @@ rule thread-compile-fail ( sources : reqs * : name )
explicit ts_ ;
test-suite ts_
:
+
#[ thread-run2-noit ./sync/futures/future/then_pass.cpp : future__then_p ]
#[ thread-run ../example/test_so.cpp ]
#[ thread-run ../example/test_so2.cpp ]
@@ -670,6 +671,7 @@ rule thread-compile-fail ( sources : reqs * : name )
#[ thread-run test_7665.cpp ]
#[ thread-run test_7666.cpp ]
#[ thread-run ../example/unwrap.cpp ]
+ [ thread-run ../example/perf_condition_variable.cpp ]
;
}