diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 1ec9ffc6..50168a91 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -157,6 +157,7 @@ rule usage-requirements ( properties * ) rule requirements ( properties * ) { local result ; + if pthread in $(properties) { result += BOOST_THREAD_POSIX ; @@ -201,5 +202,7 @@ lib boost_thread : thread_sources : @requirements : - : @usage-requirements + : shared:BOOST_THREAD_USE_DLL=1 + static:BOOST_THREAD_USE_LIB=1 + @usage-requirements ; diff --git a/include/boost/thread/detail/config.hpp b/include/boost/thread/detail/config.hpp index 94fb6b88..7661507f 100644 --- a/include/boost/thread/detail/config.hpp +++ b/include/boost/thread/detail/config.hpp @@ -17,8 +17,7 @@ # pragma warn -8066 // Unreachable code #endif -// insist on threading support being available: -#include +#include "platform.hpp" // compatibility with the rest of Boost's auto-linking code: #if defined(BOOST_THREAD_DYN_DLL) || defined(BOOST_ALL_DYN_LINK) @@ -31,7 +30,7 @@ #elif defined(BOOST_THREAD_USE_DLL) //Use dll #elif defined(BOOST_THREAD_USE_LIB) //Use lib #else //Use default -# if defined(BOOST_HAS_WINTHREADS) +# if defined(BOOST_THREAD_PLATFORM_WIN32) # if defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN) //For compilers supporting auto-tss cleanup //with Boost.Threads lib, use Boost.Threads lib diff --git a/include/boost/thread/detail/move.hpp b/include/boost/thread/detail/move.hpp index ba4bb655..011ae4ea 100644 --- a/include/boost/thread/detail/move.hpp +++ b/include/boost/thread/detail/move.hpp @@ -8,25 +8,23 @@ namespace boost { - template - struct move_t + namespace detail { - T& t; - move_t(T& t_): - t(t_) - {} - - T* operator->() const + template + struct thread_move_t { - return &t; - } - }; + T& t; + thread_move_t(T& t_): + t(t_) + {} - template - move_t move(T& t) - { - return move_t(t); + T* operator->() const + { + return &t; + } + }; } + } diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index b56c996e..7d9e8693 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -86,21 +86,32 @@ namespace boost { timed_lock(target_time); } - unique_lock(boost::move_t > other): + unique_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; + other->m=0; } - unique_lock(boost::move_t > other); + unique_lock(detail::thread_move_t > other); - unique_lock& operator=(boost::move_t > other) + operator detail::thread_move_t >() + { + return move(); + } + + detail::thread_move_t > move() + { + return detail::thread_move_t >(*this); + } + + unique_lock& operator=(detail::thread_move_t > other) { unique_lock temp(other); swap(temp); return *this; } - unique_lock& operator=(boost::move_t > other) + unique_lock& operator=(detail::thread_move_t > other) { unique_lock temp(other); swap(temp); @@ -112,7 +123,7 @@ namespace boost std::swap(m,other.m); std::swap(is_locked,other.is_locked); } - void swap(boost::move_t > other) + void swap(detail::thread_move_t > other) { std::swap(m,other->m); std::swap(is_locked,other->is_locked); @@ -196,6 +207,18 @@ namespace boost friend class upgrade_lock; }; + template + inline detail::thread_move_t > move(unique_lock & x) + { + return x.move(); + } + + template + inline detail::thread_move_t > move(detail::thread_move_t > x) + { + return x; + } + template class shared_lock { @@ -228,13 +251,13 @@ namespace boost timed_lock(target_time); } - shared_lock(boost::move_t > other): + shared_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; } - shared_lock(boost::move_t > other): + shared_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; @@ -244,7 +267,7 @@ namespace boost } } - shared_lock(boost::move_t > other): + shared_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; @@ -254,21 +277,32 @@ namespace boost } } - shared_lock& operator=(boost::move_t > other) + operator detail::thread_move_t >() + { + return move(); + } + + detail::thread_move_t > move() + { + return detail::thread_move_t >(*this); + } + + + shared_lock& operator=(detail::thread_move_t > other) { shared_lock temp(other); swap(temp); return *this; } - shared_lock& operator=(boost::move_t > other) + shared_lock& operator=(detail::thread_move_t > other) { shared_lock temp(other); swap(temp); return *this; } - shared_lock& operator=(boost::move_t > other) + shared_lock& operator=(detail::thread_move_t > other) { shared_lock temp(other); swap(temp); @@ -341,6 +375,19 @@ namespace boost }; + template + inline detail::thread_move_t > move(shared_lock & x) + { + return x.move(); + } + + template + inline detail::thread_move_t > move(detail::thread_move_t > x) + { + return x; + } + + template class upgrade_lock { @@ -364,13 +411,13 @@ namespace boost lock(); } } - upgrade_lock(boost::move_t > other): + upgrade_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; } - upgrade_lock(boost::move_t > other): + upgrade_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; @@ -380,14 +427,25 @@ namespace boost } } - upgrade_lock& operator=(boost::move_t > other) + operator detail::thread_move_t >() + { + return move(); + } + + detail::thread_move_t > move() + { + return detail::thread_move_t >(*this); + } + + + upgrade_lock& operator=(detail::thread_move_t > other) { upgrade_lock temp(other); swap(temp); return *this; } - upgrade_lock& operator=(boost::move_t > other) + upgrade_lock& operator=(detail::thread_move_t > other) { upgrade_lock temp(other); swap(temp); @@ -452,8 +510,21 @@ namespace boost friend class unique_lock; }; + template - unique_lock::unique_lock(boost::move_t > other): + inline detail::thread_move_t > move(upgrade_lock & x) + { + return x.move(); + } + + template + inline detail::thread_move_t > move(detail::thread_move_t > x) + { + return x; + } + + template + unique_lock::unique_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) { other->is_locked=false; @@ -474,23 +545,23 @@ namespace boost upgrade_to_unique_lock& operator=(upgrade_to_unique_lock&); public: explicit upgrade_to_unique_lock(upgrade_lock& m_): - source(&m_),exclusive(boost::move(*source)) + source(&m_),exclusive(move(*source)) {} ~upgrade_to_unique_lock() { if(source) { - *source=boost::move(exclusive); + *source=move(exclusive); } } - upgrade_to_unique_lock(boost::move_t > other): - source(other->source),exclusive(boost::move(other->exclusive)) + upgrade_to_unique_lock(detail::thread_move_t > other): + source(other->source),exclusive(move(other->exclusive)) { other->source=0; } - upgrade_to_unique_lock& operator=(boost::move_t > other) + upgrade_to_unique_lock& operator=(detail::thread_move_t > other) { upgrade_to_unique_lock temp(other); swap(temp); @@ -515,6 +586,7 @@ namespace boost return exclusive.owns_lock(); } }; + } #endif diff --git a/include/boost/thread/pthread/condition_variable.hpp b/include/boost/thread/pthread/condition_variable.hpp index 7306ae3e..52c156bc 100644 --- a/include/boost/thread/pthread/condition_variable.hpp +++ b/include/boost/thread/pthread/condition_variable.hpp @@ -5,11 +5,9 @@ // http://www.boost.org/LICENSE_1_0.txt) // (C) Copyright 2007 Anthony Williams -#include #include #include #include -#include #include #include "timespec.hpp" #include "pthread_mutex_scoped_lock.hpp" @@ -145,11 +143,23 @@ namespace boost while (!pred()) { if(!timed_wait(m, wait_until)) - return false; + return pred(); } return true; } + template + bool timed_wait(lock_type& m,xtime const& wait_until,predicate_type pred) + { + return timed_wait(m,system_time(wait_until),pred); + } + + template + bool timed_wait(lock_type& m,duration_type const& wait_duration,predicate_type pred) + { + return timed_wait(m,get_system_time()+wait_duration,pred); + } + void notify_one() { boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); diff --git a/include/boost/thread/pthread/condition_variable_fwd.hpp b/include/boost/thread/pthread/condition_variable_fwd.hpp index d9d589db..1d1fbdf4 100644 --- a/include/boost/thread/pthread/condition_variable_fwd.hpp +++ b/include/boost/thread/pthread/condition_variable_fwd.hpp @@ -6,8 +6,10 @@ // (C) Copyright 2007 Anthony Williams #include +#include #include #include +#include namespace boost { @@ -39,11 +41,23 @@ namespace boost while (!pred()) { if(!timed_wait(m, wait_until)) - return false; + return pred(); } return true; } + template + bool timed_wait(unique_lock& m,xtime const& wait_until,predicate_type pred) + { + return timed_wait(m,system_time(wait_until),pred); + } + + template + bool timed_wait(unique_lock& m,duration_type const& wait_duration,predicate_type pred) + { + return timed_wait(m,get_system_time()+wait_duration,pred); + } + void notify_one(); void notify_all(); }; diff --git a/include/boost/thread/pthread/once.hpp b/include/boost/thread/pthread/once.hpp index 90dc0c70..f342ed1d 100644 --- a/include/boost/thread/pthread/once.hpp +++ b/include/boost/thread/pthread/once.hpp @@ -14,8 +14,8 @@ #include #include #include "pthread_mutex_scoped_lock.hpp" -#include #include +#include namespace boost { @@ -27,12 +27,12 @@ namespace boost { namespace detail { BOOST_THREAD_DECL boost::uintmax_t& get_once_per_thread_epoch(); - extern BOOST_THREAD_DECL boost::uintmax_t once_global_epoch; - extern BOOST_THREAD_DECL pthread_mutex_t once_epoch_mutex; - extern BOOST_THREAD_DECL pthread_cond_t once_epoch_cv; + BOOST_THREAD_DECL extern boost::uintmax_t once_global_epoch; + BOOST_THREAD_DECL extern pthread_mutex_t once_epoch_mutex; + BOOST_THREAD_DECL extern pthread_cond_t once_epoch_cv; } -#define BOOST_ONCE_INITIAL_FLAG_VALUE -1 +#define BOOST_ONCE_INITIAL_FLAG_VALUE 0 #define BOOST_ONCE_INIT {BOOST_ONCE_INITIAL_FLAG_VALUE} @@ -42,15 +42,15 @@ namespace boost { void call_once(once_flag& flag,Function f) { static boost::uintmax_t const uninitialized_flag=BOOST_ONCE_INITIAL_FLAG_VALUE; - static boost::uintmax_t const being_initialized=uninitialized_flag-1; + static boost::uintmax_t const being_initialized=uninitialized_flag+1; boost::uintmax_t const epoch=flag.epoch; boost::uintmax_t& this_thread_epoch=detail::get_once_per_thread_epoch(); - if(epoch>this_thread_epoch) + if(epoch=being_initialized) + while(flag.epoch<=being_initialized) { if(flag.epoch==uninitialized_flag) { @@ -66,7 +66,7 @@ namespace boost { BOOST_VERIFY(!pthread_cond_broadcast(&detail::once_epoch_cv)); throw; } - flag.epoch=++detail::once_global_epoch; + flag.epoch=--detail::once_global_epoch; BOOST_VERIFY(!pthread_cond_broadcast(&detail::once_epoch_cv)); } else diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index d86e3465..24437ba6 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -40,44 +40,62 @@ namespace boost { class thread_id { - boost::optional id; - + private: + detail::thread_data_ptr thread_data; + + thread_id(detail::thread_data_ptr thread_data_): + thread_data(thread_data_) + {} friend class boost::thread; - friend thread_id this_thread::get_id(); - - thread_id(pthread_t id_): - id(id_) - {} - public: - thread_id() + thread_id(): + thread_data() {} - + bool operator==(const thread_id& y) const { - return (id && y.id) && (pthread_equal(*id,*y.id)!=0); + return thread_data==y.thread_data; } bool operator!=(const thread_id& y) const { - return !(*this==y); + return thread_data!=y.thread_data; + } + + bool operator<(const thread_id& y) const + { + return thread_data(const thread_id& y) const + { + return y.thread_data=(const thread_id& y) const + { + return !(thread_data friend std::basic_ostream& operator<<(std::basic_ostream& os, const thread_id& x) { - if(x.id) + if(x.thread_data) { - return os<<*x.id; + return os< f_): + thread_data(detail::thread_move_t f_): f(f_) {} @@ -108,13 +126,13 @@ namespace boost }; mutable boost::mutex thread_info_mutex; - boost::shared_ptr thread_info; + detail::thread_data_ptr thread_info; void start_thread(); - explicit thread(boost::shared_ptr data); + explicit thread(detail::thread_data_ptr data); - boost::shared_ptr get_thread_info() const; + detail::thread_data_ptr get_thread_info() const; public: thread(); @@ -127,16 +145,16 @@ namespace boost start_thread(); } template - thread(boost::move_t f): + thread(detail::thread_move_t f): thread_info(new thread_data(f)) { start_thread(); } - explicit thread(boost::move_t x); - thread& operator=(boost::move_t x); - operator boost::move_t(); - boost::move_t move(); + thread(detail::thread_move_t x); + thread& operator=(detail::thread_move_t x); + operator detail::thread_move_t(); + detail::thread_move_t move(); void swap(thread& x); @@ -169,6 +187,17 @@ namespace boost bool interruption_requested() const; }; + inline detail::thread_move_t move(thread& x) + { + return x.move(); + } + + inline detail::thread_move_t move(detail::thread_move_t x) + { + return x; + } + + template struct thread::thread_data >: detail::thread_data_base @@ -208,22 +237,19 @@ namespace boost ~restore_interruption(); }; - BOOST_THREAD_DECL inline thread::id get_id() - { - return thread::id(pthread_self()); - } + BOOST_THREAD_DECL thread::id get_id(); BOOST_THREAD_DECL void interruption_point(); BOOST_THREAD_DECL bool interruption_enabled(); BOOST_THREAD_DECL bool interruption_requested(); - BOOST_THREAD_DECL inline void yield() + inline void yield() { thread::yield(); } template - BOOST_THREAD_DECL inline void sleep(TimeDuration const& rel_time) + inline void sleep(TimeDuration const& rel_time) { thread::sleep(get_system_time()+rel_time); } diff --git a/include/boost/thread/pthread/thread_data.hpp b/include/boost/thread/pthread/thread_data.hpp index f11035f1..234ba882 100644 --- a/include/boost/thread/pthread/thread_data.hpp +++ b/include/boost/thread/pthread/thread_data.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -21,10 +22,14 @@ namespace boost { struct thread_exit_callback_node; struct tss_data_node; + + struct thread_data_base; + typedef boost::shared_ptr thread_data_ptr; - struct thread_data_base + struct thread_data_base: + enable_shared_from_this { - boost::shared_ptr self; + thread_data_ptr self; pthread_t thread_handle; boost::mutex data_mutex; boost::condition_variable done_condition; diff --git a/include/boost/thread/pthread/tss.hpp b/include/boost/thread/pthread/tss.hpp index c33e6d34..36c385c5 100644 --- a/include/boost/thread/pthread/tss.hpp +++ b/include/boost/thread/pthread/tss.hpp @@ -6,6 +6,7 @@ // http://www.boost.org/LICENSE_1_0.txt) // (C) Copyright 2007 Anthony Williams +#include #include namespace boost diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index c3861dd9..bfd1f1ff 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -13,6 +13,7 @@ #include #include #include "interlocked_read.hpp" +#include namespace boost { @@ -110,54 +111,65 @@ namespace boost }; + template + void start_wait_loop_first_time(relocker& locker, + detail::win32::handle_manager& local_wake_sem) + { + locker.unlock(); + if(!wake_sem) + { + wake_sem=detail::win32::create_anonymous_semaphore(0,LONG_MAX); + BOOST_ASSERT(wake_sem); + } + local_wake_sem=detail::win32::duplicate_handle(wake_sem); + + if(generations[0].notified) + { + shift_generations_down(); + } + else if(!active_generation_count) + { + active_generation_count=1; + } + } + + template + void start_wait_loop(relocker& locker, + detail::win32::handle_manager& local_wake_sem, + detail::win32::handle_manager& sem) + { + boost::mutex::scoped_lock internal_lock(internal_mutex); + detail::interlocked_write_release(&total_count,total_count+1); + if(!local_wake_sem) + { + start_wait_loop_first_time(locker,local_wake_sem); + } + if(!generations[0].semaphore) + { + generations[0].semaphore=detail::win32::create_anonymous_semaphore(0,LONG_MAX); + BOOST_ASSERT(generations[0].semaphore); + } + ++generations[0].count; + sem=detail::win32::duplicate_handle(generations[0].semaphore); + } + protected: template - bool do_wait(lock_type& lock,::boost::system_time const& wait_until) + bool do_wait(lock_type& lock,timeout wait_until) { detail::win32::handle_manager local_wake_sem; detail::win32::handle_manager sem; - bool first_loop=true; bool woken=false; relocker locker(lock); while(!woken) { + start_wait_loop(locker,local_wake_sem,sem); + + if(!this_thread::interruptible_wait(sem,wait_until)) { - boost::mutex::scoped_lock internal_lock(internal_mutex); - detail::interlocked_write_release(&total_count,total_count+1); - if(first_loop) - { - locker.unlock(); - if(!wake_sem) - { - wake_sem=detail::win32::create_anonymous_semaphore(0,LONG_MAX); - BOOST_ASSERT(wake_sem); - } - local_wake_sem=detail::win32::duplicate_handle(wake_sem); - - if(generations[0].notified) - { - shift_generations_down(); - } - else if(!active_generation_count) - { - active_generation_count=1; - } - - first_loop=false; - } - if(!generations[0].semaphore) - { - generations[0].semaphore=detail::win32::create_anonymous_semaphore(0,LONG_MAX); - BOOST_ASSERT(generations[0].semaphore); - } - ++generations[0].count; - sem=detail::win32::duplicate_handle(generations[0].semaphore); - } - if(!this_thread::interruptible_wait(sem,::boost::detail::get_milliseconds_until(wait_until))) - { - break; + return false; } unsigned long const woken_result=detail::win32::WaitForSingleObject(local_wake_sem,0); @@ -167,6 +179,17 @@ namespace boost } return woken; } + + template + bool do_wait(lock_type& m,timeout const& wait_until,predicate_type pred) + { + while (!pred()) + { + if(!do_wait(m, wait_until)) + return pred(); + } + return true; + } basic_condition_variable(const basic_condition_variable& other); basic_condition_variable& operator=(const basic_condition_variable& other); @@ -238,7 +261,7 @@ namespace boost public: void wait(unique_lock& m) { - do_wait(m,::boost::detail::get_system_time_sentinel()); + do_wait(m,detail::timeout::sentinel()); } template @@ -253,15 +276,30 @@ namespace boost return do_wait(m,wait_until); } + bool timed_wait(unique_lock& m,boost::xtime const& wait_until) + { + return do_wait(m,system_time(wait_until)); + } + template + bool timed_wait(unique_lock& m,duration_type const& wait_duration) + { + return do_wait(m,wait_duration.total_milliseconds()); + } + template bool timed_wait(unique_lock& m,boost::system_time const& wait_until,predicate_type pred) { - while (!pred()) - { - if(!timed_wait(m, wait_until)) - return false; - } - return true; + return do_wait(m,wait_until,pred); + } + template + bool timed_wait(unique_lock& m,boost::xtime const& wait_until,predicate_type pred) + { + return do_wait(m,system_time(wait_until),pred); + } + template + bool timed_wait(unique_lock& m,duration_type const& wait_duration,predicate_type pred) + { + return do_wait(m,wait_duration.total_milliseconds(),pred); } }; @@ -272,7 +310,7 @@ namespace boost template void wait(lock_type& m) { - do_wait(m,::boost::detail::get_system_time_sentinel()); + do_wait(m,detail::timeout::sentinel()); } template @@ -287,15 +325,34 @@ namespace boost return do_wait(m,wait_until); } + template + bool timed_wait(lock_type& m,boost::xtime const& wait_until) + { + return do_wait(m,system_time(wait_until)); + } + + template + bool timed_wait(lock_type& m,duration_type const& wait_duration) + { + return do_wait(m,wait_duration.total_milliseconds()); + } + template bool timed_wait(lock_type& m,boost::system_time const& wait_until,predicate_type pred) { - while (!pred()) - { - if(!timed_wait(m, wait_until)) - return false; - } - return true; + return do_wait(m,wait_until,pred); + } + + template + bool timed_wait(lock_type& m,boost::xtime const& wait_until,predicate_type pred) + { + return do_wait(m,system_time(wait_until),pred); + } + + template + bool timed_wait(lock_type& m,duration_type const& wait_duration,predicate_type pred) + { + return do_wait(m,wait_duration.total_milliseconds(),pred); } }; diff --git a/include/boost/thread/win32/shared_mutex.hpp b/include/boost/thread/win32/shared_mutex.hpp index 992491d5..73cdc490 100644 --- a/include/boost/thread/win32/shared_mutex.hpp +++ b/include/boost/thread/win32/shared_mutex.hpp @@ -48,10 +48,10 @@ namespace boost } state_data state; - void* semaphores[2]; - void* &unlock_sem; - void* &exclusive_sem; - void* upgrade_sem; + detail::win32::handle semaphores[2]; + detail::win32::handle &unlock_sem; + detail::win32::handle &exclusive_sem; + detail::win32::handle upgrade_sem; void release_waiters(state_data old_state) { diff --git a/include/boost/thread/win32/thread.hpp b/include/boost/thread/win32/thread.hpp index 16315b00..9ff339ec 100644 --- a/include/boost/thread/win32/thread.hpp +++ b/include/boost/thread/win32/thread.hpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace boost { @@ -73,6 +74,82 @@ namespace boost }; typedef boost::intrusive_ptr thread_data_ptr; + + struct timeout + { + unsigned long start; + uintmax_t milliseconds; + bool relative; + boost::system_time abs_time; + + static unsigned long const max_non_infinite_wait=0xfffffffe; + + timeout(uintmax_t milliseconds_): + start(win32::GetTickCount()), + milliseconds(milliseconds_), + relative(true), + abs_time(boost::get_system_time()) + {} + + timeout(boost::system_time const& abs_time_): + start(win32::GetTickCount()), + milliseconds(0), + relative(false), + abs_time(abs_time_) + {} + + struct remaining_time + { + bool more; + unsigned long milliseconds; + + remaining_time(uintmax_t remaining): + more(remaining>max_non_infinite_wait), + milliseconds(more?max_non_infinite_wait:(unsigned long)remaining) + {} + }; + + remaining_time remaining_milliseconds() const + { + if(is_sentinel()) + { + return remaining_time(win32::infinite); + } + else if(relative) + { + unsigned long const now=win32::GetTickCount(); + unsigned long const elapsed=now-start; + return remaining_time((elapsed f_): + thread_data(detail::thread_move_t f_): f(f_) {} @@ -123,16 +200,16 @@ namespace boost start_thread(); } template - explicit thread(boost::move_t f): + thread(detail::thread_move_t f): thread_info(detail::heap_new >(f)) { start_thread(); } - thread(boost::move_t x); - thread& operator=(boost::move_t x); - operator boost::move_t(); - boost::move_t move(); + thread(detail::thread_move_t x); + thread& operator=(detail::thread_move_t x); + operator detail::thread_move_t(); + detail::thread_move_t move(); void swap(thread& x); @@ -168,6 +245,16 @@ namespace boost bool interruption_requested() const; }; + inline detail::thread_move_t move(thread& x) + { + return x.move(); + } + + inline detail::thread_move_t move(detail::thread_move_t x) + { + return x; + } + template struct thread::thread_data >: detail::thread_data_base @@ -210,7 +297,7 @@ namespace boost thread::id BOOST_THREAD_DECL get_id(); - bool BOOST_THREAD_DECL interruptible_wait(detail::win32::handle handle_to_wait_for,unsigned long milliseconds); + bool BOOST_THREAD_DECL interruptible_wait(detail::win32::handle handle_to_wait_for,detail::timeout target_time); inline bool interruptible_wait(unsigned long milliseconds) { return interruptible_wait(detail::win32::invalid_handle_value,milliseconds); @@ -277,7 +364,14 @@ namespace boost friend std::basic_ostream& operator<<(std::basic_ostream& os, const id& x) { - return os< #include #include @@ -12,9 +13,9 @@ namespace boost { namespace detail { - boost::uintmax_t once_global_epoch=0; - pthread_mutex_t once_epoch_mutex=PTHREAD_MUTEX_INITIALIZER; - pthread_cond_t once_epoch_cv = PTHREAD_COND_INITIALIZER; + BOOST_THREAD_DECL boost::uintmax_t once_global_epoch=UINTMAX_C(~0); + BOOST_THREAD_DECL pthread_mutex_t once_epoch_mutex=PTHREAD_MUTEX_INITIALIZER; + BOOST_THREAD_DECL pthread_cond_t once_epoch_cv = PTHREAD_COND_INITIALIZER; namespace { @@ -41,11 +42,10 @@ namespace boost { data=malloc(sizeof(boost::uintmax_t)); BOOST_VERIFY(!pthread_setspecific(epoch_tss_key,data)); - *static_cast(data)=0; + *static_cast(data)=UINTMAX_C(~0); } return *static_cast(data); } - } } diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 8a074db4..0add81c6 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -13,6 +13,14 @@ #include #include #include +#ifdef __linux__ +#include +#elif defined(__APPLE__) || defined(__FreeBSD__) +#include +#include +#elif defined(__sun) +#include +#endif #include "timeconv.inl" @@ -188,6 +196,37 @@ namespace boost detach(); } + thread::thread(detail::thread_move_t x) + { + lock_guard lock(x->thread_info_mutex); + thread_info=x->thread_info; + x->thread_info.reset(); + } + + thread& thread::operator=(detail::thread_move_t x) + { + thread new_thread(x); + swap(new_thread); + return *this; + } + + thread::operator detail::thread_move_t() + { + return move(); + } + + detail::thread_move_t thread::move() + { + detail::thread_move_t x(*this); + return x; + } + + void thread::swap(thread& x) + { + thread_info.swap(x.thread_info); + } + + bool thread::operator==(const thread& other) const { return get_id()==other.get_id(); @@ -198,7 +237,7 @@ namespace boost return !operator==(other); } - boost::shared_ptr thread::get_thread_info() const + detail::thread_data_ptr thread::get_thread_info() const { lock_guard l(thread_info_mutex); return thread_info; @@ -206,7 +245,7 @@ namespace boost void thread::join() { - boost::shared_ptr const local_thread_info=get_thread_info(); + detail::thread_data_ptr const local_thread_info=get_thread_info(); if(local_thread_info) { bool do_join=false; @@ -250,7 +289,7 @@ namespace boost bool thread::timed_join(system_time const& wait_until) { - boost::shared_ptr const local_thread_info=get_thread_info(); + detail::thread_data_ptr const local_thread_info=get_thread_info(); if(local_thread_info) { bool do_join=false; @@ -304,7 +343,7 @@ namespace boost void thread::detach() { - boost::shared_ptr local_thread_info; + detail::thread_data_ptr local_thread_info; { lock_guard l1(thread_info_mutex); thread_info.swap(local_thread_info); @@ -377,15 +416,28 @@ namespace boost unsigned thread::hardware_concurrency() { - return 1; +#if defined(PTW32_VERSION) || defined(__hpux) + return pthread_num_processors_np(); +#elif defined(__linux__) + return get_nprocs(); +#elif defined(__APPLE__) || defined(__FreeBSD__) + int count; + size_t size=sizeof(count); + return sysctlbyname("hw.ncpu",&count,&size,NULL,0)?0:count; +#elif defined(__sun) + int const count=sysconf(_SC_NPROCESSORS_ONLN); + return (count>0)?count:0; +#else + return 0; +#endif } thread::id thread::get_id() const { - boost::shared_ptr local_thread_info=get_thread_info(); + detail::thread_data_ptr const local_thread_info=get_thread_info(); if(local_thread_info) { - return id(local_thread_info->thread_handle); + return id(local_thread_info); } else { @@ -395,7 +447,7 @@ namespace boost void thread::interrupt() { - boost::shared_ptr local_thread_info=get_thread_info(); + detail::thread_data_ptr const local_thread_info=get_thread_info(); if(local_thread_info) { lock_guard lk(local_thread_info->data_mutex); @@ -409,7 +461,7 @@ namespace boost bool thread::interruption_requested() const { - boost::shared_ptr local_thread_info=get_thread_info(); + detail::thread_data_ptr const local_thread_info=get_thread_info(); if(local_thread_info) { lock_guard lk(local_thread_info->data_mutex); @@ -424,6 +476,12 @@ namespace boost namespace this_thread { + thread::id get_id() + { + boost::detail::thread_data_base* const thread_info=get_or_make_current_thread_data(); + return thread::id(thread_info?thread_info->shared_from_this():detail::thread_data_ptr()); + } + void interruption_point() { boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data(); diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index fa83b7cf..d64c78dd 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -4,6 +4,9 @@ // (C) Copyright 2007 Anthony Williams // (C) Copyright 2007 David Deakins +#define _WIN32_WINNT 0x400 +#define WINVER 0x400 + #include #include #include @@ -15,6 +18,7 @@ #include #include #include +#include namespace boost { @@ -63,11 +67,11 @@ namespace boost typedef void* uintptr_t; inline uintptr_t const _beginthreadex(void* security, unsigned stack_size, unsigned (__stdcall* start_address)(void*), - void* arglist, unsigned initflag, unsigned* thrdaddr) + void* arglist, unsigned initflag, unsigned* thrdaddr) { DWORD threadID; HANDLE hthread=CreateThread(static_cast(security),stack_size,ThreadProxy, - new ThreadProxyData(start_address,arglist),initflag,&threadID); + new ThreadProxyData(start_address,arglist),initflag,&threadID); if (hthread!=0) *thrdaddr=threadID; return reinterpret_cast(hthread); @@ -240,30 +244,28 @@ namespace boost detach(); } - thread::thread(boost::move_t x) + thread::thread(detail::thread_move_t x) { - { - boost::mutex::scoped_lock l(x->thread_info_mutex); - thread_info=x->thread_info; - } - x->release_handle(); + lock_guard lock(x->thread_info_mutex); + thread_info=x->thread_info; + x->thread_info=0; } - thread& thread::operator=(boost::move_t x) + thread& thread::operator=(detail::thread_move_t x) { thread new_thread(x); swap(new_thread); return *this; } - thread::operator boost::move_t() + thread::operator detail::thread_move_t() { return move(); } - boost::move_t thread::move() + detail::thread_move_t thread::move() { - boost::move_t x(*this); + detail::thread_move_t x(*this); return x; } @@ -287,7 +289,7 @@ namespace boost detail::thread_data_ptr local_thread_info=get_thread_info(); if(local_thread_info) { - this_thread::interruptible_wait(local_thread_info->thread_handle,detail::win32::infinite); + this_thread::interruptible_wait(local_thread_info->thread_handle,detail::timeout::sentinel()); release_handle(); } } @@ -313,7 +315,7 @@ namespace boost void thread::release_handle() { - boost::mutex::scoped_lock l1(thread_info_mutex); + lock_guard l1(thread_info_mutex); thread_info=0; } @@ -353,13 +355,57 @@ namespace boost namespace this_thread { - bool interruptible_wait(detail::win32::handle handle_to_wait_for,unsigned long milliseconds) + namespace { - detail::win32::handle handles[2]={0}; + LARGE_INTEGER get_due_time(detail::timeout const& target_time) + { + LARGE_INTEGER due_time={0}; + if(target_time.relative) + { + unsigned long const elapsed_milliseconds=GetTickCount()-target_time.start; + LONGLONG const remaining_milliseconds=(target_time.milliseconds-elapsed_milliseconds); + LONGLONG const hundred_nanoseconds_in_one_millisecond=10000; + + if(remaining_milliseconds>0) + { + due_time.QuadPart=-(remaining_milliseconds*hundred_nanoseconds_in_one_millisecond); + } + } + else + { + SYSTEMTIME target_system_time={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(); + target_system_time.wHour=(WORD)target_time.abs_time.time_of_day().hours(); + target_system_time.wMinute=(WORD)target_time.abs_time.time_of_day().minutes(); + target_system_time.wSecond=(WORD)target_time.abs_time.time_of_day().seconds(); + + if(!SystemTimeToFileTime(&target_system_time,((FILETIME*)&due_time))) + { + due_time.QuadPart=0; + } + else + { + long const hundred_nanoseconds_in_one_second=10000000; + due_time.QuadPart+=target_time.abs_time.time_of_day().fractional_seconds()*(hundred_nanoseconds_in_one_second/target_time.abs_time.time_of_day().ticks_per_second()); + } + } + return due_time; + } + } + + + bool interruptible_wait(detail::win32::handle handle_to_wait_for,detail::timeout target_time) + { + detail::win32::handle handles[3]={0}; unsigned handle_count=0; + unsigned wait_handle_index=~0U; unsigned interruption_index=~0U; + unsigned timeout_index=~0U; if(handle_to_wait_for!=detail::win32::invalid_handle_value) { + wait_handle_index=handle_count; handles[handle_count++]=handle_to_wait_for; } if(get_current_thread_data() && get_current_thread_data()->interruption_enabled) @@ -367,24 +413,79 @@ namespace boost interruption_index=handle_count; handles[handle_count++]=get_current_thread_data()->interruption_handle; } + + detail::win32::handle_manager timer_handle; + +#ifndef UNDER_CE + unsigned const min_timer_wait_period=20; + + if(!target_time.is_sentinel()) + { + detail::timeout::remaining_time const time_left=target_time.remaining_milliseconds(); + if(time_left.milliseconds > min_timer_wait_period) + { + // for a long-enough timeout, use a waitable timer (which tracks clock changes) + timer_handle=CreateWaitableTimer(NULL,false,NULL); + if(timer_handle!=0) + { + LARGE_INTEGER due_time=get_due_time(target_time); + + bool const set_time_succeeded=SetWaitableTimer(timer_handle,&due_time,0,0,0,false)!=0; + if(set_time_succeeded) + { + timeout_index=handle_count; + handles[handle_count++]=timer_handle; + } + } + } + else if(!target_time.relative) + { + // convert short absolute-time timeouts into relative ones, so we don't race against clock changes + target_time=detail::timeout(time_left.milliseconds); + } + } +#endif - if(handle_count) + bool const using_timer=timeout_index!=~0u; + detail::timeout::remaining_time time_left(0); + + do { - unsigned long const notified_index=detail::win32::WaitForMultipleObjects(handle_count,handles,false,milliseconds); - if((handle_to_wait_for!=detail::win32::invalid_handle_value) && !notified_index) + if(!using_timer) { - return true; + time_left=target_time.remaining_milliseconds(); } - else if(notified_index==interruption_index) + + if(handle_count) { - detail::win32::ResetEvent(get_current_thread_data()->interruption_handle); - throw thread_interrupted(); + unsigned long const notified_index=detail::win32::WaitForMultipleObjects(handle_count,handles,false,using_timer?INFINITE:time_left.milliseconds); + if(notified_indexinterruption_handle); + throw thread_interrupted(); + } + else if(notified_index==timeout_index) + { + return false; + } + } + } + else + { + detail::win32::Sleep(time_left.milliseconds); + } + if(target_time.relative) + { + target_time.milliseconds-=detail::timeout::max_non_infinite_wait; } } - else - { - detail::win32::Sleep(milliseconds); - } + while(time_left.more); return false; } @@ -494,7 +595,7 @@ namespace boost 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) + if(cleanup_existing && current_node->func.get()) { (*current_node->func)(current_node->value); } diff --git a/src/win32/tss_pe.cpp b/src/win32/tss_pe.cpp index 4528b90f..dec71c3b 100644 --- a/src/win32/tss_pe.cpp +++ b/src/win32/tss_pe.cpp @@ -230,13 +230,10 @@ extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata void NTAPI on_tls_callback(HINSTANCE h, DWORD dwReason, PVOID pv) { - OutputDebugString("on_tls_callback\n"); - switch (dwReason) { case DLL_THREAD_DETACH: { - OutputDebugString("on_tls_callback: thread_exit\n"); on_thread_exit(); break; } diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 209e6b07..d9e4bcb9 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1,4 +1,5 @@ # (C) Copyright William E. Kempf 2001. +# (C) Copyright 2007 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) # @@ -34,13 +35,21 @@ rule thread-run ( sources ) { test-suite "threads" : [ thread-run test_thread.cpp ] + [ thread-run test_thread_id.cpp ] + [ thread-run test_hardware_concurrency.cpp ] + [ thread-run test_thread_move.cpp ] + [ thread-run test_move_function.cpp ] [ thread-run test_mutex.cpp ] + [ thread-run test_condition_notify_one.cpp ] + [ thread-run test_condition_timed_wait_times_out.cpp ] + [ thread-run test_condition_notify_all.cpp ] [ thread-run test_condition.cpp ] [ thread-run test_tss.cpp ] [ thread-run test_once.cpp ] [ thread-run test_xtime.cpp ] [ thread-run test_barrier.cpp ] [ thread-run test_shared_mutex.cpp ] + [ thread-run test_shared_mutex_part_2.cpp ] [ thread-run test_lock_concept.cpp ] ; } diff --git a/test/condition_test_common.hpp b/test/condition_test_common.hpp new file mode 100644 index 00000000..df7d5edc --- /dev/null +++ b/test/condition_test_common.hpp @@ -0,0 +1,95 @@ +#ifndef CONDITION_TEST_COMMON_HPP +#define CONDITION_TEST_COMMON_HPP +// Copyright (C) 2007 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) + +#include +#include +#include + +unsigned const timeout_seconds=5; + +struct wait_for_flag +{ + boost::mutex mutex; + boost::condition_variable cond_var; + bool flag; + unsigned woken; + + wait_for_flag(): + flag(false),woken(0) + {} + + struct check_flag + { + bool const& flag; + + check_flag(bool const& flag_): + flag(flag_) + {} + + bool operator()() const + { + return flag; + } + }; + + + void wait_without_predicate() + { + boost::mutex::scoped_lock lock(mutex); + while(!flag) + { + cond_var.wait(lock); + } + ++woken; + } + + void wait_with_predicate() + { + boost::mutex::scoped_lock lock(mutex); + cond_var.wait(lock,check_flag(flag)); + if(flag) + { + ++woken; + } + } + + void timed_wait_without_predicate() + { + boost::system_time const timeout=boost::get_system_time()+boost::posix_time::seconds(timeout_seconds); + + boost::mutex::scoped_lock lock(mutex); + while(!flag) + { + if(!cond_var.timed_wait(lock,timeout)) + { + return; + } + } + ++woken; + } + + void timed_wait_with_predicate() + { + boost::system_time const timeout=boost::get_system_time()+boost::posix_time::seconds(timeout_seconds); + boost::mutex::scoped_lock lock(mutex); + if(cond_var.timed_wait(lock,timeout,check_flag(flag)) && flag) + { + ++woken; + } + } + void relative_timed_wait_with_predicate() + { + boost::mutex::scoped_lock lock(mutex); + if(cond_var.timed_wait(lock,boost::posix_time::seconds(timeout_seconds),check_flag(flag)) && flag) + { + ++woken; + } + } +}; + + +#endif diff --git a/test/shared_mutex_locking_thread.hpp b/test/shared_mutex_locking_thread.hpp new file mode 100644 index 00000000..1fccbddf --- /dev/null +++ b/test/shared_mutex_locking_thread.hpp @@ -0,0 +1,62 @@ +#ifndef SHARED_MUTEX_LOCKING_THREAD_HPP +#define SHARED_MUTEX_LOCKING_THREAD_HPP + +#include +#include +#include + +template +class locking_thread +{ + boost::shared_mutex& rw_mutex; + unsigned& unblocked_count; + boost::condition_variable& unblocked_condition; + unsigned& simultaneous_running_count; + unsigned& max_simultaneous_running; + boost::mutex& unblocked_count_mutex; + boost::mutex& finish_mutex; +public: + locking_thread(boost::shared_mutex& rw_mutex_, + unsigned& unblocked_count_, + boost::mutex& unblocked_count_mutex_, + boost::condition_variable& unblocked_condition_, + boost::mutex& finish_mutex_, + unsigned& simultaneous_running_count_, + unsigned& max_simultaneous_running_): + rw_mutex(rw_mutex_), + unblocked_count(unblocked_count_), + unblocked_condition(unblocked_condition_), + simultaneous_running_count(simultaneous_running_count_), + max_simultaneous_running(max_simultaneous_running_), + unblocked_count_mutex(unblocked_count_mutex_), + finish_mutex(finish_mutex_) + {} + + void operator()() + { + // acquire lock + lock_type lock(rw_mutex); + + // increment count to show we're unblocked + { + boost::mutex::scoped_lock ublock(unblocked_count_mutex); + ++unblocked_count; + unblocked_condition.notify_one(); + ++simultaneous_running_count; + if(simultaneous_running_count>max_simultaneous_running) + { + max_simultaneous_running=simultaneous_running_count; + } + } + + // wait to finish + boost::mutex::scoped_lock finish_lock(finish_mutex); + { + boost::mutex::scoped_lock ublock(unblocked_count_mutex); + --simultaneous_running_count; + } + } +}; + + +#endif diff --git a/test/test_barrier.cpp b/test/test_barrier.cpp index bccd346f..e4879704 100644 --- a/test/test_barrier.cpp +++ b/test/test_barrier.cpp @@ -52,7 +52,7 @@ void test_barrier() throw; } - BOOST_CHECK(global_parameter == 5); + BOOST_CHECK_EQUAL(global_parameter,5); } boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) diff --git a/test/test_condition.cpp b/test/test_condition.cpp index 2dbe32d0..96b1e86e 100644 --- a/test/test_condition.cpp +++ b/test/test_condition.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf +// Copyright (C) 2007 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) @@ -19,7 +20,7 @@ struct condition_test_data condition_test_data() : notified(0), awoken(0) { } boost::mutex mutex; - boost::condition condition; + boost::condition_variable condition; int notified; int awoken; }; @@ -82,66 +83,15 @@ void condition_test_waits(condition_test_data* data) BOOST_CHECK_EQUAL(data->notified, 4); data->awoken++; data->condition.notify_one(); -} -void do_test_condition_notify_one() -{ - condition_test_data data; - - boost::thread thread(bind(&condition_test_thread, &data)); - - { - boost::mutex::scoped_lock lock(data.mutex); - BOOST_CHECK(lock ? true : false); - data.notified++; - data.condition.notify_one(); - } - - thread.join(); - BOOST_CHECK_EQUAL(data.awoken, 1); -} - -void test_condition_notify_one() -{ - timed_test(&do_test_condition_notify_one, 100, execution_monitor::use_mutex); -} - -void do_test_condition_notify_all() -{ - const int NUMTHREADS = 5; - boost::thread_group threads; - condition_test_data data; - - try - { - for (int i = 0; i < NUMTHREADS; ++i) - threads.create_thread(bind(&condition_test_thread, &data)); - - { - boost::mutex::scoped_lock lock(data.mutex); - BOOST_CHECK(lock ? true : false); - data.notified++; - data.condition.notify_all(); - } - - threads.join_all(); - } - catch(...) - { - threads.interrupt_all(); - threads.join_all(); - throw; - } - - BOOST_CHECK_EQUAL(data.awoken, NUMTHREADS); -} - -void test_condition_notify_all() -{ - // We should have already tested notify_one here, so - // a timed test with the default execution_monitor::use_condition - // should be OK, and gives the fastest performance - timed_test(&do_test_condition_notify_all, 100); + // Test predicate timed_wait with relative timeout + cond_predicate pred_rel(data->notified, 5); + BOOST_CHECK(data->condition.timed_wait(lock, boost::posix_time::seconds(10), pred_rel)); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK(pred_rel()); + BOOST_CHECK_EQUAL(data->notified, 5); + data->awoken++; + data->condition.notify_one(); } void do_test_condition_waits() @@ -185,10 +135,19 @@ void do_test_condition_waits() data.condition.wait(lock); BOOST_CHECK(lock ? true : false); BOOST_CHECK_EQUAL(data.awoken, 4); + + + boost::thread::sleep(delay(1)); + data.notified++; + data.condition.notify_one(); + while (data.awoken != 5) + data.condition.wait(lock); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data.awoken, 5); } thread.join(); - BOOST_CHECK_EQUAL(data.awoken, 4); + BOOST_CHECK_EQUAL(data.awoken, 5); } void test_condition_waits() @@ -216,14 +175,11 @@ void test_condition_wait_is_a_interruption_point() timed_test(&do_test_condition_wait_is_a_interruption_point, 1); } - boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = BOOST_TEST_SUITE("Boost.Threads: condition test suite"); - test->add(BOOST_TEST_CASE(&test_condition_notify_one)); - test->add(BOOST_TEST_CASE(&test_condition_notify_all)); test->add(BOOST_TEST_CASE(&test_condition_waits)); test->add(BOOST_TEST_CASE(&test_condition_wait_is_a_interruption_point)); diff --git a/test/test_condition_notify_all.cpp b/test/test_condition_notify_all.cpp new file mode 100644 index 00000000..22074b44 --- /dev/null +++ b/test/test_condition_notify_all.cpp @@ -0,0 +1,180 @@ +// Copyright (C) 2007 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) + +#include + +#include + +#include + +#include +#include "condition_test_common.hpp" + +unsigned const number_of_test_threads=5; + +void do_test_condition_notify_all_wakes_from_wait() +{ + wait_for_flag data; + + boost::thread_group group; + + try + { + for(unsigned i=0;iadd(BOOST_TEST_CASE(&test_condition_notify_all)); + + return test; +} diff --git a/test/test_condition_notify_one.cpp b/test/test_condition_notify_one.cpp new file mode 100644 index 00000000..45447bd4 --- /dev/null +++ b/test/test_condition_notify_one.cpp @@ -0,0 +1,113 @@ +// Copyright (C) 2007 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) + +#include + +#include + +#include + +#include +#include "condition_test_common.hpp" + +void do_test_condition_notify_one_wakes_from_wait() +{ + wait_for_flag data; + + boost::thread thread(bind(&wait_for_flag::wait_without_predicate, data)); + + { + boost::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_one(); + } + + thread.join(); + BOOST_CHECK(data.woken); +} + +void do_test_condition_notify_one_wakes_from_wait_with_predicate() +{ + wait_for_flag data; + + boost::thread thread(bind(&wait_for_flag::wait_with_predicate, data)); + + { + boost::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_one(); + } + + thread.join(); + BOOST_CHECK(data.woken); +} + +void do_test_condition_notify_one_wakes_from_timed_wait() +{ + wait_for_flag data; + + boost::thread thread(bind(&wait_for_flag::timed_wait_without_predicate, data)); + + { + boost::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_one(); + } + + thread.join(); + BOOST_CHECK(data.woken); +} + +void do_test_condition_notify_one_wakes_from_timed_wait_with_predicate() +{ + wait_for_flag data; + + boost::thread thread(bind(&wait_for_flag::timed_wait_with_predicate, data)); + + { + boost::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_one(); + } + + thread.join(); + BOOST_CHECK(data.woken); +} + +void do_test_condition_notify_one_wakes_from_relative_timed_wait_with_predicate() +{ + wait_for_flag data; + + boost::thread thread(bind(&wait_for_flag::relative_timed_wait_with_predicate, data)); + + { + boost::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_one(); + } + + thread.join(); + BOOST_CHECK(data.woken); +} + +void test_condition_notify_one() +{ + timed_test(&do_test_condition_notify_one_wakes_from_wait, timeout_seconds, execution_monitor::use_mutex); + timed_test(&do_test_condition_notify_one_wakes_from_wait_with_predicate, timeout_seconds, execution_monitor::use_mutex); + timed_test(&do_test_condition_notify_one_wakes_from_timed_wait, timeout_seconds, execution_monitor::use_mutex); + timed_test(&do_test_condition_notify_one_wakes_from_timed_wait_with_predicate, timeout_seconds, execution_monitor::use_mutex); + timed_test(&do_test_condition_notify_one_wakes_from_relative_timed_wait_with_predicate, timeout_seconds, execution_monitor::use_mutex); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: condition test suite"); + + test->add(BOOST_TEST_CASE(&test_condition_notify_one)); + + return test; +} diff --git a/test/test_condition_timed_wait_times_out.cpp b/test/test_condition_timed_wait_times_out.cpp new file mode 100644 index 00000000..128922a4 --- /dev/null +++ b/test/test_condition_timed_wait_times_out.cpp @@ -0,0 +1,89 @@ +// Copyright (C) 2007 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) + +#include + +#include +#include + +#include +#include "util.inl" + +bool fake_predicate() +{ + return false; +} + +unsigned const timeout_seconds=5; +unsigned const timeout_grace=1; +boost::posix_time::milliseconds const timeout_resolution(100); + + +void do_test_timed_wait_times_out() +{ + boost::condition_variable cond; + boost::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + boost::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+delay; + + while(cond.timed_wait(lock,timeout)); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_timed_wait_with_predicate_times_out() +{ + boost::condition_variable cond; + boost::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + boost::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+delay; + + bool const res=cond.timed_wait(lock,timeout,fake_predicate); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK(!res); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_relative_timed_wait_with_predicate_times_out() +{ + boost::condition_variable cond; + boost::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + boost::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + + bool const res=cond.timed_wait(lock,delay,fake_predicate); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK(!res); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + + +void test_timed_wait_times_out() +{ + timed_test(&do_test_timed_wait_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_timed_wait_with_predicate_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_relative_timed_wait_with_predicate_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); +} + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: condition test suite"); + + test->add(BOOST_TEST_CASE(&test_timed_wait_times_out)); + + return test; +} diff --git a/test/test_hardware_concurrency.cpp b/test/test_hardware_concurrency.cpp new file mode 100644 index 00000000..7c1c148b --- /dev/null +++ b/test/test_hardware_concurrency.cpp @@ -0,0 +1,21 @@ +// Copyright (C) 2007 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) +#include +#include +#include + +void test_hardware_concurrency_is_non_zero() +{ + BOOST_CHECK(boost::thread::hardware_concurrency()!=0); +} + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: hardware concurrency test suite"); + + test->add(BOOST_TEST_CASE(test_hardware_concurrency_is_non_zero)); + return test; +} diff --git a/test/test_move_function.cpp b/test/test_move_function.cpp new file mode 100644 index 00000000..153a4cf1 --- /dev/null +++ b/test/test_move_function.cpp @@ -0,0 +1,54 @@ +// Copyright (C) 2007 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) +#include +#include +#include + +void do_nothing() +{} + +void test_thread_move_from_lvalue_on_construction() +{ + boost::thread src(do_nothing); + boost::thread::id src_id=src.get_id(); + boost::thread dest(boost::move(src)); + boost::thread::id dest_id=dest.get_id(); + BOOST_CHECK(src_id==dest_id); + BOOST_CHECK(src.get_id()==boost::thread::id()); + dest.join(); +} + +void test_thread_move_from_rvalue_on_construction() +{ + boost::thread x(boost::move(boost::thread(do_nothing))); + BOOST_CHECK(x.get_id()!=boost::thread::id()); + x.join(); +} + + +void test_unique_lock_move_from_lvalue_on_construction() +{ + boost::mutex m; + boost::unique_lock l(m); + BOOST_CHECK(l.owns_lock()); + BOOST_CHECK(l.mutex()==&m); + + boost::unique_lock l2(boost::move(l)); + BOOST_CHECK(!l.owns_lock()); + BOOST_CHECK(!l.mutex()); + BOOST_CHECK(l2.owns_lock()); + BOOST_CHECK(l2.mutex()==&m); +} + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: thread move test suite"); + + test->add(BOOST_TEST_CASE(test_thread_move_from_lvalue_on_construction)); + test->add(BOOST_TEST_CASE(test_thread_move_from_rvalue_on_construction)); + test->add(BOOST_TEST_CASE(test_unique_lock_move_from_lvalue_on_construction)); + return test; +} diff --git a/test/test_mutex.cpp b/test/test_mutex.cpp index 08170fd2..5dae38ee 100644 --- a/test/test_mutex.cpp +++ b/test/test_mutex.cpp @@ -102,6 +102,11 @@ struct test_timedlock typedef M mutex_type; typedef typename M::scoped_timed_lock timed_lock_type; + static bool fake_predicate() + { + return false; + } + void operator()() { mutex_type mutex; @@ -123,14 +128,17 @@ struct test_timedlock BOOST_CHECK(lock ? true : false); // Construct and initialize an xtime for a fast time out. - boost::xtime xt = delay(0, 100); + boost::system_time timeout = boost::get_system_time()+boost::posix_time::milliseconds(100); // Test the lock and the mutex with condition variables. // No one is going to notify this condition variable. We expect to // time out. - BOOST_CHECK(!condition.timed_wait(lock, xt)); + BOOST_CHECK(!condition.timed_wait(lock, timeout, fake_predicate)); BOOST_CHECK(lock ? true : false); - BOOST_CHECK(in_range(xt)); + + boost::system_time now=boost::get_system_time(); + boost::posix_time::milliseconds const timeout_resolution(20); + BOOST_CHECK((timeout-timeout_resolution) #include -#include -#include -#include #include #include "util.inl" -#include -#include +#include "shared_mutex_locking_thread.hpp" #define CHECK_LOCKED_VALUE_EQUAL(mutex_name,value,expected_value) \ { \ @@ -19,65 +15,6 @@ BOOST_CHECK_EQUAL(value,expected_value); \ } - -namespace -{ - template - class locking_thread - { - boost::shared_mutex& rw_mutex; - unsigned& unblocked_count; - unsigned& simultaneous_running_count; - unsigned& max_simultaneous_running; - boost::mutex& unblocked_count_mutex; - boost::condition_variable& unblocked_condition; - boost::mutex& finish_mutex; - public: - locking_thread(boost::shared_mutex& rw_mutex_, - unsigned& unblocked_count_, - boost::mutex& unblocked_count_mutex_, - boost::condition_variable& unblocked_condition_, - boost::mutex& finish_mutex_, - unsigned& simultaneous_running_count_, - unsigned& max_simultaneous_running_): - rw_mutex(rw_mutex_), - unblocked_count(unblocked_count_), - unblocked_condition(unblocked_condition_), - simultaneous_running_count(simultaneous_running_count_), - max_simultaneous_running(max_simultaneous_running_), - unblocked_count_mutex(unblocked_count_mutex_), - finish_mutex(finish_mutex_) - {} - - void operator()() - { - // acquire lock - lock_type lock(rw_mutex); - - // increment count to show we're unblocked - { - boost::mutex::scoped_lock ublock(unblocked_count_mutex); - ++unblocked_count; - unblocked_condition.notify_one(); - ++simultaneous_running_count; - if(simultaneous_running_count>max_simultaneous_running) - { - max_simultaneous_running=simultaneous_running_count; - } - } - - // wait to finish - boost::mutex::scoped_lock finish_lock(finish_mutex); - { - boost::mutex::scoped_lock ublock(unblocked_count_mutex); - --simultaneous_running_count; - } - } - }; - -} - - void test_multiple_readers() { unsigned const number_of_threads=100; @@ -328,250 +265,11 @@ void test_unlocking_last_reader_only_unblocks_one_writer() throw; } - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+writer_count); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_readers,reader_count); CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_writers,1u); } -void test_only_one_upgrade_lock_permitted() -{ - unsigned const number_of_threads=100; - - boost::thread_group pool; - - boost::shared_mutex rw_mutex; - unsigned unblocked_count=0; - unsigned simultaneous_running_count=0; - unsigned max_simultaneous_running=0; - boost::mutex unblocked_count_mutex; - boost::condition_variable unblocked_condition; - boost::mutex finish_mutex; - boost::mutex::scoped_lock finish_lock(finish_mutex); - - try - { - for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_mutex,simultaneous_running_count,max_simultaneous_running)); - } - - boost::thread::sleep(delay(1)); - - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); - - finish_lock.unlock(); - - pool.join_all(); - } - catch(...) - { - pool.interrupt_all(); - pool.join_all(); - throw; - } - - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,number_of_threads); - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1u); -} - -void test_can_lock_upgrade_if_currently_locked_shared() -{ - boost::thread_group pool; - - boost::shared_mutex rw_mutex; - unsigned unblocked_count=0; - unsigned simultaneous_running_count=0; - unsigned max_simultaneous_running=0; - boost::mutex unblocked_count_mutex; - boost::condition_variable unblocked_condition; - boost::mutex finish_mutex; - boost::mutex::scoped_lock finish_lock(finish_mutex); - - unsigned const reader_count=100; - - try - { - for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_mutex,simultaneous_running_count,max_simultaneous_running)); - } - boost::thread::sleep(delay(1)); - pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, - finish_mutex,simultaneous_running_count,max_simultaneous_running)); - { - boost::mutex::scoped_lock lk(unblocked_count_mutex); - while(unblocked_count<(reader_count+1)) - { - unblocked_condition.wait(lk); - } - } - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); - - finish_lock.unlock(); - pool.join_all(); - } - catch(...) - { - pool.interrupt_all(); - pool.join_all(); - throw; - } - - - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); - CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,reader_count+1); -} - -namespace -{ - class simple_writing_thread - { - boost::shared_mutex& rwm; - boost::mutex& finish_mutex; - boost::mutex& unblocked_mutex; - unsigned& unblocked_count; - - public: - simple_writing_thread(boost::shared_mutex& rwm_, - boost::mutex& finish_mutex_, - boost::mutex& unblocked_mutex_, - unsigned& unblocked_count_): - rwm(rwm_),finish_mutex(finish_mutex_), - unblocked_mutex(unblocked_mutex_),unblocked_count(unblocked_count_) - {} - - void operator()() - { - boost::unique_lock lk(rwm); - - { - boost::mutex::scoped_lock ulk(unblocked_mutex); - ++unblocked_count; - } - - boost::mutex::scoped_lock flk(finish_mutex); - } - }; -} - -void test_if_other_thread_has_write_lock_try_lock_shared_returns_false() -{ - - boost::shared_mutex rw_mutex; - boost::mutex finish_mutex; - boost::mutex unblocked_mutex; - unsigned unblocked_count=0; - boost::mutex::scoped_lock finish_lock(finish_mutex); - boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); - boost::thread::sleep(delay(1)); - CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); - - bool const try_succeeded=rw_mutex.try_lock_shared(); - BOOST_CHECK(!try_succeeded); - if(try_succeeded) - { - rw_mutex.unlock_shared(); - } - - finish_lock.unlock(); - writer.join(); -} - -void test_if_no_thread_has_lock_try_lock_shared_returns_true() -{ - boost::shared_mutex rw_mutex; - bool const try_succeeded=rw_mutex.try_lock_shared(); - BOOST_CHECK(try_succeeded); - if(try_succeeded) - { - rw_mutex.unlock_shared(); - } -} - -namespace -{ - class simple_reading_thread - { - boost::shared_mutex& rwm; - boost::mutex& finish_mutex; - boost::mutex& unblocked_mutex; - unsigned& unblocked_count; - - public: - simple_reading_thread(boost::shared_mutex& rwm_, - boost::mutex& finish_mutex_, - boost::mutex& unblocked_mutex_, - unsigned& unblocked_count_): - rwm(rwm_),finish_mutex(finish_mutex_), - unblocked_mutex(unblocked_mutex_),unblocked_count(unblocked_count_) - {} - - void operator()() - { - boost::shared_lock lk(rwm); - - { - boost::mutex::scoped_lock ulk(unblocked_mutex); - ++unblocked_count; - } - - boost::mutex::scoped_lock flk(finish_mutex); - } - }; -} - -void test_if_other_thread_has_shared_lock_try_lock_shared_returns_true() -{ - - boost::shared_mutex rw_mutex; - boost::mutex finish_mutex; - boost::mutex unblocked_mutex; - unsigned unblocked_count=0; - boost::mutex::scoped_lock finish_lock(finish_mutex); - boost::thread writer(simple_reading_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); - boost::thread::sleep(delay(1)); - CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); - - bool const try_succeeded=rw_mutex.try_lock_shared(); - BOOST_CHECK(try_succeeded); - if(try_succeeded) - { - rw_mutex.unlock_shared(); - } - - finish_lock.unlock(); - writer.join(); -} - -void test_timed_lock_shared_times_out_if_write_lock_held() -{ - boost::shared_mutex rw_mutex; - boost::mutex finish_mutex; - boost::mutex unblocked_mutex; - unsigned unblocked_count=0; - boost::mutex::scoped_lock finish_lock(finish_mutex); - boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); - boost::thread::sleep(delay(1)); - CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); - - boost::system_time const start=boost::get_system_time(); - boost::system_time const timeout=start+boost::posix_time::milliseconds(2000); - bool const timed_lock_succeeded=rw_mutex.timed_lock_shared(timeout); - BOOST_CHECK(in_range(boost::get_xtime(timeout),1)); - BOOST_CHECK(!timed_lock_succeeded); - if(timed_lock_succeeded) - { - rw_mutex.unlock_shared(); - } - - finish_lock.unlock(); - writer.join(); -} - - boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { boost::unit_test_framework::test_suite* test = @@ -582,12 +280,6 @@ boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) test->add(BOOST_TEST_CASE(&test_reader_blocks_writer)); test->add(BOOST_TEST_CASE(&test_unlocking_writer_unblocks_all_readers)); test->add(BOOST_TEST_CASE(&test_unlocking_last_reader_only_unblocks_one_writer)); - test->add(BOOST_TEST_CASE(&test_only_one_upgrade_lock_permitted)); - test->add(BOOST_TEST_CASE(&test_can_lock_upgrade_if_currently_locked_shared)); - test->add(BOOST_TEST_CASE(&test_if_other_thread_has_write_lock_try_lock_shared_returns_false)); - test->add(BOOST_TEST_CASE(&test_if_no_thread_has_lock_try_lock_shared_returns_true)); - test->add(BOOST_TEST_CASE(&test_if_other_thread_has_shared_lock_try_lock_shared_returns_true)); - test->add(BOOST_TEST_CASE(&test_timed_lock_shared_times_out_if_write_lock_held)); return test; } diff --git a/test/test_shared_mutex_part_2.cpp b/test/test_shared_mutex_part_2.cpp new file mode 100644 index 00000000..5666a956 --- /dev/null +++ b/test/test_shared_mutex_part_2.cpp @@ -0,0 +1,271 @@ +// (C) Copyright 2006-7 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) + +#include +#include +#include +#include "util.inl" +#include "shared_mutex_locking_thread.hpp" + +#define CHECK_LOCKED_VALUE_EQUAL(mutex_name,value,expected_value) \ + { \ + boost::mutex::scoped_lock lock(mutex_name); \ + BOOST_CHECK_EQUAL(value,expected_value); \ + } + + +void test_only_one_upgrade_lock_permitted() +{ + unsigned const number_of_threads=100; + + boost::thread_group pool; + + boost::shared_mutex rw_mutex; + unsigned unblocked_count=0; + unsigned simultaneous_running_count=0; + unsigned max_simultaneous_running=0; + boost::mutex unblocked_count_mutex; + boost::condition_variable unblocked_condition; + boost::mutex finish_mutex; + boost::mutex::scoped_lock finish_lock(finish_mutex); + + try + { + for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); + } + + boost::thread::sleep(delay(1)); + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); + + finish_lock.unlock(); + + pool.join_all(); + } + catch(...) + { + pool.interrupt_all(); + pool.join_all(); + throw; + } + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,number_of_threads); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1u); +} + +void test_can_lock_upgrade_if_currently_locked_shared() +{ + boost::thread_group pool; + + boost::shared_mutex rw_mutex; + unsigned unblocked_count=0; + unsigned simultaneous_running_count=0; + unsigned max_simultaneous_running=0; + boost::mutex unblocked_count_mutex; + boost::condition_variable unblocked_condition; + boost::mutex finish_mutex; + boost::mutex::scoped_lock finish_lock(finish_mutex); + + unsigned const reader_count=100; + + try + { + for(unsigned i=0;i >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); + } + boost::thread::sleep(delay(1)); + pool.create_thread(locking_thread >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); + { + boost::mutex::scoped_lock lk(unblocked_count_mutex); + while(unblocked_count<(reader_count+1)) + { + unblocked_condition.wait(lk); + } + } + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); + + finish_lock.unlock(); + pool.join_all(); + } + catch(...) + { + pool.interrupt_all(); + pool.join_all(); + throw; + } + + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count+1); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,reader_count+1); +} + +namespace +{ + class simple_writing_thread + { + boost::shared_mutex& rwm; + boost::mutex& finish_mutex; + boost::mutex& unblocked_mutex; + unsigned& unblocked_count; + + public: + simple_writing_thread(boost::shared_mutex& rwm_, + boost::mutex& finish_mutex_, + boost::mutex& unblocked_mutex_, + unsigned& unblocked_count_): + rwm(rwm_),finish_mutex(finish_mutex_), + unblocked_mutex(unblocked_mutex_),unblocked_count(unblocked_count_) + {} + + void operator()() + { + boost::unique_lock lk(rwm); + + { + boost::mutex::scoped_lock ulk(unblocked_mutex); + ++unblocked_count; + } + + boost::mutex::scoped_lock flk(finish_mutex); + } + }; +} + +void test_if_other_thread_has_write_lock_try_lock_shared_returns_false() +{ + + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + unsigned unblocked_count=0; + boost::mutex::scoped_lock finish_lock(finish_mutex); + boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); + boost::this_thread::sleep(boost::posix_time::seconds(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); + + bool const try_succeeded=rw_mutex.try_lock_shared(); + BOOST_CHECK(!try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_shared(); + } + + finish_lock.unlock(); + writer.join(); +} + +void test_if_no_thread_has_lock_try_lock_shared_returns_true() +{ + boost::shared_mutex rw_mutex; + bool const try_succeeded=rw_mutex.try_lock_shared(); + BOOST_CHECK(try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_shared(); + } +} + +namespace +{ + class simple_reading_thread + { + boost::shared_mutex& rwm; + boost::mutex& finish_mutex; + boost::mutex& unblocked_mutex; + unsigned& unblocked_count; + + public: + simple_reading_thread(boost::shared_mutex& rwm_, + boost::mutex& finish_mutex_, + boost::mutex& unblocked_mutex_, + unsigned& unblocked_count_): + rwm(rwm_),finish_mutex(finish_mutex_), + unblocked_mutex(unblocked_mutex_),unblocked_count(unblocked_count_) + {} + + void operator()() + { + boost::shared_lock lk(rwm); + + { + boost::mutex::scoped_lock ulk(unblocked_mutex); + ++unblocked_count; + } + + boost::mutex::scoped_lock flk(finish_mutex); + } + }; +} + +void test_if_other_thread_has_shared_lock_try_lock_shared_returns_true() +{ + + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + unsigned unblocked_count=0; + boost::mutex::scoped_lock finish_lock(finish_mutex); + boost::thread writer(simple_reading_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); + + bool const try_succeeded=rw_mutex.try_lock_shared(); + BOOST_CHECK(try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_shared(); + } + + finish_lock.unlock(); + writer.join(); +} + +void test_timed_lock_shared_times_out_if_write_lock_held() +{ + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + unsigned unblocked_count=0; + boost::mutex::scoped_lock finish_lock(finish_mutex); + boost::thread writer(simple_writing_thread(rw_mutex,finish_mutex,unblocked_mutex,unblocked_count)); + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_mutex,unblocked_count,1u); + + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+boost::posix_time::milliseconds(2000); + boost::posix_time::milliseconds const timeout_resolution(20); + bool const timed_lock_succeeded=rw_mutex.timed_lock_shared(timeout); + BOOST_CHECK((timeout-timeout_resolution)add(BOOST_TEST_CASE(&test_only_one_upgrade_lock_permitted)); + test->add(BOOST_TEST_CASE(&test_can_lock_upgrade_if_currently_locked_shared)); + test->add(BOOST_TEST_CASE(&test_if_other_thread_has_write_lock_try_lock_shared_returns_false)); + test->add(BOOST_TEST_CASE(&test_if_no_thread_has_lock_try_lock_shared_returns_true)); + test->add(BOOST_TEST_CASE(&test_if_other_thread_has_shared_lock_try_lock_shared_returns_true)); + test->add(BOOST_TEST_CASE(&test_timed_lock_shared_times_out_if_write_lock_held)); + + return test; +} diff --git a/test/test_thread_id.cpp b/test/test_thread_id.cpp new file mode 100644 index 00000000..777bc718 --- /dev/null +++ b/test/test_thread_id.cpp @@ -0,0 +1,149 @@ +// Copyright (C) 2007 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) +#include +#include +#include + +void do_nothing() +{} + +void test_thread_id_for_default_constructed_thread_is_default_constructed_id() +{ + boost::thread t; + BOOST_CHECK(t.get_id()==boost::thread::id()); +} + +void test_thread_id_for_running_thread_is_not_default_constructed_id() +{ + boost::thread t(do_nothing); + BOOST_CHECK(t.get_id()!=boost::thread::id()); + t.join(); +} + +void test_different_threads_have_different_ids() +{ + boost::thread t(do_nothing); + boost::thread t2(do_nothing); + BOOST_CHECK(t.get_id()!=t2.get_id()); + t.join(); + t2.join(); +} + +void test_thread_ids_have_a_total_order() +{ + boost::thread t(do_nothing); + boost::thread t2(do_nothing); + boost::thread t3(do_nothing); + BOOST_CHECK(t.get_id()!=t2.get_id()); + BOOST_CHECK(t.get_id()!=t3.get_id()); + BOOST_CHECK(t2.get_id()!=t3.get_id()); + + BOOST_CHECK((t.get_id()t2.get_id()) != (t2.get_id()>t.get_id())); + BOOST_CHECK((t.get_id()>t3.get_id()) != (t3.get_id()>t.get_id())); + BOOST_CHECK((t2.get_id()>t3.get_id()) != (t3.get_id()>t2.get_id())); + + BOOST_CHECK((t.get_id()t.get_id())); + BOOST_CHECK((t2.get_id()t2.get_id())); + BOOST_CHECK((t.get_id()t.get_id())); + BOOST_CHECK((t3.get_id()t3.get_id())); + BOOST_CHECK((t2.get_id()t2.get_id())); + BOOST_CHECK((t3.get_id()t3.get_id())); + + BOOST_CHECK((t.get_id()=t.get_id())); + BOOST_CHECK((t2.get_id()=t2.get_id())); + BOOST_CHECK((t.get_id()=t.get_id())); + BOOST_CHECK((t3.get_id()=t3.get_id())); + BOOST_CHECK((t2.get_id()=t2.get_id())); + BOOST_CHECK((t3.get_id()=t3.get_id())); + + BOOST_CHECK((t.get_id()<=t2.get_id()) == (t2.get_id()>t.get_id())); + BOOST_CHECK((t2.get_id()<=t.get_id()) == (t.get_id()>t2.get_id())); + BOOST_CHECK((t.get_id()<=t3.get_id()) == (t3.get_id()>t.get_id())); + BOOST_CHECK((t3.get_id()<=t.get_id()) == (t.get_id()>t3.get_id())); + BOOST_CHECK((t2.get_id()<=t3.get_id()) == (t3.get_id()>t2.get_id())); + BOOST_CHECK((t3.get_id()<=t2.get_id()) == (t2.get_id()>t3.get_id())); + + if((t.get_id() t.get_id())); + BOOST_CHECK(!(default_id > t2.get_id())); + BOOST_CHECK(!(default_id > t3.get_id())); + + BOOST_CHECK(!(default_id >= t.get_id())); + BOOST_CHECK(!(default_id >= t2.get_id())); + BOOST_CHECK(!(default_id >= t3.get_id())); + + t.join(); + t2.join(); + t3.join(); +} + +void get_thread_id(boost::thread::id* id) +{ + *id=boost::this_thread::get_id(); +} + +void test_thread_id_of_running_thread_returned_by_this_thread_get_id() +{ + boost::thread::id id; + boost::thread t(boost::bind(get_thread_id,&id)); + boost::thread::id t_id=t.get_id(); + t.join(); + BOOST_CHECK(id==t_id); +} + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: thread move test suite"); + + test->add(BOOST_TEST_CASE(test_thread_id_for_default_constructed_thread_is_default_constructed_id)); + test->add(BOOST_TEST_CASE(test_thread_id_for_running_thread_is_not_default_constructed_id)); + test->add(BOOST_TEST_CASE(test_different_threads_have_different_ids)); + test->add(BOOST_TEST_CASE(test_thread_ids_have_a_total_order)); + test->add(BOOST_TEST_CASE(test_thread_id_of_running_thread_returned_by_this_thread_get_id)); + return test; +} diff --git a/test/test_thread_move.cpp b/test/test_thread_move.cpp new file mode 100644 index 00000000..dc8f964f --- /dev/null +++ b/test/test_thread_move.cpp @@ -0,0 +1,37 @@ +// Copyright (C) 2007 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) +#include +#include + +void do_nothing() +{} + +void test_move_on_construction() +{ + boost::thread x=boost::thread(do_nothing); + x.join(); +} + +boost::thread make_thread() +{ + return boost::thread(do_nothing); +} + +void test_move_from_function_return() +{ + boost::thread x=make_thread(); + x.join(); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: thread move test suite"); + + test->add(BOOST_TEST_CASE(test_move_on_construction)); + test->add(BOOST_TEST_CASE(test_move_from_function_return)); + return test; +} diff --git a/test/test_tss.cpp b/test/test_tss.cpp index d53c1d34..462c5523 100644 --- a/test/test_tss.cpp +++ b/test/test_tss.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf +// Copyright (C) 2007 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) @@ -73,7 +74,7 @@ void test_tss_thread() native_thread_t create_native_thread(void) { - return CreateThread( + native_thread_t const res=CreateThread( 0, //security attributes (0 = not inheritable) 0, //stack size (0 = default) &test_tss_thread_native, //function to execute @@ -81,6 +82,8 @@ void test_tss_thread() 0, //creation flags (0 = run immediately) 0 //thread id (0 = thread id not returned) ); + BOOST_CHECK(res!=0); + return res; } void join_native_thread(native_thread_t thread) @@ -154,19 +157,10 @@ void do_test_tss() tss_total = 0; native_thread_t thread1 = create_native_thread(); - BOOST_CHECK(thread1 != 0); - native_thread_t thread2 = create_native_thread(); - BOOST_CHECK(thread2 != 0); - native_thread_t thread3 = create_native_thread(); - BOOST_CHECK(thread3 != 0); - native_thread_t thread4 = create_native_thread(); - BOOST_CHECK(thread3 != 0); - native_thread_t thread5 = create_native_thread(); - BOOST_CHECK(thread3 != 0); join_native_thread(thread5); join_native_thread(thread4); diff --git a/test/util.inl b/test/util.inl index 32e7c77a..417b255d 100644 --- a/test/util.inl +++ b/test/util.inl @@ -1,5 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf +// Copyright (C) 2007 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) @@ -154,6 +155,26 @@ 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: + R (T::*func)(); + T& param; +}; + + +template +thread_member_binder bind(R (T::*func)(), T& param) +{ + return thread_member_binder(func, param); +} } // namespace #endif