From 0997fad8ec6f8b0e86088b59bbdb1f8332876085 Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Mon, 13 Oct 2008 20:30:13 +0000 Subject: [PATCH] Merged Boost.Thread changes from trunk [SVN r49324] --- doc/changes.qbk | 3 + include/boost/thread/detail/move.hpp | 4 +- include/boost/thread/detail/thread.hpp | 4 +- include/boost/thread/locks.hpp | 6 + .../boost/thread/pthread/recursive_mutex.hpp | 2 +- include/boost/thread/pthread/shared_mutex.hpp | 46 +-- .../thread/win32/basic_recursive_mutex.hpp | 10 - .../boost/thread/win32/basic_timed_mutex.hpp | 10 - src/win32/thread.cpp | 22 +- src/win32/tss_pe.cpp | 46 ++- test/test_mutex.cpp | 84 ++++ test/test_thread_move.cpp | 47 ++- test/util.inl | 366 +++++++++--------- 13 files changed, 395 insertions(+), 255 deletions(-) diff --git a/doc/changes.qbk b/doc/changes.qbk index baa24fa6..25ae1472 100644 --- a/doc/changes.qbk +++ b/doc/changes.qbk @@ -77,4 +77,7 @@ been moved to __thread_id__. * __mutex__ is now never recursive. For Boost releases prior to 1.35 __mutex__ was recursive on Windows and not on POSIX platforms. +* When using a __recursive_mutex__ with a call to [cond_any_wait_link `boost::condition_variable_any::wait()`], the mutex is only + unlocked one level, and not completely. This prior behaviour was not guaranteed and did not feature in the tests. + [endsect] diff --git a/include/boost/thread/detail/move.hpp b/include/boost/thread/detail/move.hpp index 91b2eda1..044ecda6 100644 --- a/include/boost/thread/detail/move.hpp +++ b/include/boost/thread/detail/move.hpp @@ -41,9 +41,9 @@ namespace boost #ifndef BOOST_NO_SFINAE template - typename enable_if >, detail::thread_move_t >::type move(T& t) + typename enable_if >, T >::type move(T& t) { - return t; + return T(detail::thread_move_t(t)); } #endif diff --git a/include/boost/thread/detail/thread.hpp b/include/boost/thread/detail/thread.hpp index 3d39d1df..fbb895d1 100644 --- a/include/boost/thread/detail/thread.hpp +++ b/include/boost/thread/detail/thread.hpp @@ -339,9 +339,9 @@ namespace boost return t; } #else - inline detail::thread_move_t move(detail::thread_move_t t) + inline thread move(detail::thread_move_t t) { - return t; + return thread(t); } #endif diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index 6e4341ad..abbfd75b 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -234,6 +234,12 @@ namespace boost { try_lock(); } + template + unique_lock(Mutex& m_,TimeDuration const& target_time): + m(&m_),is_locked(false) + { + timed_lock(target_time); + } unique_lock(Mutex& m_,system_time const& target_time): m(&m_),is_locked(false) { diff --git a/include/boost/thread/pthread/recursive_mutex.hpp b/include/boost/thread/pthread/recursive_mutex.hpp index 30689460..f3f7bf1d 100644 --- a/include/boost/thread/pthread/recursive_mutex.hpp +++ b/include/boost/thread/pthread/recursive_mutex.hpp @@ -177,7 +177,7 @@ namespace boost { struct timespec const timeout=detail::get_timespec(abs_time); int const res=pthread_mutex_timedlock(&m,&timeout); - BOOST_ASSERT(!res || res==EBUSY); + BOOST_ASSERT(!res || res==ETIMEDOUT); return !res; } diff --git a/include/boost/thread/pthread/shared_mutex.hpp b/include/boost/thread/pthread/shared_mutex.hpp index 74345d88..3ce4e233 100644 --- a/include/boost/thread/pthread/shared_mutex.hpp +++ b/include/boost/thread/pthread/shared_mutex.hpp @@ -57,18 +57,18 @@ namespace boost void lock_shared() { boost::this_thread::disable_interruption do_not_disturb; - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); while(state.exclusive || state.exclusive_waiting_blocked) { - shared_cond.wait(lock); + shared_cond.wait(lk); } ++state.shared_count; } bool try_lock_shared() { - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); if(state.exclusive || state.exclusive_waiting_blocked) { @@ -84,11 +84,11 @@ namespace boost bool timed_lock_shared(system_time const& timeout) { boost::this_thread::disable_interruption do_not_disturb; - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); while(state.exclusive || state.exclusive_waiting_blocked) { - if(!shared_cond.timed_wait(lock,timeout)) + if(!shared_cond.timed_wait(lk,timeout)) { return false; } @@ -105,7 +105,7 @@ namespace boost void unlock_shared() { - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); bool const last_reader=!--state.shared_count; if(last_reader) @@ -127,12 +127,12 @@ namespace boost void lock() { boost::this_thread::disable_interruption do_not_disturb; - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); while(state.shared_count || state.exclusive) { state.exclusive_waiting_blocked=true; - exclusive_cond.wait(lock); + exclusive_cond.wait(lk); } state.exclusive=true; } @@ -140,12 +140,12 @@ namespace boost bool timed_lock(system_time const& timeout) { boost::this_thread::disable_interruption do_not_disturb; - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); while(state.shared_count || state.exclusive) { state.exclusive_waiting_blocked=true; - if(!exclusive_cond.timed_wait(lock,timeout)) + if(!exclusive_cond.timed_wait(lk,timeout)) { if(state.shared_count || state.exclusive) { @@ -168,7 +168,7 @@ namespace boost bool try_lock() { - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); if(state.shared_count || state.exclusive) { @@ -184,7 +184,7 @@ namespace boost void unlock() { - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); state.exclusive=false; state.exclusive_waiting_blocked=false; release_waiters(); @@ -193,10 +193,10 @@ namespace boost void lock_upgrade() { boost::this_thread::disable_interruption do_not_disturb; - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); while(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) { - shared_cond.wait(lock); + shared_cond.wait(lk); } ++state.shared_count; state.upgrade=true; @@ -205,10 +205,10 @@ namespace boost bool timed_lock_upgrade(system_time const& timeout) { boost::this_thread::disable_interruption do_not_disturb; - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); while(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) { - if(!shared_cond.timed_wait(lock,timeout)) + if(!shared_cond.timed_wait(lk,timeout)) { if(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) { @@ -230,7 +230,7 @@ namespace boost bool try_lock_upgrade() { - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); if(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) { return false; @@ -245,7 +245,7 @@ namespace boost void unlock_upgrade() { - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); state.upgrade=false; bool const last_reader=!--state.shared_count; @@ -259,11 +259,11 @@ namespace boost void unlock_upgrade_and_lock() { boost::this_thread::disable_interruption do_not_disturb; - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); --state.shared_count; while(state.shared_count) { - upgrade_cond.wait(lock); + upgrade_cond.wait(lk); } state.upgrade=false; state.exclusive=true; @@ -271,7 +271,7 @@ namespace boost void unlock_and_lock_upgrade() { - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); state.exclusive=false; state.upgrade=true; ++state.shared_count; @@ -281,7 +281,7 @@ namespace boost void unlock_and_lock_shared() { - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); state.exclusive=false; ++state.shared_count; state.exclusive_waiting_blocked=false; @@ -290,7 +290,7 @@ namespace boost void unlock_upgrade_and_lock_shared() { - boost::mutex::scoped_lock lock(state_change); + boost::mutex::scoped_lock lk(state_change); state.upgrade=false; state.exclusive_waiting_blocked=false; release_waiters(); diff --git a/include/boost/thread/win32/basic_recursive_mutex.hpp b/include/boost/thread/win32/basic_recursive_mutex.hpp index 89c5f1dc..05eb8d76 100644 --- a/include/boost/thread/win32/basic_recursive_mutex.hpp +++ b/include/boost/thread/win32/basic_recursive_mutex.hpp @@ -64,11 +64,6 @@ namespace boost return timed_lock(get_system_time()+timeout); } - long get_active_count() - { - return mutex.get_active_count(); - } - void unlock() { if(!--recursion_count) @@ -78,11 +73,6 @@ namespace boost } } - bool locked() - { - return mutex.locked(); - } - private: bool try_recursive_lock(long current_thread_id) { diff --git a/include/boost/thread/win32/basic_timed_mutex.hpp b/include/boost/thread/win32/basic_timed_mutex.hpp index 00206061..751bdbda 100644 --- a/include/boost/thread/win32/basic_timed_mutex.hpp +++ b/include/boost/thread/win32/basic_timed_mutex.hpp @@ -123,11 +123,6 @@ namespace boost return timed_lock(system_time(timeout)); } - long get_active_count() - { - return ::boost::detail::interlocked_read_acquire(&active_count); - } - void unlock() { long const offset=lock_flag_value; @@ -141,11 +136,6 @@ namespace boost } } - bool locked() - { - return get_active_count()>=lock_flag_value; - } - private: void* get_event() { diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index e54b847d..9c4f82b0 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -29,13 +29,26 @@ namespace boost void create_current_thread_tls_key() { + tss_cleanup_implemented(); // if anyone uses TSS, we need the cleanup linked in current_thread_tls_key=TlsAlloc(); BOOST_ASSERT(current_thread_tls_key!=TLS_OUT_OF_INDEXES); } + void cleanup_tls_key() + { + if(current_thread_tls_key) + { + TlsFree(current_thread_tls_key); + current_thread_tls_key=0; + } + } + detail::thread_data_base* get_current_thread_data() { - boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); + if(!current_thread_tls_key) + { + return 0; + } return (detail::thread_data_base*)TlsGetValue(current_thread_tls_key); } @@ -141,8 +154,8 @@ namespace boost } } + set_current_thread_data(0); } - set_current_thread_data(0); } unsigned __stdcall thread_start_function(void* param) @@ -544,7 +557,6 @@ namespace boost void set_tss_data(void const* key,boost::shared_ptr func,void* tss_data,bool cleanup_existing) { - tss_cleanup_implemented(); // if anyone uses TSS, we need the cleanup linked in if(tss_data_node* const current_node=find_tss_data(key)) { if(cleanup_existing && current_node->func.get()) @@ -572,7 +584,9 @@ extern "C" BOOST_THREAD_DECL void on_thread_enter() {} extern "C" BOOST_THREAD_DECL void on_process_exit() -{} +{ + boost::cleanup_tls_key(); +} extern "C" BOOST_THREAD_DECL void on_thread_exit() { diff --git a/src/win32/tss_pe.cpp b/src/win32/tss_pe.cpp index bd2e61d8..ea831218 100644 --- a/src/win32/tss_pe.cpp +++ b/src/win32/tss_pe.cpp @@ -26,11 +26,11 @@ namespace { { switch (dwReason) { - case DLL_THREAD_DETACH: - { - on_thread_exit(); - break; - } + case DLL_THREAD_DETACH: + { + on_thread_exit(); + break; + } } } @@ -125,10 +125,10 @@ extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata #pragma section(".CRT$XCU",long,read) #pragma section(".CRT$XTU",long,read) #pragma section(".CRT$XLC",long,read) - static __declspec(allocate(".CRT$XLC")) _TLSCB __xl_ca=on_tls_callback; - static __declspec(allocate(".CRT$XIU"))_PVFV p_tls_prepare = on_tls_prepare; - static __declspec(allocate(".CRT$XCU"))_PVFV p_process_init = on_process_init; - static __declspec(allocate(".CRT$XTU"))_PVFV p_process_term = on_process_term; + __declspec(allocate(".CRT$XLC")) _TLSCB __xl_ca=on_tls_callback; + __declspec(allocate(".CRT$XIU"))_PVFV p_tls_prepare = on_tls_prepare; + __declspec(allocate(".CRT$XCU"))_PVFV p_process_init = on_process_init; + __declspec(allocate(".CRT$XTU"))_PVFV p_process_term = on_process_term; #else #if (_MSC_VER >= 1300) // 1300 == VC++ 7.0 # pragma data_seg(push, old_seg) @@ -168,6 +168,7 @@ extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata #pragma warning(push) #pragma warning(disable:4189) #endif + PVAPI on_tls_prepare(void) { //The following line has an important side effect: @@ -239,15 +240,32 @@ extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata { switch (dwReason) { - case DLL_THREAD_DETACH: - { - on_thread_exit(); - break; - } + case DLL_THREAD_DETACH: + on_thread_exit(); + break; } } + + BOOL WINAPI dll_callback(HANDLE, DWORD dwReason, LPVOID) + { + switch (dwReason) + { + case DLL_THREAD_DETACH: + on_thread_exit(); + break; + case DLL_PROCESS_DETACH: + on_process_exit(); + break; + } + return true; + } } //namespace +extern "C" +{ + extern BOOL (WINAPI * const _pRawDllMain)(HANDLE, DWORD, LPVOID)=&dll_callback; +} + extern "C" void tss_cleanup_implemented(void) { /* diff --git a/test/test_mutex.cpp b/test/test_mutex.cpp index e85459c4..8725a8fd 100644 --- a/test/test_mutex.cpp +++ b/test/test_mutex.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -96,6 +97,86 @@ struct test_trylock } }; +template +struct test_lock_times_out_if_other_thread_has_lock +{ + typedef boost::unique_lock Lock; + + Mutex m; + boost::mutex done_mutex; + bool done; + bool locked; + boost::condition_variable done_cond; + + test_lock_times_out_if_other_thread_has_lock(): + done(false),locked(false) + {} + + void locking_thread() + { + Lock lock(m,boost::defer_lock); + lock.timed_lock(boost::posix_time::milliseconds(50)); + + boost::lock_guard lk(done_mutex); + locked=lock.owns_lock(); + done=true; + done_cond.notify_one(); + } + + void locking_thread_through_constructor() + { + Lock lock(m,boost::posix_time::milliseconds(50)); + + boost::lock_guard lk(done_mutex); + locked=lock.owns_lock(); + done=true; + done_cond.notify_one(); + } + + bool is_done() const + { + return done; + } + + typedef test_lock_times_out_if_other_thread_has_lock this_type; + + void do_test(void (this_type::*test_func)()) + { + Lock lock(m); + + locked=false; + done=false; + + boost::thread t(test_func,this); + + try + { + { + boost::mutex::scoped_lock lk(done_mutex); + BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), + boost::bind(&this_type::is_done,this))); + BOOST_CHECK(!locked); + } + + lock.unlock(); + t.join(); + } + catch(...) + { + lock.unlock(); + t.join(); + throw; + } + } + + + void operator()() + { + do_test(&this_type::locking_thread); + do_test(&this_type::locking_thread_through_constructor); + } +}; + template struct test_timedlock { @@ -109,6 +190,8 @@ struct test_timedlock void operator()() { + test_lock_times_out_if_other_thread_has_lock()(); + mutex_type mutex; boost::condition condition; @@ -178,6 +261,7 @@ struct test_recursive_lock } }; + void do_test_mutex() { test_lock()(); diff --git a/test/test_thread_move.cpp b/test/test_thread_move.cpp index dc8f964f..550b62dd 100644 --- a/test/test_thread_move.cpp +++ b/test/test_thread_move.cpp @@ -5,26 +5,59 @@ #include #include -void do_nothing() -{} +void do_nothing(boost::thread::id* my_id) +{ + *my_id=boost::this_thread::get_id(); +} void test_move_on_construction() { - boost::thread x=boost::thread(do_nothing); + boost::thread::id the_id; + boost::thread x=boost::thread(do_nothing,&the_id); + boost::thread::id x_id=x.get_id(); x.join(); + BOOST_CHECK_EQUAL(the_id,x_id); } -boost::thread make_thread() +boost::thread make_thread(boost::thread::id* the_id) { - return boost::thread(do_nothing); + return boost::thread(do_nothing,the_id); } void test_move_from_function_return() { - boost::thread x=make_thread(); + boost::thread::id the_id; + boost::thread x=make_thread(&the_id); + boost::thread::id x_id=x.get_id(); x.join(); + BOOST_CHECK_EQUAL(the_id,x_id); } +boost::thread make_thread_return_lvalue(boost::thread::id* the_id) +{ + boost::thread t(do_nothing,the_id); + return boost::move(t); +} + +void test_move_from_function_return_lvalue() +{ + boost::thread::id the_id; + boost::thread x=make_thread_return_lvalue(&the_id); + boost::thread::id x_id=x.get_id(); + x.join(); + BOOST_CHECK_EQUAL(the_id,x_id); +} + +void test_move_assign() +{ + boost::thread::id the_id; + boost::thread x(do_nothing,&the_id); + boost::thread y; + y=boost::move(x); + boost::thread::id y_id=y.get_id(); + y.join(); + BOOST_CHECK_EQUAL(the_id,y_id); +} boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { @@ -33,5 +66,7 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) test->add(BOOST_TEST_CASE(test_move_on_construction)); test->add(BOOST_TEST_CASE(test_move_from_function_return)); + test->add(BOOST_TEST_CASE(test_move_from_function_return_lvalue)); + test->add(BOOST_TEST_CASE(test_move_assign)); return test; } diff --git a/test/util.inl b/test/util.inl index 5c761d50..60f88e05 100644 --- a/test/util.inl +++ b/test/util.inl @@ -1,183 +1,183 @@ -// Copyright (C) 2001-2003 -// William E. Kempf -// Copyright (C) 2007-8 Anthony Williams -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#if !defined(UTIL_INL_WEK01242003) -#define UTIL_INL_WEK01242003 - -#include -#include -#include -#include - -#ifndef DEFAULT_EXECUTION_MONITOR_TYPE -# define DEFAULT_EXECUTION_MONITOR_TYPE execution_monitor::use_condition -#endif - -// boostinspect:nounnamed - -namespace -{ -inline boost::xtime delay(int secs, int msecs=0, int nsecs=0) -{ - const int MILLISECONDS_PER_SECOND = 1000; - const int NANOSECONDS_PER_SECOND = 1000000000; - const int NANOSECONDS_PER_MILLISECOND = 1000000; - - boost::xtime xt; - if (boost::TIME_UTC != boost::xtime_get (&xt, boost::TIME_UTC)) - BOOST_ERROR ("boost::xtime_get != boost::TIME_UTC"); - - nsecs += xt.nsec; - msecs += nsecs / NANOSECONDS_PER_MILLISECOND; - secs += msecs / MILLISECONDS_PER_SECOND; - nsecs += (msecs % MILLISECONDS_PER_SECOND) * NANOSECONDS_PER_MILLISECOND; - xt.nsec = nsecs % NANOSECONDS_PER_SECOND; - xt.sec += secs + (nsecs / NANOSECONDS_PER_SECOND); - - return xt; -} - -inline bool in_range(const boost::xtime& xt, int secs=1) -{ - boost::xtime min = delay(-secs); - boost::xtime max = delay(0); - return (boost::xtime_cmp(xt, min) >= 0) && - (boost::xtime_cmp(xt, max) <= 0); -} - -class execution_monitor -{ -public: - enum wait_type { use_sleep_only, use_mutex, use_condition }; - - execution_monitor(wait_type type, int secs) - : done(false), type(type), secs(secs) { } - void start() - { - if (type != use_sleep_only) { - boost::mutex::scoped_lock lock(mutex); done = false; - } else { - done = false; - } - } - void finish() - { - if (type != use_sleep_only) { - boost::mutex::scoped_lock lock(mutex); - done = true; - if (type == use_condition) - cond.notify_one(); - } else { - done = true; - } - } - bool wait() - { - boost::xtime xt = delay(secs); - if (type != use_condition) - boost::thread::sleep(xt); - if (type != use_sleep_only) { - boost::mutex::scoped_lock lock(mutex); - while (type == use_condition && !done) { - if (!cond.timed_wait(lock, xt)) - break; - } - return done; - } - return done; - } - -private: - boost::mutex mutex; - boost::condition cond; - bool done; - wait_type type; - int secs; -}; - -template -class indirect_adapter -{ -public: - indirect_adapter(F func, execution_monitor& monitor) - : func(func), monitor(monitor) { } - void operator()() const - { - try - { - boost::thread thrd(func); - thrd.join(); - } - catch (...) - { - monitor.finish(); - throw; - } - monitor.finish(); - } - -private: - F func; - execution_monitor& monitor; - void operator=(indirect_adapter&); -}; - -template -void timed_test(F func, int secs, - execution_monitor::wait_type type=DEFAULT_EXECUTION_MONITOR_TYPE) -{ - execution_monitor monitor(type, secs); - indirect_adapter ifunc(func, monitor); - monitor.start(); - boost::thread thrd(ifunc); - BOOST_REQUIRE_MESSAGE(monitor.wait(), - "Timed test didn't complete in time, possible deadlock."); -} - -template -class thread_binder -{ -public: - thread_binder(const F& func, const T& param) - : func(func), param(param) { } - void operator()() const { func(param); } - -private: - F func; - T param; -}; - -template -thread_binder bind(const F& func, const T& param) -{ - return thread_binder(func, param); -} - -template -class thread_member_binder -{ -public: - thread_member_binder(R (T::*func)(), T& param) - : func(func), param(param) { } - void operator()() const { (param.*func)(); } - -private: - void operator=(thread_member_binder&); - - R (T::*func)(); - T& param; -}; - - -template -thread_member_binder bind(R (T::*func)(), T& param) -{ - return thread_member_binder(func, param); -} -} // namespace - -#endif +// Copyright (C) 2001-2003 +// William E. Kempf +// Copyright (C) 2007-8 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#if !defined(UTIL_INL_WEK01242003) +#define UTIL_INL_WEK01242003 + +#include +#include +#include +#include + +#ifndef DEFAULT_EXECUTION_MONITOR_TYPE +# define DEFAULT_EXECUTION_MONITOR_TYPE execution_monitor::use_condition +#endif + +// boostinspect:nounnamed + +namespace +{ +inline boost::xtime delay(int secs, int msecs=0, int nsecs=0) +{ + const int MILLISECONDS_PER_SECOND = 1000; + const int NANOSECONDS_PER_SECOND = 1000000000; + const int NANOSECONDS_PER_MILLISECOND = 1000000; + + boost::xtime xt; + if (boost::TIME_UTC != boost::xtime_get (&xt, boost::TIME_UTC)) + BOOST_ERROR ("boost::xtime_get != boost::TIME_UTC"); + + nsecs += xt.nsec; + msecs += nsecs / NANOSECONDS_PER_MILLISECOND; + secs += msecs / MILLISECONDS_PER_SECOND; + nsecs += (msecs % MILLISECONDS_PER_SECOND) * NANOSECONDS_PER_MILLISECOND; + xt.nsec = nsecs % NANOSECONDS_PER_SECOND; + xt.sec += secs + (nsecs / NANOSECONDS_PER_SECOND); + + return xt; +} + +inline bool in_range(const boost::xtime& xt, int secs=1) +{ + boost::xtime min = delay(-secs); + boost::xtime max = delay(0); + return (boost::xtime_cmp(xt, min) >= 0) && + (boost::xtime_cmp(xt, max) <= 0); +} + +class execution_monitor +{ +public: + enum wait_type { use_sleep_only, use_mutex, use_condition }; + + execution_monitor(wait_type type, int secs) + : done(false), type(type), secs(secs) { } + void start() + { + if (type != use_sleep_only) { + boost::mutex::scoped_lock lock(mutex); done = false; + } else { + done = false; + } + } + void finish() + { + if (type != use_sleep_only) { + boost::mutex::scoped_lock lock(mutex); + done = true; + if (type == use_condition) + cond.notify_one(); + } else { + done = true; + } + } + bool wait() + { + boost::xtime xt = delay(secs); + if (type != use_condition) + boost::thread::sleep(xt); + if (type != use_sleep_only) { + boost::mutex::scoped_lock lock(mutex); + while (type == use_condition && !done) { + if (!cond.timed_wait(lock, xt)) + break; + } + return done; + } + return done; + } + +private: + boost::mutex mutex; + boost::condition cond; + bool done; + wait_type type; + int secs; +}; + +template +class indirect_adapter +{ +public: + indirect_adapter(F func, execution_monitor& monitor) + : func(func), monitor(monitor) { } + void operator()() const + { + try + { + boost::thread thrd(func); + thrd.join(); + } + catch (...) + { + monitor.finish(); + throw; + } + monitor.finish(); + } + +private: + F func; + execution_monitor& monitor; + void operator=(indirect_adapter&); +}; + +template +void timed_test(F func, int secs, + execution_monitor::wait_type type=DEFAULT_EXECUTION_MONITOR_TYPE) +{ + execution_monitor monitor(type, secs); + indirect_adapter ifunc(func, monitor); + monitor.start(); + boost::thread thrd(ifunc); + BOOST_REQUIRE_MESSAGE(monitor.wait(), + "Timed test didn't complete in time, possible deadlock."); +} + +template +class thread_binder +{ +public: + thread_binder(const F& func, const T& param) + : func(func), param(param) { } + void operator()() const { func(param); } + +private: + F func; + T param; +}; + +template +thread_binder bind(const F& func, const T& param) +{ + return thread_binder(func, param); +} + +template +class thread_member_binder +{ +public: + thread_member_binder(R (T::*func)(), T& param) + : func(func), param(param) { } + void operator()() const { (param.*func)(); } + +private: + void operator=(thread_member_binder&); + + R (T::*func)(); + T& param; +}; + + +template +thread_member_binder bind(R (T::*func)(), T& param) +{ + return thread_member_binder(func, param); +} +} // namespace + +#endif