diff --git a/include/boost/thread.hpp b/include/boost/thread.hpp index 625e69ec..f0a39c3b 100644 --- a/include/boost/thread.hpp +++ b/include/boost/thread.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2001-2003 // William E. Kempf +// (C) Copyright 2008 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) @@ -10,12 +11,15 @@ #define BOOST_THREAD_WEK01082003_HPP #include -#include +#include #include #include #include #include #include -#include +#include +#include +#include +#include #endif diff --git a/include/boost/thread/locks.hpp b/include/boost/thread/locks.hpp index c056206b..741a2a47 100644 --- a/include/boost/thread/locks.hpp +++ b/include/boost/thread/locks.hpp @@ -400,13 +400,16 @@ namespace boost { lock(); } - upgrade_lock(Mutex& m_,bool do_lock): + upgrade_lock(Mutex& m_,adopt_lock_t): + m(&m_),is_locked(true) + {} + upgrade_lock(Mutex& m_,defer_lock_t): + m(&m_),is_locked(false) + {} + upgrade_lock(Mutex& m_,try_to_lock_t): m(&m_),is_locked(false) { - if(do_lock) - { - lock(); - } + try_lock(); } upgrade_lock(detail::thread_move_t > other): m(other->m),is_locked(other->is_locked) diff --git a/include/boost/thread/pthread/condition_variable_fwd.hpp b/include/boost/thread/pthread/condition_variable_fwd.hpp index 1d1fbdf4..37884dd9 100644 --- a/include/boost/thread/pthread/condition_variable_fwd.hpp +++ b/include/boost/thread/pthread/condition_variable_fwd.hpp @@ -58,6 +58,12 @@ namespace boost return timed_wait(m,get_system_time()+wait_duration,pred); } + typedef pthread_cond_t* native_handle_type; + native_handle_type native_handle() + { + return &cond; + } + void notify_one(); void notify_all(); }; diff --git a/include/boost/thread/pthread/mutex.hpp b/include/boost/thread/pthread/mutex.hpp index 89a2bb94..3044a25b 100644 --- a/include/boost/thread/pthread/mutex.hpp +++ b/include/boost/thread/pthread/mutex.hpp @@ -136,7 +136,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; } #else diff --git a/include/boost/thread/pthread/shared_mutex.hpp b/include/boost/thread/pthread/shared_mutex.hpp index 4465f559..1648150d 100644 --- a/include/boost/thread/pthread/shared_mutex.hpp +++ b/include/boost/thread/pthread/shared_mutex.hpp @@ -118,6 +118,12 @@ namespace boost } } + template + bool timed_lock_shared(TimeDuration const & relative_time) + { + return timed_lock_shared(get_system_time()+relative_time); + } + void unlock_shared() { boost::mutex::scoped_lock lock(state_change); @@ -196,6 +202,12 @@ namespace boost } } + template + bool timed_lock(TimeDuration const & relative_time) + { + return timed_lock(get_system_time()+relative_time); + } + bool try_lock() { boost::mutex::scoped_lock lock(state_change); @@ -271,6 +283,12 @@ namespace boost } } + template + bool timed_lock_upgrade(TimeDuration const & relative_time) + { + return timed_lock(get_system_time()+relative_time); + } + bool try_lock_upgrade() { boost::mutex::scoped_lock lock(state_change); diff --git a/include/boost/thread/pthread/thread.hpp b/include/boost/thread/pthread/thread.hpp index 6a2ac3d9..813ced09 100644 --- a/include/boost/thread/pthread/thread.hpp +++ b/include/boost/thread/pthread/thread.hpp @@ -214,6 +214,9 @@ namespace boost static void sleep(const system_time& xt); static void yield(); + typedef pthread_t native_handle_type; + native_handle_type native_handle(); + // extensions void interrupt(); bool interruption_requested() const; diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index 6bd4efd6..e606258d 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -34,6 +34,12 @@ namespace boost list_entry(): semaphore(0),count(0),notified(0) {} + + void release(unsigned count_to_release=1) + { + detail::win32::ReleaseSemaphore(semaphore,count_to_release,0); + } + }; BOOST_STATIC_CONSTANT(unsigned,generation_count=3); @@ -41,6 +47,13 @@ namespace boost list_entry generations[generation_count]; detail::win32::handle wake_sem; + void wake_waiters(long count_to_wake) + { + detail::interlocked_write_release(&total_count,total_count-count_to_wake); + detail::win32::ReleaseSemaphore(wake_sem,count_to_wake,0); + } + + static bool no_waiters(list_entry const& entry) { return entry.count==0; @@ -51,7 +64,7 @@ namespace boost list_entry* const last_active_entry=std::remove_if(generations,generations+generation_count,no_waiters); if(last_active_entry==generations+generation_count) { - broadcast_entry(generations[generation_count-1],false); + broadcast_entry(generations[generation_count-1]); } else { @@ -69,15 +82,9 @@ namespace boost generations[0]=list_entry(); } - void broadcast_entry(list_entry& entry,bool wake) + void broadcast_entry(list_entry& entry) { - long const count_to_wake=entry.count; - detail::interlocked_write_release(&total_count,total_count-count_to_wake); - if(wake) - { - detail::win32::ReleaseSemaphore(wake_sem,count_to_wake,0); - } - detail::win32::ReleaseSemaphore(entry.semaphore,count_to_wake,0); + entry.release(entry.count); entry.count=0; dispose_entry(entry); } @@ -124,6 +131,7 @@ namespace boost void start_wait_loop_first_time(relocker& locker, detail::win32::handle_manager& local_wake_sem) { + detail::interlocked_write_release(&total_count,total_count+1); locker.unlock(); if(!wake_sem) { @@ -141,6 +149,15 @@ namespace boost active_generation_count=1; } } + + void ensure_generation_present() + { + if(!generations[0].semaphore) + { + generations[0].semaphore=detail::win32::create_anonymous_semaphore(0,LONG_MAX); + BOOST_ASSERT(generations[0].semaphore); + } + } template void start_wait_loop(relocker& locker, @@ -148,16 +165,11 @@ namespace boost 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); - } + ensure_generation_present(); ++generations[0].count; sem=detail::win32::duplicate_handle(generations[0].semaphore); } @@ -222,15 +234,22 @@ namespace boost if(detail::interlocked_read_acquire(&total_count)) { boost::mutex::scoped_lock internal_lock(internal_mutex); - detail::win32::ReleaseSemaphore(wake_sem,1,0); + if(!total_count) + { + return; + } + wake_waiters(1); + + unsigned waiting_count=0; + for(unsigned generation=active_generation_count;generation!=0;--generation) { list_entry& entry=generations[generation-1]; + waiting_count+=entry.count; if(entry.count) { - detail::interlocked_write_release(&total_count,total_count-1); entry.notified=true; - detail::win32::ReleaseSemaphore(entry.semaphore,1,0); + entry.release(); if(!--entry.count) { dispose_entry(entry); @@ -241,6 +260,12 @@ namespace boost } } } + if(waiting_count<=total_count) + { + shift_generations_down(); + ensure_generation_present(); + generations[0].release(); + } } } @@ -249,14 +274,23 @@ namespace boost if(detail::interlocked_read_acquire(&total_count)) { boost::mutex::scoped_lock internal_lock(internal_mutex); + long waiting_count=total_count; + + wake_waiters(total_count); for(unsigned generation=active_generation_count;generation!=0;--generation) { list_entry& entry=generations[generation-1]; if(entry.count) { - broadcast_entry(entry,true); + waiting_count-=entry.count; + broadcast_entry(entry); } } + if(waiting_count) + { + ensure_generation_present(); + generations[0].release(waiting_count); + } active_generation_count=0; } } @@ -265,9 +299,18 @@ namespace boost } class condition_variable: - public detail::basic_condition_variable + private detail::basic_condition_variable { + private: + condition_variable(condition_variable&); + void operator=(condition_variable&); public: + condition_variable() + {} + + using detail::basic_condition_variable::notify_one; + using detail::basic_condition_variable::notify_all; + void wait(unique_lock& m) { do_wait(m,detail::timeout::sentinel()); @@ -313,9 +356,18 @@ namespace boost }; class condition_variable_any: - public detail::basic_condition_variable + private detail::basic_condition_variable { + private: + condition_variable_any(condition_variable_any&); + void operator=(condition_variable_any&); public: + condition_variable_any() + {} + + using detail::basic_condition_variable::notify_one; + using detail::basic_condition_variable::notify_all; + template void wait(lock_type& m) { diff --git a/include/boost/thread/win32/shared_mutex.hpp b/include/boost/thread/win32/shared_mutex.hpp index 1c9fd1be..1862cfdd 100644 --- a/include/boost/thread/win32/shared_mutex.hpp +++ b/include/boost/thread/win32/shared_mutex.hpp @@ -120,6 +120,12 @@ namespace boost BOOST_VERIFY(timed_lock_shared(::boost::detail::get_system_time_sentinel())); } + template + bool timed_lock_shared(TimeDuration const & relative_time) + { + return timed_lock_shared(get_system_time()+relative_time); + } + bool timed_lock_shared(boost::system_time const& wait_until) { #ifdef BOOST_MSVC @@ -269,6 +275,12 @@ namespace boost BOOST_VERIFY(timed_lock(::boost::detail::get_system_time_sentinel())); } + template + bool timed_lock(TimeDuration const & relative_time) + { + return timed_lock(get_system_time()+relative_time); + } + bool timed_lock(boost::system_time const& wait_until) { #ifdef BOOST_MSVC diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 754e0af5..3cd6a86a 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -472,6 +472,21 @@ namespace boost return false; } } + + thread::native_handle_type thread::native_handle() + { + detail::thread_data_ptr const local_thread_info=get_thread_info(); + if(local_thread_info) + { + lock_guard lk(local_thread_info->data_mutex); + return local_thread_info->thread_handle; + } + else + { + return pthread_t(); + } + } + namespace this_thread diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 0145cf5b..04dab7af 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -51,6 +51,7 @@ rule thread-run ( sources ) [ thread-run test_barrier.cpp ] [ thread-run test_shared_mutex.cpp ] [ thread-run test_shared_mutex_part_2.cpp ] + [ thread-run test_shared_mutex_timed_locks.cpp ] [ thread-run test_lock_concept.cpp ] ; } diff --git a/test/shared_mutex_locking_thread.hpp b/test/shared_mutex_locking_thread.hpp index 0d071bd8..98a415b1 100644 --- a/test/shared_mutex_locking_thread.hpp +++ b/test/shared_mutex_locking_thread.hpp @@ -66,5 +66,67 @@ private: void operator=(locking_thread&); }; +class simple_writing_thread +{ + boost::shared_mutex& rwm; + boost::mutex& finish_mutex; + boost::mutex& unblocked_mutex; + unsigned& unblocked_count; + + void operator=(simple_writing_thread&); + +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); + } +}; + +class simple_reading_thread +{ + boost::shared_mutex& rwm; + boost::mutex& finish_mutex; + boost::mutex& unblocked_mutex; + unsigned& unblocked_count; + + void operator=(simple_reading_thread&); + +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); + } +}; + #endif diff --git a/test/test_condition_notify_all.cpp b/test/test_condition_notify_all.cpp index 22074b44..11a983eb 100644 --- a/test/test_condition_notify_all.cpp +++ b/test/test_condition_notify_all.cpp @@ -159,6 +159,47 @@ void do_test_condition_notify_all_wakes_from_relative_timed_wait_with_predicate( } } +namespace +{ + boost::mutex multiple_wake_mutex; + boost::condition_variable multiple_wake_cond; + unsigned multiple_wake_count=0; + + void wait_for_condvar_and_increase_count() + { + boost::mutex::scoped_lock lk(multiple_wake_mutex); + multiple_wake_cond.wait(lk); + ++multiple_wake_count; + } + +} + + +void do_test_notify_all_following_notify_one_wakes_all_threads() +{ + boost::thread thread1(wait_for_condvar_and_increase_count); + boost::thread thread2(wait_for_condvar_and_increase_count); + + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + multiple_wake_cond.notify_one(); + + boost::thread thread3(wait_for_condvar_and_increase_count); + + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + multiple_wake_cond.notify_one(); + multiple_wake_cond.notify_all(); + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + + { + boost::mutex::scoped_lock lk(multiple_wake_mutex); + BOOST_CHECK(multiple_wake_count==3); + } + + thread1.join(); + thread2.join(); + thread3.join(); +} + void test_condition_notify_all() { timed_test(&do_test_condition_notify_all_wakes_from_wait, timeout_seconds); @@ -166,6 +207,7 @@ void test_condition_notify_all() timed_test(&do_test_condition_notify_all_wakes_from_timed_wait, timeout_seconds); timed_test(&do_test_condition_notify_all_wakes_from_timed_wait_with_predicate, timeout_seconds); timed_test(&do_test_condition_notify_all_wakes_from_relative_timed_wait_with_predicate, timeout_seconds); + timed_test(&do_test_notify_all_following_notify_one_wakes_all_threads, timeout_seconds); } diff --git a/test/test_condition_notify_one.cpp b/test/test_condition_notify_one.cpp index 45447bd4..a5d82bec 100644 --- a/test/test_condition_notify_one.cpp +++ b/test/test_condition_notify_one.cpp @@ -92,6 +92,47 @@ void do_test_condition_notify_one_wakes_from_relative_timed_wait_with_predicate( BOOST_CHECK(data.woken); } +namespace +{ + boost::mutex multiple_wake_mutex; + boost::condition_variable multiple_wake_cond; + unsigned multiple_wake_count=0; + + void wait_for_condvar_and_increase_count() + { + boost::mutex::scoped_lock lk(multiple_wake_mutex); + multiple_wake_cond.wait(lk); + ++multiple_wake_count; + } + +} + + +void do_test_multiple_notify_one_calls_wakes_multiple_threads() +{ + boost::thread thread1(wait_for_condvar_and_increase_count); + boost::thread thread2(wait_for_condvar_and_increase_count); + + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + multiple_wake_cond.notify_one(); + + boost::thread thread3(wait_for_condvar_and_increase_count); + + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + multiple_wake_cond.notify_one(); + multiple_wake_cond.notify_one(); + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + + { + boost::mutex::scoped_lock lk(multiple_wake_mutex); + BOOST_CHECK(multiple_wake_count==3); + } + + thread1.join(); + thread2.join(); + thread3.join(); +} + void test_condition_notify_one() { timed_test(&do_test_condition_notify_one_wakes_from_wait, timeout_seconds, execution_monitor::use_mutex); @@ -99,6 +140,7 @@ void test_condition_notify_one() 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); + timed_test(&do_test_multiple_notify_one_calls_wakes_multiple_threads, timeout_seconds, execution_monitor::use_mutex); } diff --git a/test/test_shared_mutex_part_2.cpp b/test/test_shared_mutex_part_2.cpp index ae7348df..2daa8582 100644 --- a/test/test_shared_mutex_part_2.cpp +++ b/test/test_shared_mutex_part_2.cpp @@ -107,40 +107,6 @@ void test_can_lock_upgrade_if_currently_locked_shared() 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; - - void operator=(simple_writing_thread&); - - 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() { @@ -175,40 +141,6 @@ void test_if_no_thread_has_lock_try_lock_shared_returns_true() } } -namespace -{ - class simple_reading_thread - { - boost::shared_mutex& rwm; - boost::mutex& finish_mutex; - boost::mutex& unblocked_mutex; - unsigned& unblocked_count; - - void operator=(simple_reading_thread&); - - 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() { @@ -232,33 +164,6 @@ void test_if_other_thread_has_shared_lock_try_lock_shared_returns_true() 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_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_timed_locks.cpp b/test/test_shared_mutex_timed_locks.cpp new file mode 100644 index 00000000..815f7d3e --- /dev/null +++ b/test/test_shared_mutex_timed_locks.cpp @@ -0,0 +1,235 @@ +// (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_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(500); + boost::posix_time::milliseconds const timeout_resolution(50); + bool timed_lock_succeeded=rw_mutex.timed_lock_shared(timeout); + BOOST_CHECK((timeout-timeout_resolution)add(BOOST_TEST_CASE(&test_timed_lock_shared_times_out_if_write_lock_held)); + test->add(BOOST_TEST_CASE(&test_timed_lock_shared_succeeds_if_no_lock_held)); + test->add(BOOST_TEST_CASE(&test_timed_lock_shared_succeeds_if_read_lock_held)); + test->add(BOOST_TEST_CASE(&test_timed_lock_times_out_if_write_lock_held)); + test->add(BOOST_TEST_CASE(&test_timed_lock_times_out_if_read_lock_held)); + test->add(BOOST_TEST_CASE(&test_timed_lock_succeeds_if_no_lock_held)); + + return test; +}