diff options
Diffstat (limited to 'libs/thread/test')
45 files changed, 7145 insertions, 0 deletions
diff --git a/libs/thread/test/Carbon.r b/libs/thread/test/Carbon.r new file mode 100644 index 0000000000..e0ac5b449e --- /dev/null +++ b/libs/thread/test/Carbon.r @@ -0,0 +1,18 @@ +/* + * Permit this Carbon application to launch on OS X + * + * © 1997-2000 Metrowerks Corp. + * + * Questions and comments to: + * <mailto:support@metrowerks.com> + * <http://www.metrowerks.com/> + */ + + +/*----------------------------carb ¥ Carbon on OS X launch information --------------------------*/ +type 'carb' { +}; + + +resource 'carb'(0) { +}; diff --git a/libs/thread/test/Jamfile.v2 b/libs/thread/test/Jamfile.v2 new file mode 100644 index 0000000000..d1d61179cb --- /dev/null +++ b/libs/thread/test/Jamfile.v2 @@ -0,0 +1,90 @@ +# (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) +# +# Boost.Threads test Jamfile +# +# Additional configuration variables used: +# 1. PTW32 may be used on Win32 platforms to specify that the pthreads-win32 +# library should be used instead of "native" threads. This feature is +# mostly used for testing and it's generally recommended you use the +# native threading libraries instead. PTW32 should be set to be a list +# of two strings, the first specifying the installation path of the +# pthreads-win32 library and the second specifying which library +# variant to link against (see the pthreads-win32 documentation). +# Example: jam -sPTW32="c:\pthreads-win32 pthreadVCE.lib" + +# bring in rules for testing +import testing ; + +project + : requirements <library>/boost/test//boost_unit_test_framework/<link>static + <threading>multi + ; + +rule thread-run ( sources ) +{ + return + [ run $(sources) ../build//boost_thread ] + [ run $(sources) ../src/tss_null.cpp ../build//boost_thread/<link>static + : : : : $(sources[1]:B)_lib ] + ; +} + +{ + 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_thread_return_local.cpp ] + [ thread-run test_thread_move_return.cpp ] + [ thread-run test_thread_launching.cpp ] + [ thread-run test_thread_mf.cpp ] + [ thread-run test_thread_exit.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_shared_mutex_timed_locks.cpp ] + [ thread-run test_lock_concept.cpp ] + [ thread-run test_generic_locks.cpp ] + [ thread-run test_futures.cpp ] + [ compile-fail no_implicit_move_from_lvalue_thread.cpp ] + [ compile-fail no_implicit_assign_from_lvalue_thread.cpp ] + ; + + + #explicit tickets ; + test-suite tickets + : + [ thread-run test_6170.cpp ] + ; + + + explicit oth_tickets ; + test-suite oth_tickets + : + [ thread-run test_2501.cpp ] + [ thread-run test_4521.cpp ] + [ thread-run test_4648.cpp ] + [ thread-run test_4882.cpp ] + [ thread-run test_5351.cpp ] + [ thread-run test_5502.cpp ] + [ thread-run test_5542_1.cpp ] + [ thread-run test_5542_2.cpp ] + [ thread-run test_5542_3.cpp ] + [ thread-run test_6130.cpp ] + [ thread-run test_6174.cpp ] + ; + +} diff --git a/libs/thread/test/condition_test_common.hpp b/libs/thread/test/condition_test_common.hpp new file mode 100644 index 0000000000..38f7790989 --- /dev/null +++ b/libs/thread/test/condition_test_common.hpp @@ -0,0 +1,97 @@ +#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 <boost/thread/condition_variable.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread_time.hpp> + +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; + } + private: + void operator=(check_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/libs/thread/test/no_implicit_assign_from_lvalue_thread.cpp b/libs/thread/test/no_implicit_assign_from_lvalue_thread.cpp new file mode 100644 index 0000000000..02b6893edd --- /dev/null +++ b/libs/thread/test/no_implicit_assign_from_lvalue_thread.cpp @@ -0,0 +1,15 @@ +// Copyright (C) 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) +#include <boost/thread/thread.hpp> + +void do_nothing() +{} + +void test() +{ + boost::thread t1(do_nothing); + boost::thread t2; + t2=t1; +} diff --git a/libs/thread/test/no_implicit_move_from_lvalue_thread.cpp b/libs/thread/test/no_implicit_move_from_lvalue_thread.cpp new file mode 100644 index 0000000000..5c4600ec26 --- /dev/null +++ b/libs/thread/test/no_implicit_move_from_lvalue_thread.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 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) +#include <boost/thread/thread.hpp> + +void do_nothing() +{} + +void test() +{ + boost::thread t1(do_nothing); + boost::thread t2(t1); +} diff --git a/libs/thread/test/shared_mutex_locking_thread.hpp b/libs/thread/test/shared_mutex_locking_thread.hpp new file mode 100644 index 0000000000..98a415b121 --- /dev/null +++ b/libs/thread/test/shared_mutex_locking_thread.hpp @@ -0,0 +1,132 @@ +#ifndef SHARED_MUTEX_LOCKING_THREAD_HPP +#define SHARED_MUTEX_LOCKING_THREAD_HPP + +// (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) + +#include <boost/thread/mutex.hpp> +#include <boost/thread/condition_variable.hpp> +#include <boost/thread/shared_mutex.hpp> + +template<typename lock_type> +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; + } + } +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<boost::shared_mutex> 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<boost::shared_mutex> lk(rwm); + + { + boost::mutex::scoped_lock ulk(unblocked_mutex); + ++unblocked_count; + } + + boost::mutex::scoped_lock flk(finish_mutex); + } +}; + + +#endif diff --git a/libs/thread/test/test.mcp b/libs/thread/test/test.mcp Binary files differnew file mode 100644 index 0000000000..f27f0b97b8 --- /dev/null +++ b/libs/thread/test/test.mcp diff --git a/libs/thread/test/test_2501.cpp b/libs/thread/test/test_2501.cpp new file mode 100644 index 0000000000..5091338670 --- /dev/null +++ b/libs/thread/test/test_2501.cpp @@ -0,0 +1,10 @@ +#include <boost/thread/shared_mutex.hpp> + +int main() { + + boost::shared_mutex mtx; boost::upgrade_lock<boost::shared_mutex> lk(mtx); + + boost::upgrade_to_unique_lock<boost::shared_mutex> lk2(lk); + + return 0; +} diff --git a/libs/thread/test/test_4521.cpp b/libs/thread/test/test_4521.cpp new file mode 100644 index 0000000000..24e1b165ef --- /dev/null +++ b/libs/thread/test/test_4521.cpp @@ -0,0 +1,21 @@ +#include <boost/thread.hpp> + +int calculate_the_answer_to_life_the_universe_and_everything() +{ + return 42; +} + +int main() { +boost::packaged_task<int> pt(calculate_the_answer_to_life_the_universe_and_everything); +boost::unique_future<int> fi=pt.get_future(); + +boost::thread task(boost::move(pt)); // launch task on a thread + +fi.wait(); // wait for it to finish + +//assert(fi.is_ready()); +//assert(fi.has_value()); +//assert(!fi.has_exception()); +//assert(fi.get_state()==boost::future_state::ready); +//assert(fi.get()==42); +} diff --git a/libs/thread/test/test_4648.cpp b/libs/thread/test/test_4648.cpp new file mode 100644 index 0000000000..b0f95f8d14 --- /dev/null +++ b/libs/thread/test/test_4648.cpp @@ -0,0 +1,42 @@ +#include <iostream> +#include <boost/thread.hpp> +#include <boost/current_function.hpp> + +class boostThreadLocksTest +{ +public: + boost::shared_mutex myMutex; + //boost::upgrade_lock<boost::shared_mutex> myLock; + static int firstFunction(boostThreadLocksTest *pBoostThreadLocksTest); + static int secondFunction(boostThreadLocksTest *pBoostThreadLocksTest, + boost::upgrade_lock<boost::shared_mutex>& upgr); + boostThreadLocksTest() + :myMutex() + //, myLock(myMutex,boost::defer_lock_t()) + {}; +}; + +int boostThreadLocksTest::firstFunction(boostThreadLocksTest *pBoostThreadLocksTest) +{ + std::cout<<"Entering "<<boost::this_thread::get_id()<<" "<<"firstFunction"<<std::endl; + boost::upgrade_lock<boost::shared_mutex> myLock(pBoostThreadLocksTest->myMutex); + pBoostThreadLocksTest->secondFunction(pBoostThreadLocksTest, myLock); + std::cout<<"Returned From Call "<<boost::this_thread::get_id()<<" "<<"firstFunction"<<std::endl; + std::cout<<"Returning from "<<boost::this_thread::get_id()<<" "<<"firstFunction"<<std::endl; + return(0); +} +int boostThreadLocksTest::secondFunction(boostThreadLocksTest *pBoostThreadLocksTest, boost::upgrade_lock<boost::shared_mutex>& upgr) { + std::cout<<"Before Exclusive Locking "<<boost::this_thread::get_id()<<" "<<"secondFunction"<<std::endl; + boost::upgrade_to_unique_lock<boost::shared_mutex> localUniqueLock(upgr); + std::cout<<"After Exclusive Locking "<<boost::this_thread::get_id()<<" "<<"secondFunction"<<std::endl; + return(0); +} +int main() { + boostThreadLocksTest myObject; + boost::thread_group myThreadGroup; + myThreadGroup.create_thread(boost::bind(boostThreadLocksTest::firstFunction,&myObject)); + myThreadGroup.create_thread(boost::bind(boostThreadLocksTest::firstFunction,&myObject)); + myThreadGroup.create_thread(boost::bind(boostThreadLocksTest::firstFunction,&myObject)); + myThreadGroup.join_all(); + return 0; +} diff --git a/libs/thread/test/test_4882.cpp b/libs/thread/test/test_4882.cpp new file mode 100644 index 0000000000..88dab8d9b6 --- /dev/null +++ b/libs/thread/test/test_4882.cpp @@ -0,0 +1,50 @@ +#include <boost/thread/thread.hpp> +#include <boost/thread/shared_mutex.hpp> + +#include <iostream> + +boost::shared_mutex mutex; + +void thread() +{ + std::cout << __FILE__ << ":" << __LINE__ << std::endl; + try + { + for (int i =0; i<10; ++i) + { + boost::system_time timeout = boost::get_system_time() + boost::posix_time::milliseconds(50); + + if (mutex.timed_lock(timeout)) + { + std::cout << __FILE__ << ":" << __LINE__ << std::endl; + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + mutex.unlock(); + std::cout << __FILE__ << ":" << __LINE__ << std::endl; + } + } + } + catch (boost::lock_error& le) + { + std::cerr << "lock_error exception\n"; + } + std::cout << __FILE__ << ":" << __LINE__ << std::endl; +} + +int main() +{ + std::cout << __FILE__ << ":" << __LINE__ << std::endl; + const int nrThreads = 20; + boost::thread* threads[nrThreads]; + + for (int i = 0; i < nrThreads; ++i) + threads[i] = new boost::thread(&thread); + + for (int i = 0; i < nrThreads; ++i) + { + threads[i]->join(); + std::cout << __FILE__ << ":" << __LINE__ << std::endl; + delete threads[i]; + } + std::cout << __FILE__ << ":" << __LINE__ << std::endl; + return 0; +} diff --git a/libs/thread/test/test_5351.cpp b/libs/thread/test/test_5351.cpp new file mode 100644 index 0000000000..543bb34c4c --- /dev/null +++ b/libs/thread/test/test_5351.cpp @@ -0,0 +1,45 @@ +#include <iostream> +#include <boost/thread.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> +#include <boost/thread/future.hpp> + +using namespace boost::posix_time; +using namespace boost; + +int foo() +{ + this_thread::sleep(seconds(10)); + return 0; +} + + +int main(int argc, char** argv) +{ + boost::packaged_task<int> pt(&foo); + boost::unique_future<int> fi = pt.get_future(); + boost::thread task(boost::move(pt)); // launch task on a thread + + task.interrupt(); + + try + { + int v = fi.get(); + } + catch (boost::thread_interrupted& exc) + { + std::cout << "OK: " << std::endl; + return 0; + } + catch (boost::exception& exc) + { + std::cout << __LINE__ << " ERROR: " << boost::diagnostic_information(exc) << std::endl; + return 1; + } + catch (...) + { + std::cout << __LINE__ << " ERROR: " << std::endl; + return 2; + } + std::cout << __LINE__ << " ERROR: " << std::endl; + return 3; +} diff --git a/libs/thread/test/test_5502.cpp b/libs/thread/test/test_5502.cpp new file mode 100644 index 0000000000..3baf9bb4a5 --- /dev/null +++ b/libs/thread/test/test_5502.cpp @@ -0,0 +1,86 @@ +// bm.cpp + +// g++ test.cpp -lboost_thread-mt && ./a.out + +// the ration of XXX and YYY determines +// if this works or deadlocks +int XXX = 20; +int YYY = 10; + +#include <boost/thread.hpp> +#include <boost/thread/shared_mutex.hpp> + +#include <unistd.h> +#include <iostream> +#include <boost/detail/lightweight_test.hpp> + +using namespace std; + +void sleepmillis(useconds_t miliis) +{ + usleep(miliis * 1000); +} + +void worker1(boost::shared_mutex * lk, int * x) +{ + (*x)++; // 1 + cout << "lock b try " << *x << endl; + while (1) + { + if (lk->timed_lock(boost::posix_time::milliseconds(XXX))) break; + sleepmillis(YYY); + } + cout << "lock b got " << *x << endl; + (*x)++; // 2 + lk->unlock(); +} + +void worker2(boost::shared_mutex * lk, int * x) +{ + cout << "lock c try" << endl; + lk->lock_shared(); + (*x)++; + cout << "lock c got" << endl; + lk->unlock_shared(); + cout << "lock c unlocked" << endl; + (*x)++; +} + +int main() +{ + + // create + boost::shared_mutex* lk = new boost::shared_mutex(); + + // read lock + cout << "lock a" << endl; + lk->lock_shared(); + + int x1 = 0; + boost::thread t1(boost::bind(worker1, lk, &x1)); + while (!x1) + ; + BOOST_TEST(x1 == 1); + sleepmillis(500); + BOOST_TEST(x1 == 1); + + int x2 = 0; + boost::thread t2(boost::bind(worker2, lk, &x2)); + t2.join(); + BOOST_TEST(x2 == 2); + + lk->unlock_shared(); + cout << "unlock a" << endl; + + for (int i = 0; i < 2000; i++) + { + if (x1 == 2) break; + sleepmillis(10); + } + + BOOST_TEST(x1 == 2); + t1.join(); + delete lk; + + return 0; +} diff --git a/libs/thread/test/test_5542_1.cpp b/libs/thread/test/test_5542_1.cpp new file mode 100644 index 0000000000..79dfd60db6 --- /dev/null +++ b/libs/thread/test/test_5542_1.cpp @@ -0,0 +1,62 @@ +#include <iostream> +#include <boost/thread.hpp> + +class Worker +{ +public: + + Worker() + { + // the thread is not-a-thread until we call start() + } + + void start(int N) + { + std::cout << "start\n"; + m_Thread = boost::thread(&Worker::processQueue, this, N); + std::cout << "started\n"; + } + + void join() + { + m_Thread.join(); + } + + void processQueue(unsigned N) + { + float ms = N * 1e3; + boost::posix_time::milliseconds workTime(ms); + + std::cout << "Worker: started, will work for " + << ms << "ms" + << std::endl; + + // We're busy, honest! + boost::this_thread::sleep(workTime); + + std::cout << "Worker: completed" << std::endl; + } + +private: + + boost::thread m_Thread; +}; + +int main(int argc, char* argv[]) +{ + std::cout << "main: startup" << std::endl; + + Worker worker; + + std::cout << "main: create worker" << std::endl; + + worker.start(3); + + std::cout << "main: waiting for thread" << std::endl; + + worker.join(); + + std::cout << "main: done" << std::endl; + + return 0; +} diff --git a/libs/thread/test/test_5542_2.cpp b/libs/thread/test/test_5542_2.cpp new file mode 100644 index 0000000000..c48ef4eaba --- /dev/null +++ b/libs/thread/test/test_5542_2.cpp @@ -0,0 +1,11 @@ +#include <boost/thread.hpp> + +void run_thread() { + return; +} + +int main() { + boost::thread t(run_thread); + return 0; +} + diff --git a/libs/thread/test/test_5542_3.cpp b/libs/thread/test/test_5542_3.cpp new file mode 100644 index 0000000000..0c1ac40bb2 --- /dev/null +++ b/libs/thread/test/test_5542_3.cpp @@ -0,0 +1,30 @@ +#include <iostream> +#include <boost/thread.hpp> +#include <boost/date_time.hpp> + +void workerFunc() +{ + boost::posix_time::seconds workTime(3); + + std::cout << "Worker: running" << std::endl; + + // Pretend to do something useful... + boost::this_thread::sleep(workTime); + + std::cout << "Worker: finished" << std::endl; +} + +int main(int argc, char* argv[]) +{ + std::cout << "main: startup" << std::endl; + + boost::thread workerThread(workerFunc); + + std::cout << "main: waiting for thread" << std::endl; + + workerThread.join(); + + std::cout << "main: done" << std::endl; + + return 0; +} diff --git a/libs/thread/test/test_6130.cpp b/libs/thread/test/test_6130.cpp new file mode 100644 index 0000000000..3ef1f18373 --- /dev/null +++ b/libs/thread/test/test_6130.cpp @@ -0,0 +1,22 @@ +#include <boost/thread.hpp> +#include <assert.h> +#include <iostream> +#include <stdlib.h> +#include <unistd.h> + +boost::mutex mtx; +boost::condition_variable cv; + +int main() +{ + for (int i=0; i<3; ++i) { + const time_t wait_time = ::time(0)+1; + + boost::mutex::scoped_lock lk(mtx); + const bool res = cv.timed_wait(lk, boost::posix_time::from_time_t(wait_time)); + const time_t end_time = ::time(0); + assert(end_time >= wait_time); + std::cerr << end_time - wait_time << " OK\n"; + } + return 0; +} diff --git a/libs/thread/test/test_6170.cpp b/libs/thread/test/test_6170.cpp new file mode 100644 index 0000000000..6f2f28fd65 --- /dev/null +++ b/libs/thread/test/test_6170.cpp @@ -0,0 +1,26 @@ +#include <boost/thread/shared_mutex.hpp> +#include <boost/thread/locks.hpp> + +// Including this will cause ambiguous errors in boost::move +#include <boost/unordered_map.hpp> + +using namespace boost; + +typedef upgrade_lock<shared_mutex> auto_upgrade_lock; +typedef upgrade_to_unique_lock<shared_mutex> auto_upgrade_unique_lock; + +void testUpgrade(void) +{ + shared_mutex mtx; + auto_upgrade_lock lock(mtx); + // Do some read-only stuff + + auto_upgrade_unique_lock writeLock(lock); + // Do some write-only stuff with the upgraded lock +} + +int main() +{ + testUpgrade(); + return 0; +}
\ No newline at end of file diff --git a/libs/thread/test/test_6174.cpp b/libs/thread/test/test_6174.cpp new file mode 100644 index 0000000000..b3c14ecacf --- /dev/null +++ b/libs/thread/test/test_6174.cpp @@ -0,0 +1,35 @@ + + +#include <boost/thread.hpp> +#include <boost/config.hpp> + +#ifndef BOOST_NO_RVALUE_REFERENCES +struct MovableButNonCopyable { +#ifndef BOOST_NO_DEFAULTED_FUNCTIONS + MovableButNonCopyable() = default; + MovableButNonCopyable(MovableButNonCopyable const&) = delete; + MovableButNonCopyable& operator=(MovableButNonCopyable const&) = delete; + MovableButNonCopyable(MovableButNonCopyable&&) = default; + MovableButNonCopyable& operator=(MovableButNonCopyable&&) = default; +#else + MovableButNonCopyable() {}; + MovableButNonCopyable(MovableButNonCopyable&&) {}; + MovableButNonCopyable& operator=(MovableButNonCopyable&&) { + return *this; + }; +private: + MovableButNonCopyable(MovableButNonCopyable const&); + MovableButNonCopyable& operator=(MovableButNonCopyable const&); +#endif +}; +int main() +{ + boost::packaged_task<MovableButNonCopyable>(MovableButNonCopyable()); + return 0; +} +#else +int main() +{ + return 0; +} +#endif diff --git a/libs/thread/test/test_barrier.cpp b/libs/thread/test/test_barrier.cpp new file mode 100644 index 0000000000..cd350ba2f7 --- /dev/null +++ b/libs/thread/test/test_barrier.cpp @@ -0,0 +1,66 @@ +// Copyright (C) 2001-2003 +// William E. Kempf +// +// 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 <boost/thread/detail/config.hpp> + +#include <boost/thread/thread.hpp> +#include <boost/thread/barrier.hpp> + +#include <boost/test/unit_test.hpp> +#include <vector> + +namespace { + +// Shared variables for generation barrier test +const int N_THREADS=10; +boost::barrier gen_barrier(N_THREADS); +boost::mutex mutex; +long global_parameter; + +void barrier_thread() +{ + for (int i = 0; i < 5; ++i) + { + if (gen_barrier.wait()) + { + boost::mutex::scoped_lock lock(mutex); + global_parameter++; + } + } +} + +} // namespace + +void test_barrier() +{ + boost::thread_group g; + global_parameter = 0; + + try + { + for (int i = 0; i < N_THREADS; ++i) + g.create_thread(&barrier_thread); + g.join_all(); + } + catch(...) + { + g.interrupt_all(); + g.join_all(); + throw; + } + + BOOST_CHECK_EQUAL(global_parameter,5); +} + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: barrier test suite"); + + test->add(BOOST_TEST_CASE(&test_barrier)); + + return test; +} diff --git a/libs/thread/test/test_condition.cpp b/libs/thread/test/test_condition.cpp new file mode 100644 index 0000000000..f33b7a2bd7 --- /dev/null +++ b/libs/thread/test/test_condition.cpp @@ -0,0 +1,190 @@ +// 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) + +#include <boost/thread/detail/config.hpp> + +#include <boost/thread/condition.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/xtime.hpp> + +#include <boost/test/unit_test.hpp> + +#include <libs/thread/test/util.inl> + +struct condition_test_data +{ + condition_test_data() : notified(0), awoken(0) { } + + boost::mutex mutex; + boost::condition_variable condition; + int notified; + int awoken; +}; + +void condition_test_thread(condition_test_data* data) +{ + boost::mutex::scoped_lock lock(data->mutex); + BOOST_CHECK(lock ? true : false); + while (!(data->notified > 0)) + data->condition.wait(lock); + BOOST_CHECK(lock ? true : false); + data->awoken++; +} + +struct cond_predicate +{ + cond_predicate(int& var, int val) : _var(var), _val(val) { } + + bool operator()() { return _var == _val; } + + int& _var; + int _val; +private: + void operator=(cond_predicate&); + +}; + +void condition_test_waits(condition_test_data* data) +{ + boost::mutex::scoped_lock lock(data->mutex); + BOOST_CHECK(lock ? true : false); + + // Test wait. + while (data->notified != 1) + data->condition.wait(lock); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data->notified, 1); + data->awoken++; + data->condition.notify_one(); + + // Test predicate wait. + data->condition.wait(lock, cond_predicate(data->notified, 2)); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data->notified, 2); + data->awoken++; + data->condition.notify_one(); + + // Test timed_wait. + boost::xtime xt = delay(10); + while (data->notified != 3) + data->condition.timed_wait(lock, xt); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data->notified, 3); + data->awoken++; + data->condition.notify_one(); + + // Test predicate timed_wait. + xt = delay(10); + cond_predicate pred(data->notified, 4); + BOOST_CHECK(data->condition.timed_wait(lock, xt, pred)); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK(pred()); + BOOST_CHECK_EQUAL(data->notified, 4); + data->awoken++; + data->condition.notify_one(); + + // 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() +{ + condition_test_data data; + + boost::thread thread(bind(&condition_test_waits, &data)); + + { + boost::mutex::scoped_lock lock(data.mutex); + BOOST_CHECK(lock ? true : false); + + boost::thread::sleep(delay(1)); + data.notified++; + data.condition.notify_one(); + while (data.awoken != 1) + data.condition.wait(lock); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data.awoken, 1); + + boost::thread::sleep(delay(1)); + data.notified++; + data.condition.notify_one(); + while (data.awoken != 2) + data.condition.wait(lock); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data.awoken, 2); + + boost::thread::sleep(delay(1)); + data.notified++; + data.condition.notify_one(); + while (data.awoken != 3) + data.condition.wait(lock); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data.awoken, 3); + + boost::thread::sleep(delay(1)); + data.notified++; + data.condition.notify_one(); + while (data.awoken != 4) + 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, 5); +} + +void test_condition_waits() +{ + // 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_waits, 12); +} + +void do_test_condition_wait_is_a_interruption_point() +{ + condition_test_data data; + + boost::thread thread(bind(&condition_test_thread, &data)); + + thread.interrupt(); + thread.join(); + BOOST_CHECK_EQUAL(data.awoken,0); +} + + +void test_condition_wait_is_a_interruption_point() +{ + timed_test(&do_test_condition_wait_is_a_interruption_point, 1); +} + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: condition test suite"); + + test->add(BOOST_TEST_CASE(&test_condition_waits)); + test->add(BOOST_TEST_CASE(&test_condition_wait_is_a_interruption_point)); + + return test; +} diff --git a/libs/thread/test/test_condition_notify_all.cpp b/libs/thread/test/test_condition_notify_all.cpp new file mode 100644 index 0000000000..83165c999b --- /dev/null +++ b/libs/thread/test/test_condition_notify_all.cpp @@ -0,0 +1,222 @@ +// 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 <boost/thread/detail/config.hpp> + +#include <boost/thread/thread.hpp> + +#include <boost/test/unit_test.hpp> + +#include <libs/thread/test/util.inl> +#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;i<number_of_test_threads;++i) + { + group.create_thread(bind(&wait_for_flag::wait_without_predicate, data)); + } + + { + boost::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_all(); + } + + group.join_all(); + BOOST_CHECK_EQUAL(data.woken,number_of_test_threads); + } + catch(...) + { + group.join_all(); + throw; + } +} + +void do_test_condition_notify_all_wakes_from_wait_with_predicate() +{ + wait_for_flag data; + + boost::thread_group group; + + try + { + for(unsigned i=0;i<number_of_test_threads;++i) + { + group.create_thread(bind(&wait_for_flag::wait_with_predicate, data)); + } + + { + boost::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_all(); + } + + group.join_all(); + BOOST_CHECK_EQUAL(data.woken,number_of_test_threads); + } + catch(...) + { + group.join_all(); + throw; + } +} + +void do_test_condition_notify_all_wakes_from_timed_wait() +{ + wait_for_flag data; + + boost::thread_group group; + + try + { + for(unsigned i=0;i<number_of_test_threads;++i) + { + group.create_thread(bind(&wait_for_flag::timed_wait_without_predicate, data)); + } + + { + boost::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_all(); + } + + group.join_all(); + BOOST_CHECK_EQUAL(data.woken,number_of_test_threads); + } + catch(...) + { + group.join_all(); + throw; + } +} + +void do_test_condition_notify_all_wakes_from_timed_wait_with_predicate() +{ + wait_for_flag data; + + boost::thread_group group; + + try + { + for(unsigned i=0;i<number_of_test_threads;++i) + { + group.create_thread(bind(&wait_for_flag::timed_wait_with_predicate, data)); + } + + { + boost::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_all(); + } + + group.join_all(); + BOOST_CHECK_EQUAL(data.woken,number_of_test_threads); + } + catch(...) + { + group.join_all(); + throw; + } +} + +void do_test_condition_notify_all_wakes_from_relative_timed_wait_with_predicate() +{ + wait_for_flag data; + + boost::thread_group group; + + try + { + for(unsigned i=0;i<number_of_test_threads;++i) + { + group.create_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_all(); + } + + group.join_all(); + BOOST_CHECK_EQUAL(data.woken,number_of_test_threads); + } + catch(...) + { + group.join_all(); + throw; + } +} + +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); + timed_test(&do_test_condition_notify_all_wakes_from_wait_with_predicate, timeout_seconds); + 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); +} + + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: condition test suite"); + + test->add(BOOST_TEST_CASE(&test_condition_notify_all)); + + return test; +} diff --git a/libs/thread/test/test_condition_notify_one.cpp b/libs/thread/test/test_condition_notify_one.cpp new file mode 100644 index 0000000000..1e489da60c --- /dev/null +++ b/libs/thread/test/test_condition_notify_one.cpp @@ -0,0 +1,155 @@ +// 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 <boost/thread/detail/config.hpp> + +#include <boost/thread/thread.hpp> + +#include <boost/test/unit_test.hpp> + +#include <libs/thread/test/util.inl> +#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); +} + +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); + 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); + timed_test(&do_test_multiple_notify_one_calls_wakes_multiple_threads, timeout_seconds, execution_monitor::use_mutex); +} + + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::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/libs/thread/test/test_condition_timed_wait_times_out.cpp b/libs/thread/test/test_condition_timed_wait_times_out.cpp new file mode 100644 index 0000000000..c731b5b68d --- /dev/null +++ b/libs/thread/test/test_condition_timed_wait_times_out.cpp @@ -0,0 +1,173 @@ +// Copyright (C) 2007-8 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/thread/detail/config.hpp> + +#include <boost/thread/condition.hpp> +#include <boost/thread/thread.hpp> + +#include <boost/test/unit_test.hpp> +#include "util.inl" + +bool fake_predicate() +{ + return false; +} + +unsigned const timeout_seconds=2; +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 do_test_timed_wait_relative_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(); + + while(cond.timed_wait(lock,delay)); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_cv_any_timed_wait_times_out() +{ + boost::condition_variable_any 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_cv_any_timed_wait_with_predicate_times_out() +{ + boost::condition_variable_any 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_cv_any_relative_timed_wait_with_predicate_times_out() +{ + boost::condition_variable_any 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 do_test_cv_any_timed_wait_relative_times_out() +{ + boost::condition_variable_any 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(); + + while(cond.timed_wait(lock,delay)); + + boost::system_time const end=boost::get_system_time(); + 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); + timed_test(&do_test_timed_wait_relative_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_cv_any_timed_wait_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_cv_any_timed_wait_with_predicate_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_cv_any_relative_timed_wait_with_predicate_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_cv_any_timed_wait_relative_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); +} + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::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/libs/thread/test/test_futures.cpp b/libs/thread/test/test_futures.cpp new file mode 100644 index 0000000000..4d89e5a9fa --- /dev/null +++ b/libs/thread/test/test_futures.cpp @@ -0,0 +1,1220 @@ +// (C) Copyright 2008-10 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 "boost/thread/thread.hpp" +#include "boost/thread/mutex.hpp" +#include "boost/thread/condition.hpp" +#include "boost/thread/future.hpp" +#include <utility> +#include <memory> +#include <string> + +#include <boost/test/unit_test.hpp> + +#ifndef BOOST_NO_RVALUE_REFERENCES + template<typename T> + typename boost::remove_reference<T>::type&& cast_to_rval(T&& t) + { + return static_cast<typename boost::remove_reference<T>::type&&>(t); + } +#else + template<typename T> + boost::detail::thread_move_t<T> cast_to_rval(T& t) + { + return boost::move(t); + } +#endif + +struct X +{ +private: + + X(X& other); + +public: + + int i; + + X(): + i(42) + {} +#ifndef BOOST_NO_RVALUE_REFERENCES + X(X&& other): + i(other.i) + { + other.i=0; + } +#else + X(boost::detail::thread_move_t<X> other): + i(other->i) + { + other->i=0; + } + operator boost::detail::thread_move_t<X>() + { + return boost::detail::thread_move_t<X>(*this); + } +#endif + ~X() + {} +}; + +int make_int() +{ + return 42; +} + +int throw_runtime_error() +{ + throw std::runtime_error("42"); +} + +void set_promise_thread(boost::promise<int>* p) +{ + p->set_value(42); +} + +struct my_exception +{}; + +void set_promise_exception_thread(boost::promise<int>* p) +{ + p->set_exception(boost::copy_exception(my_exception())); +} + + +void test_store_value_from_thread() +{ + boost::promise<int> pi2; + boost::unique_future<int> fi2(pi2.get_future()); + boost::thread(set_promise_thread,&pi2); + int j=fi2.get(); + BOOST_CHECK(j==42); + BOOST_CHECK(fi2.is_ready()); + BOOST_CHECK(fi2.has_value()); + BOOST_CHECK(!fi2.has_exception()); + BOOST_CHECK(fi2.get_state()==boost::future_state::ready); +} + + +void test_store_exception() +{ + boost::promise<int> pi3; + boost::unique_future<int> fi3=pi3.get_future(); + boost::thread(set_promise_exception_thread,&pi3); + try + { + fi3.get(); + BOOST_CHECK(false); + } + catch(my_exception) + { + BOOST_CHECK(true); + } + + BOOST_CHECK(fi3.is_ready()); + BOOST_CHECK(!fi3.has_value()); + BOOST_CHECK(fi3.has_exception()); + BOOST_CHECK(fi3.get_state()==boost::future_state::ready); +} + +void test_initial_state() +{ + boost::unique_future<int> fi; + BOOST_CHECK(!fi.is_ready()); + BOOST_CHECK(!fi.has_value()); + BOOST_CHECK(!fi.has_exception()); + BOOST_CHECK(fi.get_state()==boost::future_state::uninitialized); + int i; + try + { + i=fi.get(); + BOOST_CHECK(false); + } + catch(boost::future_uninitialized) + { + BOOST_CHECK(true); + } +} + +void test_waiting_future() +{ + boost::promise<int> pi; + boost::unique_future<int> fi; + fi=pi.get_future(); + + int i=0; + BOOST_CHECK(!fi.is_ready()); + BOOST_CHECK(!fi.has_value()); + BOOST_CHECK(!fi.has_exception()); + BOOST_CHECK(fi.get_state()==boost::future_state::waiting); + BOOST_CHECK(i==0); +} + +void test_cannot_get_future_twice() +{ + boost::promise<int> pi; + pi.get_future(); + + try + { + pi.get_future(); + BOOST_CHECK(false); + } + catch(boost::future_already_retrieved&) + { + BOOST_CHECK(true); + } +} + +void test_set_value_updates_future_state() +{ + boost::promise<int> pi; + boost::unique_future<int> fi; + fi=pi.get_future(); + + pi.set_value(42); + + BOOST_CHECK(fi.is_ready()); + BOOST_CHECK(fi.has_value()); + BOOST_CHECK(!fi.has_exception()); + BOOST_CHECK(fi.get_state()==boost::future_state::ready); +} + +void test_set_value_can_be_retrieved() +{ + boost::promise<int> pi; + boost::unique_future<int> fi; + fi=pi.get_future(); + + pi.set_value(42); + + int i=0; + BOOST_CHECK(i=fi.get()); + BOOST_CHECK(i==42); + BOOST_CHECK(fi.is_ready()); + BOOST_CHECK(fi.has_value()); + BOOST_CHECK(!fi.has_exception()); + BOOST_CHECK(fi.get_state()==boost::future_state::ready); +} + +void test_set_value_can_be_moved() +{ +// boost::promise<int> pi; +// boost::unique_future<int> fi; +// fi=pi.get_future(); + +// pi.set_value(42); + +// int i=0; +// BOOST_CHECK(i=fi.get()); +// BOOST_CHECK(i==42); +// BOOST_CHECK(fi.is_ready()); +// BOOST_CHECK(fi.has_value()); +// BOOST_CHECK(!fi.has_exception()); +// BOOST_CHECK(fi.get_state()==boost::future_state::ready); +} + +void test_future_from_packaged_task_is_waiting() +{ + boost::packaged_task<int> pt(make_int); + boost::unique_future<int> fi=pt.get_future(); + int i=0; + BOOST_CHECK(!fi.is_ready()); + BOOST_CHECK(!fi.has_value()); + BOOST_CHECK(!fi.has_exception()); + BOOST_CHECK(fi.get_state()==boost::future_state::waiting); + BOOST_CHECK(i==0); +} + +void test_invoking_a_packaged_task_populates_future() +{ + boost::packaged_task<int> pt(make_int); + boost::unique_future<int> fi=pt.get_future(); + + pt(); + + int i=0; + BOOST_CHECK(fi.is_ready()); + BOOST_CHECK(fi.has_value()); + BOOST_CHECK(!fi.has_exception()); + BOOST_CHECK(fi.get_state()==boost::future_state::ready); + BOOST_CHECK(i=fi.get()); + BOOST_CHECK(i==42); +} + +void test_invoking_a_packaged_task_twice_throws() +{ + boost::packaged_task<int> pt(make_int); + + pt(); + try + { + pt(); + BOOST_CHECK(false); + } + catch(boost::task_already_started) + { + BOOST_CHECK(true); + } +} + + +void test_cannot_get_future_twice_from_task() +{ + boost::packaged_task<int> pt(make_int); + pt.get_future(); + try + { + pt.get_future(); + BOOST_CHECK(false); + } + catch(boost::future_already_retrieved) + { + BOOST_CHECK(true); + } +} + +void test_task_stores_exception_if_function_throws() +{ + boost::packaged_task<int> pt(throw_runtime_error); + boost::unique_future<int> fi=pt.get_future(); + + pt(); + + BOOST_CHECK(fi.is_ready()); + BOOST_CHECK(!fi.has_value()); + BOOST_CHECK(fi.has_exception()); + BOOST_CHECK(fi.get_state()==boost::future_state::ready); + try + { + fi.get(); + BOOST_CHECK(false); + } + catch(std::exception&) + { + BOOST_CHECK(true); + } + catch(...) + { + BOOST_CHECK(!"Unknown exception thrown"); + } + +} + +void test_void_promise() +{ + boost::promise<void> p; + boost::unique_future<void> f=p.get_future(); + p.set_value(); + BOOST_CHECK(f.is_ready()); + BOOST_CHECK(f.has_value()); + BOOST_CHECK(!f.has_exception()); + BOOST_CHECK(f.get_state()==boost::future_state::ready); + f.get(); +} + +void test_reference_promise() +{ + boost::promise<int&> p; + boost::unique_future<int&> f=p.get_future(); + int i=42; + p.set_value(i); + BOOST_CHECK(f.is_ready()); + BOOST_CHECK(f.has_value()); + BOOST_CHECK(!f.has_exception()); + BOOST_CHECK(f.get_state()==boost::future_state::ready); + BOOST_CHECK(&f.get()==&i); +} + +void do_nothing() +{} + +void test_task_returning_void() +{ + boost::packaged_task<void> pt(do_nothing); + boost::unique_future<void> fi=pt.get_future(); + + pt(); + + BOOST_CHECK(fi.is_ready()); + BOOST_CHECK(fi.has_value()); + BOOST_CHECK(!fi.has_exception()); + BOOST_CHECK(fi.get_state()==boost::future_state::ready); +} + +int global_ref_target=0; + +int& return_ref() +{ + return global_ref_target; +} + +void test_task_returning_reference() +{ + boost::packaged_task<int&> pt(return_ref); + boost::unique_future<int&> fi=pt.get_future(); + + pt(); + + BOOST_CHECK(fi.is_ready()); + BOOST_CHECK(fi.has_value()); + BOOST_CHECK(!fi.has_exception()); + BOOST_CHECK(fi.get_state()==boost::future_state::ready); + int& i=fi.get(); + BOOST_CHECK(&i==&global_ref_target); +} + +void test_shared_future() +{ + boost::packaged_task<int> pt(make_int); + boost::unique_future<int> fi=pt.get_future(); + + boost::shared_future<int> sf(::cast_to_rval(fi)); + BOOST_CHECK(fi.get_state()==boost::future_state::uninitialized); + + pt(); + + int i=0; + BOOST_CHECK(sf.is_ready()); + BOOST_CHECK(sf.has_value()); + BOOST_CHECK(!sf.has_exception()); + BOOST_CHECK(sf.get_state()==boost::future_state::ready); + BOOST_CHECK(i=sf.get()); + BOOST_CHECK(i==42); +} + +void test_copies_of_shared_future_become_ready_together() +{ + boost::packaged_task<int> pt(make_int); + boost::unique_future<int> fi=pt.get_future(); + + boost::shared_future<int> sf(::cast_to_rval(fi)); + boost::shared_future<int> sf2(sf); + boost::shared_future<int> sf3; + sf3=sf; + BOOST_CHECK(sf.get_state()==boost::future_state::waiting); + BOOST_CHECK(sf2.get_state()==boost::future_state::waiting); + BOOST_CHECK(sf3.get_state()==boost::future_state::waiting); + + pt(); + + int i=0; + BOOST_CHECK(sf.is_ready()); + BOOST_CHECK(sf.has_value()); + BOOST_CHECK(!sf.has_exception()); + BOOST_CHECK(sf.get_state()==boost::future_state::ready); + BOOST_CHECK(i=sf.get()); + BOOST_CHECK(i==42); + i=0; + BOOST_CHECK(sf2.is_ready()); + BOOST_CHECK(sf2.has_value()); + BOOST_CHECK(!sf2.has_exception()); + BOOST_CHECK(sf2.get_state()==boost::future_state::ready); + BOOST_CHECK(i=sf2.get()); + BOOST_CHECK(i==42); + i=0; + BOOST_CHECK(sf3.is_ready()); + BOOST_CHECK(sf3.has_value()); + BOOST_CHECK(!sf3.has_exception()); + BOOST_CHECK(sf3.get_state()==boost::future_state::ready); + BOOST_CHECK(i=sf3.get()); + BOOST_CHECK(i==42); +} + +void test_shared_future_can_be_move_assigned_from_unique_future() +{ + boost::packaged_task<int> pt(make_int); + boost::unique_future<int> fi=pt.get_future(); + + boost::shared_future<int> sf; + sf=::cast_to_rval(fi); + BOOST_CHECK(fi.get_state()==boost::future_state::uninitialized); + + BOOST_CHECK(!sf.is_ready()); + BOOST_CHECK(!sf.has_value()); + BOOST_CHECK(!sf.has_exception()); + BOOST_CHECK(sf.get_state()==boost::future_state::waiting); +} + +void test_shared_future_void() +{ + boost::packaged_task<void> pt(do_nothing); + boost::unique_future<void> fi=pt.get_future(); + + boost::shared_future<void> sf(::cast_to_rval(fi)); + BOOST_CHECK(fi.get_state()==boost::future_state::uninitialized); + + pt(); + + BOOST_CHECK(sf.is_ready()); + BOOST_CHECK(sf.has_value()); + BOOST_CHECK(!sf.has_exception()); + BOOST_CHECK(sf.get_state()==boost::future_state::ready); + sf.get(); +} + +void test_shared_future_ref() +{ + boost::promise<int&> p; + boost::shared_future<int&> f(p.get_future()); + int i=42; + p.set_value(i); + BOOST_CHECK(f.is_ready()); + BOOST_CHECK(f.has_value()); + BOOST_CHECK(!f.has_exception()); + BOOST_CHECK(f.get_state()==boost::future_state::ready); + BOOST_CHECK(&f.get()==&i); +} + +void test_can_get_a_second_future_from_a_moved_promise() +{ + boost::promise<int> pi; + boost::unique_future<int> fi=pi.get_future(); + + boost::promise<int> pi2(::cast_to_rval(pi)); + boost::unique_future<int> fi2=pi.get_future(); + + pi2.set_value(3); + BOOST_CHECK(fi.is_ready()); + BOOST_CHECK(!fi2.is_ready()); + BOOST_CHECK(fi.get()==3); + pi.set_value(42); + BOOST_CHECK(fi2.is_ready()); + BOOST_CHECK(fi2.get()==42); +} + +void test_can_get_a_second_future_from_a_moved_void_promise() +{ + boost::promise<void> pi; + boost::unique_future<void> fi=pi.get_future(); + + boost::promise<void> pi2(::cast_to_rval(pi)); + boost::unique_future<void> fi2=pi.get_future(); + + pi2.set_value(); + BOOST_CHECK(fi.is_ready()); + BOOST_CHECK(!fi2.is_ready()); + pi.set_value(); + BOOST_CHECK(fi2.is_ready()); +} + +void test_unique_future_for_move_only_udt() +{ + boost::promise<X> pt; + boost::unique_future<X> fi=pt.get_future(); + + pt.set_value(X()); + X res(fi.get()); + BOOST_CHECK(res.i==42); +} + +void test_unique_future_for_string() +{ + boost::promise<std::string> pt; + boost::unique_future<std::string> fi=pt.get_future(); + + pt.set_value(std::string("hello")); + std::string res(fi.get()); + BOOST_CHECK(res=="hello"); + + boost::promise<std::string> pt2; + fi=pt2.get_future(); + + std::string const s="goodbye"; + + pt2.set_value(s); + res=fi.get(); + BOOST_CHECK(res=="goodbye"); + + boost::promise<std::string> pt3; + fi=pt3.get_future(); + + std::string s2="foo"; + + pt3.set_value(s2); + res=fi.get(); + BOOST_CHECK(res=="foo"); +} + +boost::mutex callback_mutex; +unsigned callback_called=0; + +void wait_callback(boost::promise<int>& pi) +{ + boost::lock_guard<boost::mutex> lk(callback_mutex); + ++callback_called; + try + { + pi.set_value(42); + } + catch(...) + { + } +} + +void do_nothing_callback(boost::promise<int>& /*pi*/) +{ + boost::lock_guard<boost::mutex> lk(callback_mutex); + ++callback_called; +} + +void test_wait_callback() +{ + callback_called=0; + boost::promise<int> pi; + boost::unique_future<int> fi=pi.get_future(); + pi.set_wait_callback(wait_callback); + fi.wait(); + BOOST_CHECK(callback_called); + BOOST_CHECK(fi.get()==42); + fi.wait(); + fi.wait(); + BOOST_CHECK(callback_called==1); +} + +void test_wait_callback_with_timed_wait() +{ + callback_called=0; + boost::promise<int> pi; + boost::unique_future<int> fi=pi.get_future(); + pi.set_wait_callback(do_nothing_callback); + bool success=fi.timed_wait(boost::posix_time::milliseconds(10)); + BOOST_CHECK(callback_called); + BOOST_CHECK(!success); + success=fi.timed_wait(boost::posix_time::milliseconds(10)); + BOOST_CHECK(!success); + success=fi.timed_wait(boost::posix_time::milliseconds(10)); + BOOST_CHECK(!success); + BOOST_CHECK(callback_called==3); + pi.set_value(42); + success=fi.timed_wait(boost::posix_time::milliseconds(10)); + BOOST_CHECK(success); + BOOST_CHECK(callback_called==3); + BOOST_CHECK(fi.get()==42); + BOOST_CHECK(callback_called==3); +} + + +void wait_callback_for_task(boost::packaged_task<int>& pt) +{ + boost::lock_guard<boost::mutex> lk(callback_mutex); + ++callback_called; + try + { + pt(); + } + catch(...) + { + } +} + + +void test_wait_callback_for_packaged_task() +{ + callback_called=0; + boost::packaged_task<int> pt(make_int); + boost::unique_future<int> fi=pt.get_future(); + pt.set_wait_callback(wait_callback_for_task); + fi.wait(); + BOOST_CHECK(callback_called); + BOOST_CHECK(fi.get()==42); + fi.wait(); + fi.wait(); + BOOST_CHECK(callback_called==1); +} + +void test_packaged_task_can_be_moved() +{ + boost::packaged_task<int> pt(make_int); + + boost::unique_future<int> fi=pt.get_future(); + + BOOST_CHECK(!fi.is_ready()); + + boost::packaged_task<int> pt2(::cast_to_rval(pt)); + + BOOST_CHECK(!fi.is_ready()); + try + { + pt(); + BOOST_CHECK(!"Can invoke moved task!"); + } + catch(boost::task_moved&) + { + } + + BOOST_CHECK(!fi.is_ready()); + + pt2(); + + BOOST_CHECK(fi.is_ready()); +} + +void test_destroying_a_promise_stores_broken_promise() +{ + boost::unique_future<int> f; + + { + boost::promise<int> p; + f=p.get_future(); + } + BOOST_CHECK(f.is_ready()); + BOOST_CHECK(f.has_exception()); + try + { + f.get(); + } + catch(boost::broken_promise&) + { + } +} + +void test_destroying_a_packaged_task_stores_broken_promise() +{ + boost::unique_future<int> f; + + { + boost::packaged_task<int> p(make_int); + f=p.get_future(); + } + BOOST_CHECK(f.is_ready()); + BOOST_CHECK(f.has_exception()); + try + { + f.get(); + } + catch(boost::broken_promise&) + { + } +} + +int make_int_slowly() +{ + boost::this_thread::sleep(boost::posix_time::seconds(1)); + return 42; +} + +void test_wait_for_either_of_two_futures_1() +{ + boost::packaged_task<int> pt(make_int_slowly); + boost::unique_future<int> f1(pt.get_future()); + boost::packaged_task<int> pt2(make_int_slowly); + boost::unique_future<int> f2(pt2.get_future()); + + boost::thread(::cast_to_rval(pt)); + + unsigned const future=boost::wait_for_any(f1,f2); + + BOOST_CHECK(future==0); + BOOST_CHECK(f1.is_ready()); + BOOST_CHECK(!f2.is_ready()); + BOOST_CHECK(f1.get()==42); +} + +void test_wait_for_either_of_two_futures_2() +{ + boost::packaged_task<int> pt(make_int_slowly); + boost::unique_future<int> f1(pt.get_future()); + boost::packaged_task<int> pt2(make_int_slowly); + boost::unique_future<int> f2(pt2.get_future()); + + boost::thread(::cast_to_rval(pt2)); + + unsigned const future=boost::wait_for_any(f1,f2); + + BOOST_CHECK(future==1); + BOOST_CHECK(!f1.is_ready()); + BOOST_CHECK(f2.is_ready()); + BOOST_CHECK(f2.get()==42); +} + +void test_wait_for_either_of_three_futures_1() +{ + boost::packaged_task<int> pt(make_int_slowly); + boost::unique_future<int> f1(pt.get_future()); + boost::packaged_task<int> pt2(make_int_slowly); + boost::unique_future<int> f2(pt2.get_future()); + boost::packaged_task<int> pt3(make_int_slowly); + boost::unique_future<int> f3(pt3.get_future()); + + boost::thread(::cast_to_rval(pt)); + + unsigned const future=boost::wait_for_any(f1,f2,f3); + + BOOST_CHECK(future==0); + BOOST_CHECK(f1.is_ready()); + BOOST_CHECK(!f2.is_ready()); + BOOST_CHECK(!f3.is_ready()); + BOOST_CHECK(f1.get()==42); +} + +void test_wait_for_either_of_three_futures_2() +{ + boost::packaged_task<int> pt(make_int_slowly); + boost::unique_future<int> f1(pt.get_future()); + boost::packaged_task<int> pt2(make_int_slowly); + boost::unique_future<int> f2(pt2.get_future()); + boost::packaged_task<int> pt3(make_int_slowly); + boost::unique_future<int> f3(pt3.get_future()); + + boost::thread(::cast_to_rval(pt2)); + + unsigned const future=boost::wait_for_any(f1,f2,f3); + + BOOST_CHECK(future==1); + BOOST_CHECK(!f1.is_ready()); + BOOST_CHECK(f2.is_ready()); + BOOST_CHECK(!f3.is_ready()); + BOOST_CHECK(f2.get()==42); +} + +void test_wait_for_either_of_three_futures_3() +{ + boost::packaged_task<int> pt(make_int_slowly); + boost::unique_future<int> f1(pt.get_future()); + boost::packaged_task<int> pt2(make_int_slowly); + boost::unique_future<int> f2(pt2.get_future()); + boost::packaged_task<int> pt3(make_int_slowly); + boost::unique_future<int> f3(pt3.get_future()); + + boost::thread(::cast_to_rval(pt3)); + + unsigned const future=boost::wait_for_any(f1,f2,f3); + + BOOST_CHECK(future==2); + BOOST_CHECK(!f1.is_ready()); + BOOST_CHECK(!f2.is_ready()); + BOOST_CHECK(f3.is_ready()); + BOOST_CHECK(f3.get()==42); +} + +void test_wait_for_either_of_four_futures_1() +{ + boost::packaged_task<int> pt(make_int_slowly); + boost::unique_future<int> f1(pt.get_future()); + boost::packaged_task<int> pt2(make_int_slowly); + boost::unique_future<int> f2(pt2.get_future()); + boost::packaged_task<int> pt3(make_int_slowly); + boost::unique_future<int> f3(pt3.get_future()); + boost::packaged_task<int> pt4(make_int_slowly); + boost::unique_future<int> f4(pt4.get_future()); + + boost::thread(::cast_to_rval(pt)); + + unsigned const future=boost::wait_for_any(f1,f2,f3,f4); + + BOOST_CHECK(future==0); + BOOST_CHECK(f1.is_ready()); + BOOST_CHECK(!f2.is_ready()); + BOOST_CHECK(!f3.is_ready()); + BOOST_CHECK(!f4.is_ready()); + BOOST_CHECK(f1.get()==42); +} + +void test_wait_for_either_of_four_futures_2() +{ + boost::packaged_task<int> pt(make_int_slowly); + boost::unique_future<int> f1(pt.get_future()); + boost::packaged_task<int> pt2(make_int_slowly); + boost::unique_future<int> f2(pt2.get_future()); + boost::packaged_task<int> pt3(make_int_slowly); + boost::unique_future<int> f3(pt3.get_future()); + boost::packaged_task<int> pt4(make_int_slowly); + boost::unique_future<int> f4(pt4.get_future()); + + boost::thread(::cast_to_rval(pt2)); + + unsigned const future=boost::wait_for_any(f1,f2,f3,f4); + + BOOST_CHECK(future==1); + BOOST_CHECK(!f1.is_ready()); + BOOST_CHECK(f2.is_ready()); + BOOST_CHECK(!f3.is_ready()); + BOOST_CHECK(!f4.is_ready()); + BOOST_CHECK(f2.get()==42); +} + +void test_wait_for_either_of_four_futures_3() +{ + boost::packaged_task<int> pt(make_int_slowly); + boost::unique_future<int> f1(pt.get_future()); + boost::packaged_task<int> pt2(make_int_slowly); + boost::unique_future<int> f2(pt2.get_future()); + boost::packaged_task<int> pt3(make_int_slowly); + boost::unique_future<int> f3(pt3.get_future()); + boost::packaged_task<int> pt4(make_int_slowly); + boost::unique_future<int> f4(pt4.get_future()); + + boost::thread(::cast_to_rval(pt3)); + + unsigned const future=boost::wait_for_any(f1,f2,f3,f4); + + BOOST_CHECK(future==2); + BOOST_CHECK(!f1.is_ready()); + BOOST_CHECK(!f2.is_ready()); + BOOST_CHECK(f3.is_ready()); + BOOST_CHECK(!f4.is_ready()); + BOOST_CHECK(f3.get()==42); +} + +void test_wait_for_either_of_four_futures_4() +{ + boost::packaged_task<int> pt(make_int_slowly); + boost::unique_future<int> f1(pt.get_future()); + boost::packaged_task<int> pt2(make_int_slowly); + boost::unique_future<int> f2(pt2.get_future()); + boost::packaged_task<int> pt3(make_int_slowly); + boost::unique_future<int> f3(pt3.get_future()); + boost::packaged_task<int> pt4(make_int_slowly); + boost::unique_future<int> f4(pt4.get_future()); + + boost::thread(::cast_to_rval(pt4)); + + unsigned const future=boost::wait_for_any(f1,f2,f3,f4); + + BOOST_CHECK(future==3); + BOOST_CHECK(!f1.is_ready()); + BOOST_CHECK(!f2.is_ready()); + BOOST_CHECK(!f3.is_ready()); + BOOST_CHECK(f4.is_ready()); + BOOST_CHECK(f4.get()==42); +} + +void test_wait_for_either_of_five_futures_1() +{ + boost::packaged_task<int> pt(make_int_slowly); + boost::unique_future<int> f1(pt.get_future()); + boost::packaged_task<int> pt2(make_int_slowly); + boost::unique_future<int> f2(pt2.get_future()); + boost::packaged_task<int> pt3(make_int_slowly); + boost::unique_future<int> f3(pt3.get_future()); + boost::packaged_task<int> pt4(make_int_slowly); + boost::unique_future<int> f4(pt4.get_future()); + boost::packaged_task<int> pt5(make_int_slowly); + boost::unique_future<int> f5(pt5.get_future()); + + boost::thread(::cast_to_rval(pt)); + + unsigned const future=boost::wait_for_any(f1,f2,f3,f4,f5); + + BOOST_CHECK(future==0); + BOOST_CHECK(f1.is_ready()); + BOOST_CHECK(!f2.is_ready()); + BOOST_CHECK(!f3.is_ready()); + BOOST_CHECK(!f4.is_ready()); + BOOST_CHECK(!f5.is_ready()); + BOOST_CHECK(f1.get()==42); +} + +void test_wait_for_either_of_five_futures_2() +{ + boost::packaged_task<int> pt(make_int_slowly); + boost::unique_future<int> f1(pt.get_future()); + boost::packaged_task<int> pt2(make_int_slowly); + boost::unique_future<int> f2(pt2.get_future()); + boost::packaged_task<int> pt3(make_int_slowly); + boost::unique_future<int> f3(pt3.get_future()); + boost::packaged_task<int> pt4(make_int_slowly); + boost::unique_future<int> f4(pt4.get_future()); + boost::packaged_task<int> pt5(make_int_slowly); + boost::unique_future<int> f5(pt5.get_future()); + + boost::thread(::cast_to_rval(pt2)); + + unsigned const future=boost::wait_for_any(f1,f2,f3,f4,f5); + + BOOST_CHECK(future==1); + BOOST_CHECK(!f1.is_ready()); + BOOST_CHECK(f2.is_ready()); + BOOST_CHECK(!f3.is_ready()); + BOOST_CHECK(!f4.is_ready()); + BOOST_CHECK(!f5.is_ready()); + BOOST_CHECK(f2.get()==42); +} +void test_wait_for_either_of_five_futures_3() +{ + boost::packaged_task<int> pt(make_int_slowly); + boost::unique_future<int> f1(pt.get_future()); + boost::packaged_task<int> pt2(make_int_slowly); + boost::unique_future<int> f2(pt2.get_future()); + boost::packaged_task<int> pt3(make_int_slowly); + boost::unique_future<int> f3(pt3.get_future()); + boost::packaged_task<int> pt4(make_int_slowly); + boost::unique_future<int> f4(pt4.get_future()); + boost::packaged_task<int> pt5(make_int_slowly); + boost::unique_future<int> f5(pt5.get_future()); + + boost::thread(::cast_to_rval(pt3)); + + unsigned const future=boost::wait_for_any(f1,f2,f3,f4,f5); + + BOOST_CHECK(future==2); + BOOST_CHECK(!f1.is_ready()); + BOOST_CHECK(!f2.is_ready()); + BOOST_CHECK(f3.is_ready()); + BOOST_CHECK(!f4.is_ready()); + BOOST_CHECK(!f5.is_ready()); + BOOST_CHECK(f3.get()==42); +} +void test_wait_for_either_of_five_futures_4() +{ + boost::packaged_task<int> pt(make_int_slowly); + boost::unique_future<int> f1(pt.get_future()); + boost::packaged_task<int> pt2(make_int_slowly); + boost::unique_future<int> f2(pt2.get_future()); + boost::packaged_task<int> pt3(make_int_slowly); + boost::unique_future<int> f3(pt3.get_future()); + boost::packaged_task<int> pt4(make_int_slowly); + boost::unique_future<int> f4(pt4.get_future()); + boost::packaged_task<int> pt5(make_int_slowly); + boost::unique_future<int> f5(pt5.get_future()); + + boost::thread(::cast_to_rval(pt4)); + + unsigned const future=boost::wait_for_any(f1,f2,f3,f4,f5); + + BOOST_CHECK(future==3); + BOOST_CHECK(!f1.is_ready()); + BOOST_CHECK(!f2.is_ready()); + BOOST_CHECK(!f3.is_ready()); + BOOST_CHECK(f4.is_ready()); + BOOST_CHECK(!f5.is_ready()); + BOOST_CHECK(f4.get()==42); +} +void test_wait_for_either_of_five_futures_5() +{ + boost::packaged_task<int> pt(make_int_slowly); + boost::unique_future<int> f1(pt.get_future()); + boost::packaged_task<int> pt2(make_int_slowly); + boost::unique_future<int> f2(pt2.get_future()); + boost::packaged_task<int> pt3(make_int_slowly); + boost::unique_future<int> f3(pt3.get_future()); + boost::packaged_task<int> pt4(make_int_slowly); + boost::unique_future<int> f4(pt4.get_future()); + boost::packaged_task<int> pt5(make_int_slowly); + boost::unique_future<int> f5(pt5.get_future()); + + boost::thread(::cast_to_rval(pt5)); + + unsigned const future=boost::wait_for_any(f1,f2,f3,f4,f5); + + BOOST_CHECK(future==4); + BOOST_CHECK(!f1.is_ready()); + BOOST_CHECK(!f2.is_ready()); + BOOST_CHECK(!f3.is_ready()); + BOOST_CHECK(!f4.is_ready()); + BOOST_CHECK(f5.is_ready()); + BOOST_CHECK(f5.get()==42); +} + +void test_wait_for_either_invokes_callbacks() +{ + callback_called=0; + boost::packaged_task<int> pt(make_int_slowly); + boost::unique_future<int> fi=pt.get_future(); + boost::packaged_task<int> pt2(make_int_slowly); + boost::unique_future<int> fi2=pt2.get_future(); + pt.set_wait_callback(wait_callback_for_task); + + boost::thread(::cast_to_rval(pt)); + + boost::wait_for_any(fi,fi2); + BOOST_CHECK(callback_called==1); + BOOST_CHECK(fi.get()==42); +} + +void test_wait_for_any_from_range() +{ + unsigned const count=10; + for(unsigned i=0;i<count;++i) + { + boost::packaged_task<int> tasks[count]; + boost::unique_future<int> futures[count]; + for(unsigned j=0;j<count;++j) + { + tasks[j]=boost::packaged_task<int>(make_int_slowly); + futures[j]=tasks[j].get_future(); + } + boost::thread(::cast_to_rval(tasks[i])); + + BOOST_CHECK(boost::wait_for_any(futures,futures)==futures); + + boost::unique_future<int>* const future=boost::wait_for_any(futures,futures+count); + + BOOST_CHECK(future==(futures+i)); + for(unsigned j=0;j<count;++j) + { + if(j!=i) + { + BOOST_CHECK(!futures[j].is_ready()); + } + else + { + BOOST_CHECK(futures[j].is_ready()); + } + } + BOOST_CHECK(futures[i].get()==42); + } +} + +void test_wait_for_all_from_range() +{ + unsigned const count=10; + boost::unique_future<int> futures[count]; + for(unsigned j=0;j<count;++j) + { + boost::packaged_task<int> task(make_int_slowly); + futures[j]=task.get_future(); + boost::thread(::cast_to_rval(task)); + } + + boost::wait_for_all(futures,futures+count); + + for(unsigned j=0;j<count;++j) + { + BOOST_CHECK(futures[j].is_ready()); + } +} + +void test_wait_for_all_two_futures() +{ + unsigned const count=2; + boost::unique_future<int> futures[count]; + for(unsigned j=0;j<count;++j) + { + boost::packaged_task<int> task(make_int_slowly); + futures[j]=task.get_future(); + boost::thread(::cast_to_rval(task)); + } + + boost::wait_for_all(futures[0],futures[1]); + + for(unsigned j=0;j<count;++j) + { + BOOST_CHECK(futures[j].is_ready()); + } +} + +void test_wait_for_all_three_futures() +{ + unsigned const count=3; + boost::unique_future<int> futures[count]; + for(unsigned j=0;j<count;++j) + { + boost::packaged_task<int> task(make_int_slowly); + futures[j]=task.get_future(); + boost::thread(::cast_to_rval(task)); + } + + boost::wait_for_all(futures[0],futures[1],futures[2]); + + for(unsigned j=0;j<count;++j) + { + BOOST_CHECK(futures[j].is_ready()); + } +} + +void test_wait_for_all_four_futures() +{ + unsigned const count=4; + boost::unique_future<int> futures[count]; + for(unsigned j=0;j<count;++j) + { + boost::packaged_task<int> task(make_int_slowly); + futures[j]=task.get_future(); + boost::thread(::cast_to_rval(task)); + } + + boost::wait_for_all(futures[0],futures[1],futures[2],futures[3]); + + for(unsigned j=0;j<count;++j) + { + BOOST_CHECK(futures[j].is_ready()); + } +} + +void test_wait_for_all_five_futures() +{ + unsigned const count=5; + boost::unique_future<int> futures[count]; + for(unsigned j=0;j<count;++j) + { + boost::packaged_task<int> task(make_int_slowly); + futures[j]=task.get_future(); + boost::thread(::cast_to_rval(task)); + } + + boost::wait_for_all(futures[0],futures[1],futures[2],futures[3],futures[4]); + + for(unsigned j=0;j<count;++j) + { + BOOST_CHECK(futures[j].is_ready()); + } +} + + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: futures test suite"); + + test->add(BOOST_TEST_CASE(test_initial_state)); + test->add(BOOST_TEST_CASE(test_waiting_future)); + test->add(BOOST_TEST_CASE(test_cannot_get_future_twice)); + test->add(BOOST_TEST_CASE(test_set_value_updates_future_state)); + test->add(BOOST_TEST_CASE(test_set_value_can_be_retrieved)); + test->add(BOOST_TEST_CASE(test_set_value_can_be_moved)); + test->add(BOOST_TEST_CASE(test_store_value_from_thread)); + test->add(BOOST_TEST_CASE(test_store_exception)); + test->add(BOOST_TEST_CASE(test_future_from_packaged_task_is_waiting)); + test->add(BOOST_TEST_CASE(test_invoking_a_packaged_task_populates_future)); + test->add(BOOST_TEST_CASE(test_invoking_a_packaged_task_twice_throws)); + test->add(BOOST_TEST_CASE(test_cannot_get_future_twice_from_task)); + test->add(BOOST_TEST_CASE(test_task_stores_exception_if_function_throws)); + test->add(BOOST_TEST_CASE(test_void_promise)); + test->add(BOOST_TEST_CASE(test_reference_promise)); + test->add(BOOST_TEST_CASE(test_task_returning_void)); + test->add(BOOST_TEST_CASE(test_task_returning_reference)); + test->add(BOOST_TEST_CASE(test_shared_future)); + test->add(BOOST_TEST_CASE(test_copies_of_shared_future_become_ready_together)); + test->add(BOOST_TEST_CASE(test_shared_future_can_be_move_assigned_from_unique_future)); + test->add(BOOST_TEST_CASE(test_shared_future_void)); + test->add(BOOST_TEST_CASE(test_shared_future_ref)); + test->add(BOOST_TEST_CASE(test_can_get_a_second_future_from_a_moved_promise)); + test->add(BOOST_TEST_CASE(test_can_get_a_second_future_from_a_moved_void_promise)); + test->add(BOOST_TEST_CASE(test_unique_future_for_move_only_udt)); + test->add(BOOST_TEST_CASE(test_unique_future_for_string)); + test->add(BOOST_TEST_CASE(test_wait_callback)); + test->add(BOOST_TEST_CASE(test_wait_callback_with_timed_wait)); + test->add(BOOST_TEST_CASE(test_wait_callback_for_packaged_task)); + test->add(BOOST_TEST_CASE(test_packaged_task_can_be_moved)); + test->add(BOOST_TEST_CASE(test_destroying_a_promise_stores_broken_promise)); + test->add(BOOST_TEST_CASE(test_destroying_a_packaged_task_stores_broken_promise)); + test->add(BOOST_TEST_CASE(test_wait_for_either_of_two_futures_1)); + test->add(BOOST_TEST_CASE(test_wait_for_either_of_two_futures_2)); + test->add(BOOST_TEST_CASE(test_wait_for_either_of_three_futures_1)); + test->add(BOOST_TEST_CASE(test_wait_for_either_of_three_futures_2)); + test->add(BOOST_TEST_CASE(test_wait_for_either_of_three_futures_3)); + test->add(BOOST_TEST_CASE(test_wait_for_either_of_four_futures_1)); + test->add(BOOST_TEST_CASE(test_wait_for_either_of_four_futures_2)); + test->add(BOOST_TEST_CASE(test_wait_for_either_of_four_futures_3)); + test->add(BOOST_TEST_CASE(test_wait_for_either_of_four_futures_4)); + test->add(BOOST_TEST_CASE(test_wait_for_either_of_five_futures_1)); + test->add(BOOST_TEST_CASE(test_wait_for_either_of_five_futures_2)); + test->add(BOOST_TEST_CASE(test_wait_for_either_of_five_futures_3)); + test->add(BOOST_TEST_CASE(test_wait_for_either_of_five_futures_4)); + test->add(BOOST_TEST_CASE(test_wait_for_either_of_five_futures_5)); + test->add(BOOST_TEST_CASE(test_wait_for_either_invokes_callbacks)); + test->add(BOOST_TEST_CASE(test_wait_for_any_from_range)); + test->add(BOOST_TEST_CASE(test_wait_for_all_from_range)); + test->add(BOOST_TEST_CASE(test_wait_for_all_two_futures)); + test->add(BOOST_TEST_CASE(test_wait_for_all_three_futures)); + test->add(BOOST_TEST_CASE(test_wait_for_all_four_futures)); + test->add(BOOST_TEST_CASE(test_wait_for_all_five_futures)); + + return test; +} diff --git a/libs/thread/test/test_generic_locks.cpp b/libs/thread/test/test_generic_locks.cpp new file mode 100644 index 0000000000..8e66fad459 --- /dev/null +++ b/libs/thread/test/test_generic_locks.cpp @@ -0,0 +1,594 @@ +// (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) + +#include <boost/test/unit_test.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/locks.hpp> +#include <boost/thread/condition_variable.hpp> + +void test_lock_two_uncontended() +{ + boost::mutex m1,m2; + + boost::mutex::scoped_lock l1(m1,boost::defer_lock), + l2(m2,boost::defer_lock); + + BOOST_CHECK(!l1.owns_lock()); + BOOST_CHECK(!l2.owns_lock()); + + boost::lock(l1,l2); + + BOOST_CHECK(l1.owns_lock()); + BOOST_CHECK(l2.owns_lock()); +} + +struct wait_data +{ + boost::mutex m; + bool flag; + boost::condition_variable cond; + + wait_data(): + flag(false) + {} + + void wait() + { + boost::mutex::scoped_lock l(m); + while(!flag) + { + cond.wait(l); + } + } + + template<typename Duration> + bool timed_wait(Duration d) + { + boost::system_time const target=boost::get_system_time()+d; + + boost::mutex::scoped_lock l(m); + while(!flag) + { + if(!cond.timed_wait(l,target)) + { + return flag; + } + } + return true; + } + + void signal() + { + boost::mutex::scoped_lock l(m); + flag=true; + cond.notify_all(); + } +}; + + +void lock_mutexes_slowly(boost::mutex* m1,boost::mutex* m2,wait_data* locked,wait_data* quit) +{ + boost::lock_guard<boost::mutex> l1(*m1); + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + boost::lock_guard<boost::mutex> l2(*m2); + locked->signal(); + quit->wait(); +} + +void lock_pair(boost::mutex* m1,boost::mutex* m2) +{ + boost::lock(*m1,*m2); + boost::mutex::scoped_lock l1(*m1,boost::adopt_lock), + l2(*m2,boost::adopt_lock); +} + +void test_lock_two_other_thread_locks_in_order() +{ + boost::mutex m1,m2; + wait_data locked; + wait_data release; + + boost::thread t(lock_mutexes_slowly,&m1,&m2,&locked,&release); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + + boost::thread t2(lock_pair,&m1,&m2); + BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(1))); + + release.signal(); + + BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(1))); + + t.join(); +} + +void test_lock_two_other_thread_locks_in_opposite_order() +{ + boost::mutex m1,m2; + wait_data locked; + wait_data release; + + boost::thread t(lock_mutexes_slowly,&m1,&m2,&locked,&release); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + + boost::thread t2(lock_pair,&m2,&m1); + BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(1))); + + release.signal(); + + BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(1))); + + t.join(); +} + +void test_lock_five_uncontended() +{ + boost::mutex m1,m2,m3,m4,m5; + + boost::mutex::scoped_lock l1(m1,boost::defer_lock), + l2(m2,boost::defer_lock), + l3(m3,boost::defer_lock), + l4(m4,boost::defer_lock), + l5(m5,boost::defer_lock); + + BOOST_CHECK(!l1.owns_lock()); + BOOST_CHECK(!l2.owns_lock()); + BOOST_CHECK(!l3.owns_lock()); + BOOST_CHECK(!l4.owns_lock()); + BOOST_CHECK(!l5.owns_lock()); + + boost::lock(l1,l2,l3,l4,l5); + + BOOST_CHECK(l1.owns_lock()); + BOOST_CHECK(l2.owns_lock()); + BOOST_CHECK(l3.owns_lock()); + BOOST_CHECK(l4.owns_lock()); + BOOST_CHECK(l5.owns_lock()); +} + +void lock_five_mutexes_slowly(boost::mutex* m1,boost::mutex* m2,boost::mutex* m3,boost::mutex* m4,boost::mutex* m5, + wait_data* locked,wait_data* quit) +{ + boost::lock_guard<boost::mutex> l1(*m1); + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + boost::lock_guard<boost::mutex> l2(*m2); + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + boost::lock_guard<boost::mutex> l3(*m3); + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + boost::lock_guard<boost::mutex> l4(*m4); + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + boost::lock_guard<boost::mutex> l5(*m5); + locked->signal(); + quit->wait(); +} + +void lock_five(boost::mutex* m1,boost::mutex* m2,boost::mutex* m3,boost::mutex* m4,boost::mutex* m5) +{ + boost::lock(*m1,*m2,*m3,*m4,*m5); + m1->unlock(); + m2->unlock(); + m3->unlock(); + m4->unlock(); + m5->unlock(); +} + +void test_lock_five_other_thread_locks_in_order() +{ + boost::mutex m1,m2,m3,m4,m5; + wait_data locked; + wait_data release; + + boost::thread t(lock_five_mutexes_slowly,&m1,&m2,&m3,&m4,&m5,&locked,&release); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + + boost::thread t2(lock_five,&m1,&m2,&m3,&m4,&m5); + BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(3))); + + release.signal(); + + BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(3))); + + t.join(); +} + +void test_lock_five_other_thread_locks_in_different_order() +{ + boost::mutex m1,m2,m3,m4,m5; + wait_data locked; + wait_data release; + + boost::thread t(lock_five_mutexes_slowly,&m1,&m2,&m3,&m4,&m5,&locked,&release); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + + boost::thread t2(lock_five,&m5,&m1,&m4,&m2,&m3); + BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(3))); + + release.signal(); + + BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(3))); + + t.join(); +} + +void lock_n(boost::mutex* mutexes,unsigned count) +{ + boost::lock(mutexes,mutexes+count); + for(unsigned i=0;i<count;++i) + { + mutexes[i].unlock(); + } +} + + +void test_lock_ten_other_thread_locks_in_different_order() +{ + unsigned const num_mutexes=10; + + boost::mutex mutexes[num_mutexes]; + wait_data locked; + wait_data release; + + boost::thread t(lock_five_mutexes_slowly,&mutexes[6],&mutexes[3],&mutexes[8],&mutexes[0],&mutexes[2],&locked,&release); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + + boost::thread t2(lock_n,mutexes,num_mutexes); + BOOST_CHECK(locked.timed_wait(boost::posix_time::seconds(3))); + + release.signal(); + + BOOST_CHECK(t2.timed_join(boost::posix_time::seconds(3))); + + t.join(); +} + +struct dummy_mutex +{ + bool is_locked; + + dummy_mutex(): + is_locked(false) + {} + + void lock() + { + is_locked=true; + } + + bool try_lock() + { + if(is_locked) + { + return false; + } + is_locked=true; + return true; + } + + void unlock() + { + is_locked=false; + } +}; + +namespace boost +{ + template<> + struct is_mutex_type<dummy_mutex> + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; +} + + + +void test_lock_five_in_range() +{ + unsigned const num_mutexes=5; + dummy_mutex mutexes[num_mutexes]; + + boost::lock(mutexes,mutexes+num_mutexes); + + for(unsigned i=0;i<num_mutexes;++i) + { + BOOST_CHECK(mutexes[i].is_locked); + } +} + +class dummy_iterator: + public std::iterator<std::forward_iterator_tag, + dummy_mutex> +{ +private: + dummy_mutex* p; +public: + explicit dummy_iterator(dummy_mutex* p_): + p(p_) + {} + + bool operator==(dummy_iterator const& other) const + { + return p==other.p; + } + + bool operator!=(dummy_iterator const& other) const + { + return p!=other.p; + } + + bool operator<(dummy_iterator const& other) const + { + return p<other.p; + } + + dummy_mutex& operator*() const + { + return *p; + } + + dummy_mutex* operator->() const + { + return p; + } + + dummy_iterator operator++(int) + { + dummy_iterator temp(*this); + ++p; + return temp; + } + + dummy_iterator& operator++() + { + ++p; + return *this; + } + +}; + + +void test_lock_five_in_range_custom_iterator() +{ + unsigned const num_mutexes=5; + dummy_mutex mutexes[num_mutexes]; + + boost::lock(dummy_iterator(mutexes),dummy_iterator(mutexes+num_mutexes)); + + for(unsigned i=0;i<num_mutexes;++i) + { + BOOST_CHECK(mutexes[i].is_locked); + } +} + +class dummy_mutex2: + public dummy_mutex +{}; + + +void test_lock_ten_in_range_inherited_mutex() +{ + unsigned const num_mutexes=10; + dummy_mutex2 mutexes[num_mutexes]; + + boost::lock(mutexes,mutexes+num_mutexes); + + for(unsigned i=0;i<num_mutexes;++i) + { + BOOST_CHECK(mutexes[i].is_locked); + } +} + +void test_try_lock_two_uncontended() +{ + dummy_mutex m1,m2; + + int const res=boost::try_lock(m1,m2); + + BOOST_CHECK(res==-1); + BOOST_CHECK(m1.is_locked); + BOOST_CHECK(m2.is_locked); +} +void test_try_lock_two_first_locked() +{ + dummy_mutex m1,m2; + m1.lock(); + + boost::unique_lock<dummy_mutex> l1(m1,boost::defer_lock), + l2(m2,boost::defer_lock); + + int const res=boost::try_lock(l1,l2); + + BOOST_CHECK(res==0); + BOOST_CHECK(m1.is_locked); + BOOST_CHECK(!m2.is_locked); + BOOST_CHECK(!l1.owns_lock()); + BOOST_CHECK(!l2.owns_lock()); +} +void test_try_lock_two_second_locked() +{ + dummy_mutex m1,m2; + m2.lock(); + + boost::unique_lock<dummy_mutex> l1(m1,boost::defer_lock), + l2(m2,boost::defer_lock); + + int const res=boost::try_lock(l1,l2); + + BOOST_CHECK(res==1); + BOOST_CHECK(!m1.is_locked); + BOOST_CHECK(m2.is_locked); + BOOST_CHECK(!l1.owns_lock()); + BOOST_CHECK(!l2.owns_lock()); +} + +void test_try_lock_three() +{ + int const num_mutexes=3; + + for(int i=-1;i<num_mutexes;++i) + { + dummy_mutex mutexes[num_mutexes]; + + if(i>=0) + { + mutexes[i].lock(); + } + boost::unique_lock<dummy_mutex> l1(mutexes[0],boost::defer_lock), + l2(mutexes[1],boost::defer_lock), + l3(mutexes[2],boost::defer_lock); + + int const res=boost::try_lock(l1,l2,l3); + + BOOST_CHECK(res==i); + for(int j=0;j<num_mutexes;++j) + { + if((i==j) || (i==-1)) + { + BOOST_CHECK(mutexes[j].is_locked); + } + else + { + BOOST_CHECK(!mutexes[j].is_locked); + } + } + if(i==-1) + { + BOOST_CHECK(l1.owns_lock()); + BOOST_CHECK(l2.owns_lock()); + BOOST_CHECK(l3.owns_lock()); + } + else + { + BOOST_CHECK(!l1.owns_lock()); + BOOST_CHECK(!l2.owns_lock()); + BOOST_CHECK(!l3.owns_lock()); + } + } +} + +void test_try_lock_four() +{ + int const num_mutexes=4; + + for(int i=-1;i<num_mutexes;++i) + { + dummy_mutex mutexes[num_mutexes]; + + if(i>=0) + { + mutexes[i].lock(); + } + boost::unique_lock<dummy_mutex> l1(mutexes[0],boost::defer_lock), + l2(mutexes[1],boost::defer_lock), + l3(mutexes[2],boost::defer_lock), + l4(mutexes[3],boost::defer_lock); + + int const res=boost::try_lock(l1,l2,l3,l4); + + BOOST_CHECK(res==i); + for(int j=0;j<num_mutexes;++j) + { + if((i==j) || (i==-1)) + { + BOOST_CHECK(mutexes[j].is_locked); + } + else + { + BOOST_CHECK(!mutexes[j].is_locked); + } + } + if(i==-1) + { + BOOST_CHECK(l1.owns_lock()); + BOOST_CHECK(l2.owns_lock()); + BOOST_CHECK(l3.owns_lock()); + BOOST_CHECK(l4.owns_lock()); + } + else + { + BOOST_CHECK(!l1.owns_lock()); + BOOST_CHECK(!l2.owns_lock()); + BOOST_CHECK(!l3.owns_lock()); + BOOST_CHECK(!l4.owns_lock()); + } + } +} + +void test_try_lock_five() +{ + int const num_mutexes=5; + + for(int i=-1;i<num_mutexes;++i) + { + dummy_mutex mutexes[num_mutexes]; + + if(i>=0) + { + mutexes[i].lock(); + } + boost::unique_lock<dummy_mutex> l1(mutexes[0],boost::defer_lock), + l2(mutexes[1],boost::defer_lock), + l3(mutexes[2],boost::defer_lock), + l4(mutexes[3],boost::defer_lock), + l5(mutexes[4],boost::defer_lock); + + int const res=boost::try_lock(l1,l2,l3,l4,l5); + + BOOST_CHECK(res==i); + for(int j=0;j<num_mutexes;++j) + { + if((i==j) || (i==-1)) + { + BOOST_CHECK(mutexes[j].is_locked); + } + else + { + BOOST_CHECK(!mutexes[j].is_locked); + } + } + if(i==-1) + { + BOOST_CHECK(l1.owns_lock()); + BOOST_CHECK(l2.owns_lock()); + BOOST_CHECK(l3.owns_lock()); + BOOST_CHECK(l4.owns_lock()); + BOOST_CHECK(l5.owns_lock()); + } + else + { + BOOST_CHECK(!l1.owns_lock()); + BOOST_CHECK(!l2.owns_lock()); + BOOST_CHECK(!l3.owns_lock()); + BOOST_CHECK(!l4.owns_lock()); + BOOST_CHECK(!l5.owns_lock()); + } + } +} + + + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: generic locks test suite"); + + test->add(BOOST_TEST_CASE(&test_lock_two_uncontended)); + test->add(BOOST_TEST_CASE(&test_lock_two_other_thread_locks_in_order)); + test->add(BOOST_TEST_CASE(&test_lock_two_other_thread_locks_in_opposite_order)); + test->add(BOOST_TEST_CASE(&test_lock_five_uncontended)); + test->add(BOOST_TEST_CASE(&test_lock_five_other_thread_locks_in_order)); + test->add(BOOST_TEST_CASE(&test_lock_five_other_thread_locks_in_different_order)); + test->add(BOOST_TEST_CASE(&test_lock_five_in_range)); + test->add(BOOST_TEST_CASE(&test_lock_five_in_range_custom_iterator)); + test->add(BOOST_TEST_CASE(&test_lock_ten_in_range_inherited_mutex)); + test->add(BOOST_TEST_CASE(&test_lock_ten_other_thread_locks_in_different_order)); + test->add(BOOST_TEST_CASE(&test_try_lock_two_uncontended)); + test->add(BOOST_TEST_CASE(&test_try_lock_two_first_locked)); + test->add(BOOST_TEST_CASE(&test_try_lock_two_second_locked)); + test->add(BOOST_TEST_CASE(&test_try_lock_three)); + test->add(BOOST_TEST_CASE(&test_try_lock_four)); + test->add(BOOST_TEST_CASE(&test_try_lock_five)); + + return test; +} diff --git a/libs/thread/test/test_hardware_concurrency.cpp b/libs/thread/test/test_hardware_concurrency.cpp new file mode 100644 index 0000000000..39f2fd6ad2 --- /dev/null +++ b/libs/thread/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 <boost/thread/thread.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/thread/mutex.hpp> + +void test_hardware_concurrency_is_non_zero() +{ + BOOST_CHECK(boost::thread::hardware_concurrency()!=0); +} + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::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/libs/thread/test/test_lock_concept.cpp b/libs/thread/test/test_lock_concept.cpp new file mode 100644 index 0000000000..bd0a22ad89 --- /dev/null +++ b/libs/thread/test/test_lock_concept.cpp @@ -0,0 +1,570 @@ +// (C) Copyright 2006-8 Anthony Williams +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/test/unit_test.hpp> +#include <boost/mpl/vector.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/shared_mutex.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/recursive_mutex.hpp> +#include <boost/thread/condition_variable.hpp> + +template<typename Mutex,typename Lock> +struct test_initially_locked +{ + void operator()() const + { + Mutex m; + Lock lock(m); + + BOOST_CHECK(lock); + BOOST_CHECK(lock.owns_lock()); + } +}; + +template<typename Mutex,typename Lock> +struct test_initially_unlocked_if_other_thread_has_lock +{ + Mutex m; + boost::mutex done_mutex; + bool done; + bool locked; + boost::condition_variable done_cond; + + test_initially_unlocked_if_other_thread_has_lock(): + done(false),locked(false) + {} + + void locking_thread() + { + Lock lock(m); + + boost::lock_guard<boost::mutex> lk(done_mutex); + locked=lock.owns_lock(); + done=true; + done_cond.notify_one(); + } + + bool is_done() const + { + return done; + } + + + void operator()() + { + Lock lock(m); + + typedef test_initially_unlocked_if_other_thread_has_lock<Mutex,Lock> this_type; + + boost::thread t(&this_type::locking_thread,this); + + try + { + { + boost::mutex::scoped_lock lk(done_mutex); + BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), + boost::bind(&this_type::is_done,this))); + BOOST_CHECK(!locked); + } + + lock.unlock(); + t.join(); + } + catch(...) + { + lock.unlock(); + t.join(); + throw; + } + } +}; + +template<typename Mutex,typename Lock> +struct test_initially_unlocked_with_try_lock_if_other_thread_has_unique_lock +{ + Mutex m; + boost::mutex done_mutex; + bool done; + bool locked; + boost::condition_variable done_cond; + + test_initially_unlocked_with_try_lock_if_other_thread_has_unique_lock(): + done(false),locked(false) + {} + + void locking_thread() + { + Lock lock(m,boost::try_to_lock); + + boost::lock_guard<boost::mutex> lk(done_mutex); + locked=lock.owns_lock(); + done=true; + done_cond.notify_one(); + } + + bool is_done() const + { + return done; + } + + + void operator()() + { + boost::unique_lock<Mutex> lock(m); + + typedef test_initially_unlocked_with_try_lock_if_other_thread_has_unique_lock<Mutex,Lock> this_type; + + boost::thread t(&this_type::locking_thread,this); + + try + { + { + boost::mutex::scoped_lock lk(done_mutex); + BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), + boost::bind(&this_type::is_done,this))); + BOOST_CHECK(!locked); + } + + lock.unlock(); + t.join(); + } + catch(...) + { + lock.unlock(); + t.join(); + throw; + } + } +}; + +template<typename Mutex,typename Lock> +struct test_initially_locked_if_other_thread_has_shared_lock +{ + Mutex m; + boost::mutex done_mutex; + bool done; + bool locked; + boost::condition_variable done_cond; + + test_initially_locked_if_other_thread_has_shared_lock(): + done(false),locked(false) + {} + + void locking_thread() + { + Lock lock(m); + + boost::lock_guard<boost::mutex> lk(done_mutex); + locked=lock.owns_lock(); + done=true; + done_cond.notify_one(); + } + + bool is_done() const + { + return done; + } + + + void operator()() + { + boost::shared_lock<Mutex> lock(m); + + typedef test_initially_locked_if_other_thread_has_shared_lock<Mutex,Lock> this_type; + + boost::thread t(&this_type::locking_thread,this); + + try + { + { + boost::mutex::scoped_lock lk(done_mutex); + BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), + boost::bind(&this_type::is_done,this))); + BOOST_CHECK(locked); + } + + lock.unlock(); + t.join(); + } + catch(...) + { + lock.unlock(); + t.join(); + throw; + } + } +}; + +template<typename Mutex,typename Lock> +struct test_initially_unlocked_with_defer_lock_parameter +{ + void operator()() const + { + Mutex m; + Lock lock(m,boost::defer_lock); + + BOOST_CHECK(!lock); + BOOST_CHECK(!lock.owns_lock()); + } +}; + +template<typename Mutex,typename Lock> +struct test_initially_locked_with_adopt_lock_parameter +{ + void operator()() const + { + Mutex m; + m.lock(); + Lock lock(m,boost::adopt_lock); + + BOOST_CHECK(lock); + BOOST_CHECK(lock.owns_lock()); + } +}; + + +template<typename Mutex,typename Lock> +struct test_unlocked_after_unlock_called +{ + void operator()() const + { + Mutex m; + Lock lock(m); + lock.unlock(); + BOOST_CHECK(!lock); + BOOST_CHECK(!lock.owns_lock()); + } +}; + +template<typename Mutex,typename Lock> +struct test_locked_after_lock_called +{ + void operator()() const + { + Mutex m; + Lock lock(m,boost::defer_lock); + lock.lock(); + BOOST_CHECK(lock); + BOOST_CHECK(lock.owns_lock()); + } +}; + +template<typename Mutex,typename Lock> +struct test_locked_after_try_lock_called +{ + void operator()() const + { + Mutex m; + Lock lock(m,boost::defer_lock); + lock.try_lock(); + BOOST_CHECK(lock); + BOOST_CHECK(lock.owns_lock()); + } +}; + +template<typename Mutex,typename Lock> +struct test_unlocked_after_try_lock_if_other_thread_has_lock +{ + Mutex m; + boost::mutex done_mutex; + bool done; + bool locked; + boost::condition_variable done_cond; + + test_unlocked_after_try_lock_if_other_thread_has_lock(): + done(false),locked(false) + {} + + void locking_thread() + { + Lock lock(m,boost::defer_lock); + + boost::lock_guard<boost::mutex> lk(done_mutex); + locked=lock.owns_lock(); + done=true; + done_cond.notify_one(); + } + + bool is_done() const + { + return done; + } + + + void operator()() + { + Lock lock(m); + + typedef test_unlocked_after_try_lock_if_other_thread_has_lock<Mutex,Lock> this_type; + + boost::thread t(&this_type::locking_thread,this); + + try + { + { + boost::mutex::scoped_lock lk(done_mutex); + BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), + boost::bind(&this_type::is_done,this))); + BOOST_CHECK(!locked); + } + + lock.unlock(); + t.join(); + } + catch(...) + { + lock.unlock(); + t.join(); + throw; + } + } +}; + +template<typename Mutex,typename Lock> +struct test_throws_if_lock_called_when_already_locked +{ + void operator()() const + { + Mutex m; + Lock lock(m); + + BOOST_CHECK_THROW( lock.lock(), boost::lock_error ); + } +}; + +template<typename Mutex,typename Lock> +struct test_throws_if_try_lock_called_when_already_locked +{ + void operator()() const + { + Mutex m; + Lock lock(m); + + BOOST_CHECK_THROW( lock.try_lock(), boost::lock_error ); + } +}; + +template<typename Mutex,typename Lock> +struct test_throws_if_unlock_called_when_already_unlocked +{ + void operator()() const + { + Mutex m; + Lock lock(m); + lock.unlock(); + + BOOST_CHECK_THROW( lock.unlock(), boost::lock_error ); + } +}; +template<typename Lock> +struct test_default_constructed_has_no_mutex_and_unlocked +{ + void operator()() const + { + Lock l; + BOOST_CHECK(!l.mutex()); + BOOST_CHECK(!l.owns_lock()); + }; +}; + + +template<typename Mutex,typename Lock> +struct test_locks_can_be_swapped +{ + void operator()() const + { + Mutex m1; + Mutex m2; + Mutex m3; + + Lock l1(m1); + Lock l2(m2); + + BOOST_CHECK_EQUAL(l1.mutex(),&m1); + BOOST_CHECK_EQUAL(l2.mutex(),&m2); + + l1.swap(l2); + + BOOST_CHECK_EQUAL(l1.mutex(),&m2); + BOOST_CHECK_EQUAL(l2.mutex(),&m1); + + swap(l1,l2); + + BOOST_CHECK_EQUAL(l1.mutex(),&m1); + BOOST_CHECK_EQUAL(l2.mutex(),&m2); + + l1.swap(Lock(m3)); + + BOOST_CHECK_EQUAL(l1.mutex(),&m3); + } +}; + +template<typename Mutex,typename Lock> +void test_lock_is_scoped_lock_concept_for_mutex() +{ + test_default_constructed_has_no_mutex_and_unlocked<Lock>()(); + test_initially_locked<Mutex,Lock>()(); + test_initially_unlocked_with_defer_lock_parameter<Mutex,Lock>()(); + test_initially_locked_with_adopt_lock_parameter<Mutex,Lock>()(); + test_unlocked_after_unlock_called<Mutex,Lock>()(); + test_locked_after_lock_called<Mutex,Lock>()(); + test_throws_if_lock_called_when_already_locked<Mutex,Lock>()(); + test_throws_if_unlock_called_when_already_unlocked<Mutex,Lock>()(); + test_locks_can_be_swapped<Mutex,Lock>()(); + test_locked_after_try_lock_called<Mutex,Lock>()(); + test_throws_if_try_lock_called_when_already_locked<Mutex,Lock>()(); + test_unlocked_after_try_lock_if_other_thread_has_lock<Mutex,Lock>()(); +} + + +BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_lock_concept,Mutex) +{ + typedef typename Mutex::scoped_lock Lock; + + test_lock_is_scoped_lock_concept_for_mutex<Mutex,Lock>(); +} + +BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_unique_lock_is_scoped_lock,Mutex) +{ + typedef boost::unique_lock<Mutex> Lock; + + test_lock_is_scoped_lock_concept_for_mutex<Mutex,Lock>(); +} + +BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_scoped_try_lock_concept,Mutex) +{ + typedef typename Mutex::scoped_try_lock Lock; + + test_default_constructed_has_no_mutex_and_unlocked<Lock>()(); + test_initially_locked<Mutex,Lock>()(); + test_initially_unlocked_if_other_thread_has_lock<Mutex,Lock>()(); + test_initially_unlocked_with_defer_lock_parameter<Mutex,Lock>()(); + test_initially_locked_with_adopt_lock_parameter<Mutex,Lock>()(); + test_unlocked_after_unlock_called<Mutex,Lock>()(); + test_locked_after_lock_called<Mutex,Lock>()(); + test_locked_after_try_lock_called<Mutex,Lock>()(); + test_unlocked_after_try_lock_if_other_thread_has_lock<Mutex,Lock>()(); + test_throws_if_lock_called_when_already_locked<Mutex,Lock>()(); + test_throws_if_try_lock_called_when_already_locked<Mutex,Lock>()(); + test_throws_if_unlock_called_when_already_unlocked<Mutex,Lock>()(); + test_locks_can_be_swapped<Mutex,Lock>()(); +} + +struct dummy_shared_mutex +{ + bool locked; + bool shared_locked; + bool shared_unlocked; + bool shared_timed_locked_relative; + bool shared_timed_locked_absolute; + bool timed_locked_relative; + bool timed_locked_absolute; + + dummy_shared_mutex(): + locked(false),shared_locked(false),shared_unlocked(false), + shared_timed_locked_relative(false), + shared_timed_locked_absolute(false), + timed_locked_relative(false), + timed_locked_absolute(false) + {} + + void lock() + { + locked=true; + } + + void lock_shared() + { + shared_locked=true; + } + + void unlock() + {} + + void unlock_shared() + { + shared_unlocked=true; + } + + bool timed_lock_shared(boost::system_time) + { + shared_timed_locked_absolute=true; + return false; + } + template<typename Duration> + bool timed_lock_shared(Duration) + { + shared_timed_locked_relative=true; + return false; + } + bool timed_lock(boost::system_time) + { + timed_locked_absolute=true; + return false; + } + template<typename Duration> + bool timed_lock(Duration) + { + timed_locked_relative=true; + return false; + } + +}; + + +void test_shared_lock() +{ + typedef boost::shared_mutex Mutex; + typedef boost::shared_lock<Mutex> Lock; + + test_default_constructed_has_no_mutex_and_unlocked<Lock>()(); + test_initially_locked<Mutex,Lock>()(); + test_initially_unlocked_with_try_lock_if_other_thread_has_unique_lock<Mutex,Lock>()(); + test_initially_locked_if_other_thread_has_shared_lock<Mutex,Lock>()(); + test_initially_unlocked_with_defer_lock_parameter<Mutex,Lock>()(); + test_initially_locked_with_adopt_lock_parameter<Mutex,Lock>()(); + test_unlocked_after_unlock_called<Mutex,Lock>()(); + test_locked_after_lock_called<Mutex,Lock>()(); + test_locked_after_try_lock_called<Mutex,Lock>()(); + test_throws_if_lock_called_when_already_locked<Mutex,Lock>()(); + test_throws_if_try_lock_called_when_already_locked<Mutex,Lock>()(); + test_throws_if_unlock_called_when_already_unlocked<Mutex,Lock>()(); + test_locks_can_be_swapped<Mutex,Lock>()(); + + dummy_shared_mutex dummy; + boost::shared_lock<dummy_shared_mutex> lk(dummy); + BOOST_CHECK(dummy.shared_locked); + lk.unlock(); + BOOST_CHECK(dummy.shared_unlocked); + lk.timed_lock(boost::posix_time::milliseconds(5)); + BOOST_CHECK(dummy.shared_timed_locked_relative); + lk.timed_lock(boost::get_system_time()); + BOOST_CHECK(dummy.shared_timed_locked_absolute); +} + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: lock concept test suite"); + + typedef boost::mpl::vector<boost::mutex,boost::try_mutex,boost::timed_mutex, + boost::recursive_mutex,boost::recursive_try_mutex,boost::recursive_timed_mutex> mutex_types_with_scoped_lock; + + test->add(BOOST_TEST_CASE_TEMPLATE(test_scoped_lock_concept,mutex_types_with_scoped_lock)); + + typedef boost::mpl::vector<boost::try_mutex,boost::timed_mutex, + boost::recursive_try_mutex,boost::recursive_timed_mutex> mutex_types_with_scoped_try_lock; + + test->add(BOOST_TEST_CASE_TEMPLATE(test_scoped_try_lock_concept,mutex_types_with_scoped_try_lock)); + + typedef boost::mpl::vector<boost::mutex,boost::try_mutex,boost::timed_mutex, + boost::recursive_mutex,boost::recursive_try_mutex,boost::recursive_timed_mutex,boost::shared_mutex> all_mutex_types; + + test->add(BOOST_TEST_CASE_TEMPLATE(test_unique_lock_is_scoped_lock,all_mutex_types)); + test->add(BOOST_TEST_CASE(&test_shared_lock)); + + return test; +} diff --git a/libs/thread/test/test_move_function.cpp b/libs/thread/test/test_move_function.cpp new file mode 100644 index 0000000000..fa139e8863 --- /dev/null +++ b/libs/thread/test/test_move_function.cpp @@ -0,0 +1,145 @@ +// Copyright (C) 2007-8 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#include <boost/thread/thread.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/shared_ptr.hpp> + +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_lvalue_on_assignment() +{ + boost::thread src(do_nothing); + boost::thread::id src_id=src.get_id(); + boost::thread dest; + 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(); +} + +boost::thread start_thread() +{ + return boost::thread(do_nothing); +} + +void test_thread_move_from_rvalue_on_construction() +{ + boost::thread x(start_thread()); + BOOST_CHECK(x.get_id()!=boost::thread::id()); + x.join(); +} + +void test_thread_move_from_rvalue_using_explicit_move() +{ + boost::thread x(boost::move(start_thread())); + 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<boost::mutex> l(m); + BOOST_CHECK(l.owns_lock()); + BOOST_CHECK(l.mutex()==&m); + + boost::unique_lock<boost::mutex> l2(boost::move(l)); + BOOST_CHECK(!l.owns_lock()); + BOOST_CHECK(!l.mutex()); + BOOST_CHECK(l2.owns_lock()); + BOOST_CHECK(l2.mutex()==&m); +} + +boost::unique_lock<boost::mutex> get_lock(boost::mutex& m) +{ + return boost::unique_lock<boost::mutex>(m); +} + + +void test_unique_lock_move_from_rvalue_on_construction() +{ + boost::mutex m; + boost::unique_lock<boost::mutex> l(get_lock(m)); + BOOST_CHECK(l.owns_lock()); + BOOST_CHECK(l.mutex()==&m); +} + +namespace user_test_ns +{ + template<typename T> + T move(T& t) + { + return t.move(); + } + + bool move_called=false; + + struct nc: + public boost::shared_ptr<int> + { +#ifndef BOOST_NO_RVALUE_REFERENCES + nc() {} + nc(nc&&) + { + move_called=true; + } +#endif + nc move() + { + move_called=true; + return nc(); + } + }; +} + +#ifdef BOOST_NO_RVALUE_REFERENCES +namespace boost +{ + template <> + struct has_move_emulation_enabled_aux<user_test_ns::nc> + : BOOST_MOVE_BOOST_NS::integral_constant<bool, true> + {}; +} +#endif + +void test_move_for_user_defined_type_unaffected() +{ + user_test_ns::nc src; +#ifndef BOOST_NO_RVALUE_REFERENCES + user_test_ns::nc dest=boost::move(src); +#else + user_test_ns::nc dest=move(src); +#endif + BOOST_CHECK(user_test_ns::move_called); +} + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::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_thread_move_from_rvalue_using_explicit_move)); + test->add(BOOST_TEST_CASE(test_thread_move_from_lvalue_on_assignment)); + test->add(BOOST_TEST_CASE(test_unique_lock_move_from_lvalue_on_construction)); + test->add(BOOST_TEST_CASE(test_unique_lock_move_from_rvalue_on_construction)); + test->add(BOOST_TEST_CASE(test_move_for_user_defined_type_unaffected)); + return test; +} diff --git a/libs/thread/test/test_mutex.cpp b/libs/thread/test/test_mutex.cpp new file mode 100644 index 0000000000..0350898e8b --- /dev/null +++ b/libs/thread/test/test_mutex.cpp @@ -0,0 +1,347 @@ +// Copyright (C) 2001-2003 +// William E. Kempf +// +// 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 <boost/thread/detail/config.hpp> + +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/recursive_mutex.hpp> +#include <boost/thread/thread_time.hpp> +#include <boost/thread/condition.hpp> + +#include <boost/test/unit_test.hpp> + +#define DEFAULT_EXECUTION_MONITOR_TYPE execution_monitor::use_sleep_only +#include <libs/thread/test/util.inl> + +template <typename M> +struct test_lock +{ + typedef M mutex_type; + typedef typename M::scoped_lock lock_type; + + void operator()() + { + mutex_type mutex; + boost::condition condition; + + // Test the lock's constructors. + { + lock_type lock(mutex, boost::defer_lock); + BOOST_CHECK(!lock); + } + lock_type lock(mutex); + BOOST_CHECK(lock ? true : false); + + // Construct and initialize an xtime for a fast time out. + boost::xtime xt = delay(0, 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(lock ? true : false); + + // Test the lock and unlock methods. + lock.unlock(); + BOOST_CHECK(!lock); + lock.lock(); + BOOST_CHECK(lock ? true : false); + } +}; + +template <typename M> +struct test_trylock +{ + typedef M mutex_type; + typedef typename M::scoped_try_lock try_lock_type; + + void operator()() + { + mutex_type mutex; + boost::condition condition; + + // Test the lock's constructors. + { + try_lock_type lock(mutex); + BOOST_CHECK(lock ? true : false); + } + { + try_lock_type lock(mutex, boost::defer_lock); + BOOST_CHECK(!lock); + } + try_lock_type lock(mutex); + BOOST_CHECK(lock ? true : false); + + // Construct and initialize an xtime for a fast time out. + boost::xtime xt = delay(0, 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(lock ? true : false); + + // Test the lock, unlock and trylock methods. + lock.unlock(); + BOOST_CHECK(!lock); + lock.lock(); + BOOST_CHECK(lock ? true : false); + lock.unlock(); + BOOST_CHECK(!lock); + BOOST_CHECK(lock.try_lock()); + BOOST_CHECK(lock ? true : false); + } +}; + +template<typename Mutex> +struct test_lock_times_out_if_other_thread_has_lock +{ + typedef boost::unique_lock<Mutex> Lock; + + Mutex m; + boost::mutex done_mutex; + bool done; + bool locked; + boost::condition_variable done_cond; + + test_lock_times_out_if_other_thread_has_lock(): + done(false),locked(false) + {} + + void locking_thread() + { + Lock lock(m,boost::defer_lock); + lock.timed_lock(boost::posix_time::milliseconds(50)); + + boost::lock_guard<boost::mutex> lk(done_mutex); + locked=lock.owns_lock(); + done=true; + done_cond.notify_one(); + } + + void locking_thread_through_constructor() + { + Lock lock(m,boost::posix_time::milliseconds(50)); + + boost::lock_guard<boost::mutex> lk(done_mutex); + locked=lock.owns_lock(); + done=true; + done_cond.notify_one(); + } + + bool is_done() const + { + return done; + } + + typedef test_lock_times_out_if_other_thread_has_lock<Mutex> this_type; + + void do_test(void (this_type::*test_func)()) + { + Lock lock(m); + + locked=false; + done=false; + + boost::thread t(test_func,this); + + try + { + { + boost::mutex::scoped_lock lk(done_mutex); + BOOST_CHECK(done_cond.timed_wait(lk,boost::posix_time::seconds(2), + boost::bind(&this_type::is_done,this))); + BOOST_CHECK(!locked); + } + + lock.unlock(); + t.join(); + } + catch(...) + { + lock.unlock(); + t.join(); + throw; + } + } + + + void operator()() + { + do_test(&this_type::locking_thread); + do_test(&this_type::locking_thread_through_constructor); + } +}; + +template <typename M> +struct test_timedlock +{ + typedef M mutex_type; + typedef typename M::scoped_timed_lock timed_lock_type; + + static bool fake_predicate() + { + return false; + } + + void operator()() + { + test_lock_times_out_if_other_thread_has_lock<mutex_type>()(); + + mutex_type mutex; + boost::condition condition; + + // Test the lock's constructors. + { + // Construct and initialize an xtime for a fast time out. + boost::system_time xt = boost::get_system_time()+boost::posix_time::milliseconds(100); + + timed_lock_type lock(mutex, xt); + BOOST_CHECK(lock ? true : false); + } + { + timed_lock_type lock(mutex, boost::defer_lock); + BOOST_CHECK(!lock); + } + timed_lock_type lock(mutex); + BOOST_CHECK(lock ? true : false); + + // Construct and initialize an xtime for a fast time out. + 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, timeout, fake_predicate)); + BOOST_CHECK(lock ? true : false); + + boost::system_time now=boost::get_system_time(); + boost::posix_time::milliseconds const timeout_resolution(20); + BOOST_CHECK((timeout-timeout_resolution)<now); + + // Test the lock, unlock and timedlock methods. + lock.unlock(); + BOOST_CHECK(!lock); + lock.lock(); + BOOST_CHECK(lock ? true : false); + lock.unlock(); + BOOST_CHECK(!lock); + boost::system_time target = boost::get_system_time()+boost::posix_time::milliseconds(100); + BOOST_CHECK(lock.timed_lock(target)); + BOOST_CHECK(lock ? true : false); + lock.unlock(); + BOOST_CHECK(!lock); + + BOOST_CHECK(mutex.timed_lock(boost::posix_time::milliseconds(100))); + mutex.unlock(); + + BOOST_CHECK(lock.timed_lock(boost::posix_time::milliseconds(100))); + BOOST_CHECK(lock ? true : false); + lock.unlock(); + BOOST_CHECK(!lock); + + } +}; + +template <typename M> +struct test_recursive_lock +{ + typedef M mutex_type; + typedef typename M::scoped_lock lock_type; + + void operator()() + { + mutex_type mx; + lock_type lock1(mx); + lock_type lock2(mx); + } +}; + + +void do_test_mutex() +{ + test_lock<boost::mutex>()(); +} + +void test_mutex() +{ + timed_test(&do_test_mutex, 3); +} + +void do_test_try_mutex() +{ + test_lock<boost::try_mutex>()(); + test_trylock<boost::try_mutex>()(); +} + +void test_try_mutex() +{ + timed_test(&do_test_try_mutex, 3); +} + +void do_test_timed_mutex() +{ + test_lock<boost::timed_mutex>()(); + test_trylock<boost::timed_mutex>()(); + test_timedlock<boost::timed_mutex>()(); +} + +void test_timed_mutex() +{ + timed_test(&do_test_timed_mutex, 3); +} + +void do_test_recursive_mutex() +{ + test_lock<boost::recursive_mutex>()(); + test_recursive_lock<boost::recursive_mutex>()(); +} + +void test_recursive_mutex() +{ + timed_test(&do_test_recursive_mutex, 3); +} + +void do_test_recursive_try_mutex() +{ + test_lock<boost::recursive_try_mutex>()(); + test_trylock<boost::recursive_try_mutex>()(); + test_recursive_lock<boost::recursive_try_mutex>()(); +} + +void test_recursive_try_mutex() +{ + timed_test(&do_test_recursive_try_mutex, 3); +} + +void do_test_recursive_timed_mutex() +{ + test_lock<boost::recursive_timed_mutex>()(); + test_trylock<boost::recursive_timed_mutex>()(); + test_timedlock<boost::recursive_timed_mutex>()(); + test_recursive_lock<boost::recursive_timed_mutex>()(); +} + +void test_recursive_timed_mutex() +{ + timed_test(&do_test_recursive_timed_mutex, 3); +} + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: mutex test suite"); + + test->add(BOOST_TEST_CASE(&test_mutex)); + test->add(BOOST_TEST_CASE(&test_try_mutex)); + test->add(BOOST_TEST_CASE(&test_timed_mutex)); + test->add(BOOST_TEST_CASE(&test_recursive_mutex)); + test->add(BOOST_TEST_CASE(&test_recursive_try_mutex)); + test->add(BOOST_TEST_CASE(&test_recursive_timed_mutex)); + + return test; +} diff --git a/libs/thread/test/test_once.cpp b/libs/thread/test/test_once.cpp new file mode 100644 index 0000000000..340bef7e3c --- /dev/null +++ b/libs/thread/test/test_once.cpp @@ -0,0 +1,191 @@ +// (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 <boost/test/unit_test.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/once.hpp> + +boost::once_flag flag=BOOST_ONCE_INIT; +int var_to_init=0; +boost::mutex m; + +void initialize_variable() +{ + // ensure that if multiple threads get in here, they are serialized, so we can see the effect + boost::mutex::scoped_lock lock(m); + ++var_to_init; +} + + +void call_once_thread() +{ + unsigned const loop_count=100; + int my_once_value=0; + for(unsigned i=0;i<loop_count;++i) + { + boost::call_once(flag, initialize_variable); + my_once_value=var_to_init; + if(my_once_value!=1) + { + break; + } + } + boost::mutex::scoped_lock lock(m); + BOOST_CHECK_EQUAL(my_once_value, 1); +} + +void test_call_once() +{ + unsigned const num_threads=20; + boost::thread_group group; + + try + { + for(unsigned i=0;i<num_threads;++i) + { + group.create_thread(&call_once_thread); + } + group.join_all(); + } + catch(...) + { + group.interrupt_all(); + group.join_all(); + throw; + } + + BOOST_CHECK_EQUAL(var_to_init,1); +} + +int var_to_init_with_functor=0; + +struct increment_value +{ + int* value; + explicit increment_value(int* value_): + value(value_) + {} + + void operator()() const + { + boost::mutex::scoped_lock lock(m); + ++(*value); + } +}; + +void call_once_with_functor() +{ + unsigned const loop_count=100; + int my_once_value=0; + static boost::once_flag functor_flag=BOOST_ONCE_INIT; + for(unsigned i=0;i<loop_count;++i) + { + boost::call_once(functor_flag, increment_value(&var_to_init_with_functor)); + my_once_value=var_to_init_with_functor; + if(my_once_value!=1) + { + break; + } + } + boost::mutex::scoped_lock lock(m); + BOOST_CHECK_EQUAL(my_once_value, 1); +} + +void test_call_once_arbitrary_functor() +{ + unsigned const num_threads=20; + boost::thread_group group; + + try + { + for(unsigned i=0;i<num_threads;++i) + { + group.create_thread(&call_once_with_functor); + } + group.join_all(); + } + catch(...) + { + group.interrupt_all(); + group.join_all(); + throw; + } + + BOOST_CHECK_EQUAL(var_to_init_with_functor,1); +} + + +struct throw_before_third_pass +{ + struct my_exception + {}; + + static unsigned pass_counter; + + void operator()() const + { + boost::mutex::scoped_lock lock(m); + ++pass_counter; + if(pass_counter<3) + { + throw my_exception(); + } + } +}; + +unsigned throw_before_third_pass::pass_counter=0; +unsigned exception_counter=0; + +void call_once_with_exception() +{ + static boost::once_flag functor_flag=BOOST_ONCE_INIT; + try + { + boost::call_once(functor_flag, throw_before_third_pass()); + } + catch(throw_before_third_pass::my_exception) + { + boost::mutex::scoped_lock lock(m); + ++exception_counter; + } +} + +void test_call_once_retried_on_exception() +{ + unsigned const num_threads=20; + boost::thread_group group; + + try + { + for(unsigned i=0;i<num_threads;++i) + { + group.create_thread(&call_once_with_exception); + } + group.join_all(); + } + catch(...) + { + group.interrupt_all(); + group.join_all(); + throw; + } + + BOOST_CHECK_EQUAL(throw_before_third_pass::pass_counter,3u); + BOOST_CHECK_EQUAL(exception_counter,2u); +} + + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: call_once test suite"); + + test->add(BOOST_TEST_CASE(test_call_once)); + test->add(BOOST_TEST_CASE(test_call_once_arbitrary_functor)); + test->add(BOOST_TEST_CASE(test_call_once_retried_on_exception)); + + return test; +} diff --git a/libs/thread/test/test_shared_mutex.cpp b/libs/thread/test/test_shared_mutex.cpp new file mode 100644 index 0000000000..6085bd1211 --- /dev/null +++ b/libs/thread/test/test_shared_mutex.cpp @@ -0,0 +1,285 @@ +// (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 <boost/test/unit_test.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/xtime.hpp> +#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_multiple_readers() +{ + unsigned const number_of_threads=10; + + 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<number_of_threads;++i) + { + pool.create_thread(locking_thread<boost::shared_lock<boost::shared_mutex> >(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<number_of_threads) + { + unblocked_condition.wait(lk); + } + } + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,number_of_threads); + + finish_lock.unlock(); + + pool.join_all(); + } + catch(...) + { + pool.interrupt_all(); + pool.join_all(); + throw; + } + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,number_of_threads); +} + +void test_only_one_writer_permitted() +{ + unsigned const number_of_threads=10; + + 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<number_of_threads;++i) + { + pool.create_thread(locking_thread<boost::unique_lock<boost::shared_mutex> >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_mutex,simultaneous_running_count,max_simultaneous_running)); + } + + boost::thread::sleep(delay(2)); + + 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_reader_blocks_writer() +{ + 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 + { + + pool.create_thread(locking_thread<boost::shared_lock<boost::shared_mutex> >(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<1) + { + unblocked_condition.wait(lk); + } + } + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,1U); + pool.create_thread(locking_thread<boost::unique_lock<boost::shared_mutex> >(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,2U); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,1u); +} + +void test_unlocking_writer_unblocks_all_readers() +{ + boost::thread_group pool; + + boost::shared_mutex rw_mutex; + boost::unique_lock<boost::shared_mutex> write_lock(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=10; + + try + { + for(unsigned i=0;i<reader_count;++i) + { + pool.create_thread(locking_thread<boost::shared_lock<boost::shared_mutex> >(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,0U); + + write_lock.unlock(); + + { + boost::mutex::scoped_lock lk(unblocked_count_mutex); + while(unblocked_count<reader_count) + { + unblocked_condition.wait(lk); + } + } + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count); + + finish_lock.unlock(); + pool.join_all(); + } + catch(...) + { + pool.interrupt_all(); + pool.join_all(); + throw; + } + + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,max_simultaneous_running,reader_count); +} + +void test_unlocking_last_reader_only_unblocks_one_writer() +{ + boost::thread_group pool; + + boost::shared_mutex rw_mutex; + unsigned unblocked_count=0; + unsigned simultaneous_running_readers=0; + unsigned max_simultaneous_readers=0; + unsigned simultaneous_running_writers=0; + unsigned max_simultaneous_writers=0; + boost::mutex unblocked_count_mutex; + boost::condition_variable unblocked_condition; + boost::mutex finish_reading_mutex; + boost::mutex::scoped_lock finish_reading_lock(finish_reading_mutex); + boost::mutex finish_writing_mutex; + boost::mutex::scoped_lock finish_writing_lock(finish_writing_mutex); + + unsigned const reader_count=10; + unsigned const writer_count=10; + + try + { + for(unsigned i=0;i<reader_count;++i) + { + pool.create_thread(locking_thread<boost::shared_lock<boost::shared_mutex> >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_reading_mutex,simultaneous_running_readers,max_simultaneous_readers)); + } + boost::thread::sleep(delay(1)); + for(unsigned i=0;i<writer_count;++i) + { + pool.create_thread(locking_thread<boost::unique_lock<boost::shared_mutex> >(rw_mutex,unblocked_count,unblocked_count_mutex,unblocked_condition, + finish_writing_mutex,simultaneous_running_writers,max_simultaneous_writers)); + } + { + boost::mutex::scoped_lock lk(unblocked_count_mutex); + while(unblocked_count<reader_count) + { + unblocked_condition.wait(lk); + } + } + boost::thread::sleep(delay(1)); + CHECK_LOCKED_VALUE_EQUAL(unblocked_count_mutex,unblocked_count,reader_count); + + finish_reading_lock.unlock(); + + { + 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_writing_lock.unlock(); + pool.join_all(); + } + catch(...) + { + pool.interrupt_all(); + pool.join_all(); + 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); +} + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: shared_mutex test suite"); + + test->add(BOOST_TEST_CASE(&test_multiple_readers)); + test->add(BOOST_TEST_CASE(&test_only_one_writer_permitted)); + 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)); + + return test; +} diff --git a/libs/thread/test/test_shared_mutex_part_2.cpp b/libs/thread/test/test_shared_mutex_part_2.cpp new file mode 100644 index 0000000000..eb9c05ee21 --- /dev/null +++ b/libs/thread/test/test_shared_mutex_part_2.cpp @@ -0,0 +1,299 @@ +// (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 <boost/test/unit_test.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/xtime.hpp> +#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); \ + } + +class simple_upgrade_thread +{ + boost::shared_mutex& rwm; + boost::mutex& finish_mutex; + boost::mutex& unblocked_mutex; + unsigned& unblocked_count; + + void operator=(simple_upgrade_thread&); + +public: + simple_upgrade_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::upgrade_lock<boost::shared_mutex> lk(rwm); + + { + boost::mutex::scoped_lock ulk(unblocked_mutex); + ++unblocked_count; + } + + boost::mutex::scoped_lock flk(finish_mutex); + } +}; + + +void test_only_one_upgrade_lock_permitted() +{ + unsigned const number_of_threads=10; + + 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<number_of_threads;++i) + { + pool.create_thread(locking_thread<boost::upgrade_lock<boost::shared_mutex> >(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=10; + + try + { + for(unsigned i=0;i<reader_count;++i) + { + pool.create_thread(locking_thread<boost::shared_lock<boost::shared_mutex> >(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<boost::upgrade_lock<boost::shared_mutex> >(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); +} + +void test_can_lock_upgrade_to_unique_if_currently_locked_upgrade() +{ + boost::shared_mutex mtx; + boost::upgrade_lock<boost::shared_mutex> l(mtx); + boost::upgrade_to_unique_lock<boost::shared_mutex> ul(l); + BOOST_CHECK(ul.owns_lock()); +} + +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_other_thread_has_write_lock_try_lock_upgrade_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_upgrade(); + BOOST_CHECK(!try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_upgrade(); + } + + 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(); + } +} + +void test_if_no_thread_has_lock_try_lock_upgrade_returns_true() +{ + boost::shared_mutex rw_mutex; + bool const try_succeeded=rw_mutex.try_lock_upgrade(); + BOOST_CHECK(try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_upgrade(); + } +} + +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_if_other_thread_has_shared_lock_try_lock_upgrade_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_upgrade(); + BOOST_CHECK(try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_upgrade(); + } + + finish_lock.unlock(); + writer.join(); +} + +void test_if_other_thread_has_upgrade_lock_try_lock_upgrade_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_upgrade_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_upgrade(); + BOOST_CHECK(!try_succeeded); + if(try_succeeded) + { + rw_mutex.unlock_upgrade(); + } + + finish_lock.unlock(); + writer.join(); +} + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: shared_mutex test suite"); + + 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_can_lock_upgrade_to_unique_if_currently_locked_upgrade)); + 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)); + + return test; +} diff --git a/libs/thread/test/test_shared_mutex_timed_locks.cpp b/libs/thread/test/test_shared_mutex_timed_locks.cpp new file mode 100644 index 0000000000..2e39cc062a --- /dev/null +++ b/libs/thread/test/test_shared_mutex_timed_locks.cpp @@ -0,0 +1,268 @@ +// (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 <boost/test/unit_test.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/xtime.hpp> +#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)<boost::get_system_time()); + BOOST_CHECK(!timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock_shared(); + } + + boost::posix_time::milliseconds const wait_duration(500); + boost::system_time const timeout2=boost::get_system_time()+wait_duration; + timed_lock_succeeded=rw_mutex.timed_lock_shared(wait_duration); + BOOST_CHECK((timeout2-timeout_resolution)<boost::get_system_time()); + BOOST_CHECK(!timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock_shared(); + } + + finish_lock.unlock(); + writer.join(); +} + +void test_timed_lock_shared_succeeds_if_no_lock_held() +{ + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + + 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(boost::get_system_time()<timeout); + BOOST_CHECK(timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock_shared(); + } + + boost::posix_time::milliseconds const wait_duration(500); + boost::system_time const timeout2=boost::get_system_time()+wait_duration; + timed_lock_succeeded=rw_mutex.timed_lock_shared(wait_duration); + BOOST_CHECK(boost::get_system_time()<timeout2); + BOOST_CHECK(timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock_shared(); + } + +} + +void test_timed_lock_shared_succeeds_if_read_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 reader(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); + + 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(boost::get_system_time()<timeout); + BOOST_CHECK(timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock_shared(); + } + + boost::posix_time::milliseconds const wait_duration(500); + boost::system_time const timeout2=boost::get_system_time()+wait_duration; + timed_lock_succeeded=rw_mutex.timed_lock_shared(wait_duration); + BOOST_CHECK(boost::get_system_time()<timeout2); + BOOST_CHECK(timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock_shared(); + } + + finish_lock.unlock(); + reader.join(); +} + +void test_timed_lock_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(timeout); + BOOST_CHECK((timeout-timeout_resolution)<boost::get_system_time()); + BOOST_CHECK(!timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock(); + } + + boost::posix_time::milliseconds const wait_duration(500); + boost::system_time const timeout2=boost::get_system_time()+wait_duration; + timed_lock_succeeded=rw_mutex.timed_lock(wait_duration); + BOOST_CHECK((timeout2-timeout_resolution)<boost::get_system_time()); + BOOST_CHECK(!timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock(); + } + + finish_lock.unlock(); + writer.join(); +} + +void test_timed_lock_succeeds_if_no_lock_held() +{ + boost::shared_mutex rw_mutex; + boost::mutex finish_mutex; + boost::mutex unblocked_mutex; + + 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(timeout); + BOOST_CHECK(boost::get_system_time()<timeout); + BOOST_CHECK(timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock(); + } + + boost::posix_time::milliseconds const wait_duration(500); + boost::system_time const timeout2=boost::get_system_time()+wait_duration; + timed_lock_succeeded=rw_mutex.timed_lock(wait_duration); + BOOST_CHECK(boost::get_system_time()<timeout2); + BOOST_CHECK(timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock(); + } + +} + +void test_timed_lock_times_out_if_read_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 reader(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); + + 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(timeout); + BOOST_CHECK((timeout-timeout_resolution)<boost::get_system_time()); + BOOST_CHECK(!timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock(); + } + + boost::posix_time::milliseconds const wait_duration(500); + boost::system_time const timeout2=boost::get_system_time()+wait_duration; + timed_lock_succeeded=rw_mutex.timed_lock(wait_duration); + BOOST_CHECK((timeout2-timeout_resolution)<boost::get_system_time()); + BOOST_CHECK(!timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock(); + } + + finish_lock.unlock(); + reader.join(); +} + +void test_timed_lock_times_out_but_read_lock_succeeds_if_read_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 reader(simple_reading_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); + + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+boost::posix_time::milliseconds(500); + bool timed_lock_succeeded=rw_mutex.timed_lock(timeout); + BOOST_CHECK(!timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock(); + } + + boost::posix_time::milliseconds const wait_duration(500); + timed_lock_succeeded=rw_mutex.timed_lock_shared(wait_duration); + BOOST_CHECK(timed_lock_succeeded); + if(timed_lock_succeeded) + { + rw_mutex.unlock_shared(); + } + + finish_lock.unlock(); + reader.join(); +} + + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: shared_mutex test suite"); + + test->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)); + test->add(BOOST_TEST_CASE(&test_timed_lock_times_out_but_read_lock_succeeds_if_read_lock_held)); + + return test; +} diff --git a/libs/thread/test/test_thread.cpp b/libs/thread/test/test_thread.cpp new file mode 100644 index 0000000000..fe6ffa36fa --- /dev/null +++ b/libs/thread/test/test_thread.cpp @@ -0,0 +1,234 @@ +// Copyright (C) 2001-2003 +// William E. Kempf +// Copyright (C) 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) + +#include <boost/thread/detail/config.hpp> + +#include <boost/thread/thread.hpp> +#include <boost/thread/xtime.hpp> +#include <boost/bind.hpp> +#include <boost/ref.hpp> +#include <boost/utility.hpp> + +#include <boost/test/unit_test.hpp> + +#define DEFAULT_EXECUTION_MONITOR_TYPE execution_monitor::use_sleep_only +#include <libs/thread/test/util.inl> + +int test_value; + +void simple_thread() +{ + test_value = 999; +} + +void comparison_thread(boost::thread::id parent) +{ + boost::thread::id const my_id=boost::this_thread::get_id(); + + BOOST_CHECK(my_id != parent); + boost::thread::id const my_id2=boost::this_thread::get_id(); + BOOST_CHECK(my_id == my_id2); + + boost::thread::id const no_thread_id=boost::thread::id(); + BOOST_CHECK(my_id != no_thread_id); +} + +void test_sleep() +{ + boost::xtime xt = delay(3); + boost::thread::sleep(xt); + + // Ensure it's in a range instead of checking actual equality due to time + // lapse + BOOST_CHECK(in_range(xt, 2)); +} + +void do_test_creation() +{ + test_value = 0; + boost::thread thrd(&simple_thread); + thrd.join(); + BOOST_CHECK_EQUAL(test_value, 999); +} + +void test_creation() +{ + timed_test(&do_test_creation, 1); +} + +void do_test_id_comparison() +{ + boost::thread::id const self=boost::this_thread::get_id(); + boost::thread thrd(boost::bind(&comparison_thread, self)); + thrd.join(); +} + +void test_id_comparison() +{ + timed_test(&do_test_id_comparison, 1); +} + +void interruption_point_thread(boost::mutex* m,bool* failed) +{ + boost::mutex::scoped_lock lk(*m); + boost::this_thread::interruption_point(); + *failed=true; +} + +void do_test_thread_interrupts_at_interruption_point() +{ + boost::mutex m; + bool failed=false; + boost::mutex::scoped_lock lk(m); + boost::thread thrd(boost::bind(&interruption_point_thread,&m,&failed)); + thrd.interrupt(); + lk.unlock(); + thrd.join(); + BOOST_CHECK(!failed); +} + +void test_thread_interrupts_at_interruption_point() +{ + timed_test(&do_test_thread_interrupts_at_interruption_point, 1); +} + +void disabled_interruption_point_thread(boost::mutex* m,bool* failed) +{ + boost::mutex::scoped_lock lk(*m); + boost::this_thread::disable_interruption dc; + boost::this_thread::interruption_point(); + *failed=false; +} + +void do_test_thread_no_interrupt_if_interrupts_disabled_at_interruption_point() +{ + boost::mutex m; + bool failed=true; + boost::mutex::scoped_lock lk(m); + boost::thread thrd(boost::bind(&disabled_interruption_point_thread,&m,&failed)); + thrd.interrupt(); + lk.unlock(); + thrd.join(); + BOOST_CHECK(!failed); +} + +void test_thread_no_interrupt_if_interrupts_disabled_at_interruption_point() +{ + timed_test(&do_test_thread_no_interrupt_if_interrupts_disabled_at_interruption_point, 1); +} + +struct non_copyable_functor: + boost::noncopyable +{ + unsigned value; + + non_copyable_functor(): + value(0) + {} + + void operator()() + { + value=999; + } +}; + +void do_test_creation_through_reference_wrapper() +{ + non_copyable_functor f; + + boost::thread thrd(boost::ref(f)); + thrd.join(); + BOOST_CHECK_EQUAL(f.value, 999u); +} + +void test_creation_through_reference_wrapper() +{ + timed_test(&do_test_creation_through_reference_wrapper, 1); +} + +struct long_running_thread +{ + boost::condition_variable cond; + boost::mutex mut; + bool done; + + long_running_thread(): + done(false) + {} + + void operator()() + { + boost::mutex::scoped_lock lk(mut); + while(!done) + { + cond.wait(lk); + } + } +}; + +void do_test_timed_join() +{ + long_running_thread f; + boost::thread thrd(boost::ref(f)); + BOOST_CHECK(thrd.joinable()); + boost::system_time xt=delay(3); + bool const joined=thrd.timed_join(xt); + BOOST_CHECK(in_range(boost::get_xtime(xt), 2)); + BOOST_CHECK(!joined); + BOOST_CHECK(thrd.joinable()); + { + boost::mutex::scoped_lock lk(f.mut); + f.done=true; + f.cond.notify_one(); + } + + xt=delay(3); + bool const joined2=thrd.timed_join(xt); + boost::system_time const now=boost::get_system_time(); + BOOST_CHECK(xt>now); + BOOST_CHECK(joined2); + BOOST_CHECK(!thrd.joinable()); +} + +void test_timed_join() +{ + timed_test(&do_test_timed_join, 10); +} + +void test_swap() +{ + boost::thread t(simple_thread); + boost::thread t2(simple_thread); + boost::thread::id id1=t.get_id(); + boost::thread::id id2=t2.get_id(); + + t.swap(t2); + BOOST_CHECK(t.get_id()==id2); + BOOST_CHECK(t2.get_id()==id1); + + swap(t,t2); + BOOST_CHECK(t.get_id()==id1); + BOOST_CHECK(t2.get_id()==id2); +} + + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: thread test suite"); + + test->add(BOOST_TEST_CASE(test_sleep)); + test->add(BOOST_TEST_CASE(test_creation)); + test->add(BOOST_TEST_CASE(test_id_comparison)); + test->add(BOOST_TEST_CASE(test_thread_interrupts_at_interruption_point)); + test->add(BOOST_TEST_CASE(test_thread_no_interrupt_if_interrupts_disabled_at_interruption_point)); + test->add(BOOST_TEST_CASE(test_creation_through_reference_wrapper)); + test->add(BOOST_TEST_CASE(test_timed_join)); + test->add(BOOST_TEST_CASE(test_swap)); + + return test; +} diff --git a/libs/thread/test/test_thread_exit.cpp b/libs/thread/test/test_thread_exit.cpp new file mode 100644 index 0000000000..a706cb3306 --- /dev/null +++ b/libs/thread/test/test_thread_exit.cpp @@ -0,0 +1,73 @@ +// (C) Copyright 2009 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 "boost/thread/thread.hpp" +#include "boost/thread/mutex.hpp" +#include "boost/thread/condition.hpp" +#include "boost/thread/future.hpp" +#include <utility> +#include <memory> +#include <string> + +#include <boost/test/unit_test.hpp> + +boost::thread::id exit_func_thread_id; + +void exit_func() +{ + exit_func_thread_id=boost::this_thread::get_id(); +} + +void tf1() +{ + boost::this_thread::at_thread_exit(exit_func); + BOOST_CHECK(exit_func_thread_id!=boost::this_thread::get_id()); +} + +void test_thread_exit_func_runs_when_thread_exits() +{ + exit_func_thread_id=boost::thread::id(); + boost::thread t(tf1); + boost::thread::id const t_id=t.get_id(); + t.join(); + BOOST_CHECK(exit_func_thread_id==t_id); +} + +struct fo +{ + void operator()() + { + exit_func_thread_id=boost::this_thread::get_id(); + } +}; + +void tf2() +{ + boost::this_thread::at_thread_exit(fo()); + BOOST_CHECK(exit_func_thread_id!=boost::this_thread::get_id()); +} + + +void test_can_use_function_object_for_exit_func() +{ + exit_func_thread_id=boost::thread::id(); + boost::thread t(tf2); + boost::thread::id const t_id=t.get_id(); + t.join(); + BOOST_CHECK(exit_func_thread_id==t_id); +} + + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: futures test suite"); + + test->add(BOOST_TEST_CASE(test_thread_exit_func_runs_when_thread_exits)); + test->add(BOOST_TEST_CASE(test_can_use_function_object_for_exit_func)); + + return test; +} diff --git a/libs/thread/test/test_thread_id.cpp b/libs/thread/test/test_thread_id.cpp new file mode 100644 index 0000000000..a20805d8c4 --- /dev/null +++ b/libs/thread/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 <boost/thread/thread.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/bind.hpp> + +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()>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()<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())); + + 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())); + + 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()<t2.get_id()) && (t2.get_id()<t3.get_id())) + { + BOOST_CHECK(t.get_id()<t3.get_id()); + } + else if((t.get_id()<t3.get_id()) && (t3.get_id()<t2.get_id())) + { + BOOST_CHECK(t.get_id()<t2.get_id()); + } + else if((t2.get_id()<t3.get_id()) && (t3.get_id()<t.get_id())) + { + BOOST_CHECK(t2.get_id()<t.get_id()); + } + else if((t2.get_id()<t.get_id()) && (t.get_id()<t3.get_id())) + { + BOOST_CHECK(t2.get_id()<t3.get_id()); + } + else if((t3.get_id()<t.get_id()) && (t.get_id()<t2.get_id())) + { + BOOST_CHECK(t3.get_id()<t2.get_id()); + } + else if((t3.get_id()<t2.get_id()) && (t2.get_id()<t.get_id())) + { + BOOST_CHECK(t3.get_id()<t.get_id()); + } + else + { + BOOST_CHECK(false); + } + + boost::thread::id default_id; + + BOOST_CHECK(default_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()); + + BOOST_CHECK(!(default_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::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::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/libs/thread/test/test_thread_launching.cpp b/libs/thread/test/test_thread_launching.cpp new file mode 100644 index 0000000000..714f33bf6a --- /dev/null +++ b/libs/thread/test/test_thread_launching.cpp @@ -0,0 +1,226 @@ +// Copyright (C) 2007-8 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#include <boost/thread/thread.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/ref.hpp> +#include <boost/utility.hpp> +#include <string> +#include <vector> + +bool normal_function_called=false; + +void normal_function() +{ + normal_function_called=true; +} + +void test_thread_function_no_arguments() +{ + boost::thread function(normal_function); + function.join(); + BOOST_CHECK(normal_function_called); +} + +int nfoa_res=0; + +void normal_function_one_arg(int i) +{ + nfoa_res=i; +} + +void test_thread_function_one_argument() +{ + boost::thread function(normal_function_one_arg,42); + function.join(); + BOOST_CHECK_EQUAL(42,nfoa_res); +} + +struct callable_no_args +{ + static bool called; + + void operator()() const + { + called=true; + } +}; + +bool callable_no_args::called=false; + +void test_thread_callable_object_no_arguments() +{ + callable_no_args func; + boost::thread callable(func); + callable.join(); + BOOST_CHECK(callable_no_args::called); +} + +struct callable_noncopyable_no_args: + boost::noncopyable +{ + static bool called; + + void operator()() const + { + called=true; + } +}; + +bool callable_noncopyable_no_args::called=false; + +void test_thread_callable_object_ref_no_arguments() +{ + callable_noncopyable_no_args func; + + boost::thread callable(boost::ref(func)); + callable.join(); + BOOST_CHECK(callable_noncopyable_no_args::called); +} + +struct callable_one_arg +{ + static bool called; + static int called_arg; + + void operator()(int arg) const + { + called=true; + called_arg=arg; + } +}; + +bool callable_one_arg::called=false; +int callable_one_arg::called_arg=0; + +void test_thread_callable_object_one_argument() +{ + callable_one_arg func; + boost::thread callable(func,42); + callable.join(); + BOOST_CHECK(callable_one_arg::called); + BOOST_CHECK_EQUAL(callable_one_arg::called_arg,42); +} + +struct callable_multiple_arg +{ + static bool called_two; + static int called_two_arg1; + static double called_two_arg2; + static bool called_three; + static std::string called_three_arg1; + static std::vector<int> called_three_arg2; + static int called_three_arg3; + + void operator()(int arg1,double arg2) const + { + called_two=true; + called_two_arg1=arg1; + called_two_arg2=arg2; + } + void operator()(std::string const& arg1,std::vector<int> const& arg2,int arg3) const + { + called_three=true; + called_three_arg1=arg1; + called_three_arg2=arg2; + called_three_arg3=arg3; + } +}; + +bool callable_multiple_arg::called_two=false; +bool callable_multiple_arg::called_three=false; +int callable_multiple_arg::called_two_arg1; +double callable_multiple_arg::called_two_arg2; +std::string callable_multiple_arg::called_three_arg1; +std::vector<int> callable_multiple_arg::called_three_arg2; +int callable_multiple_arg::called_three_arg3; + +void test_thread_callable_object_multiple_arguments() +{ + std::vector<int> x; + for(unsigned i=0;i<7;++i) + { + x.push_back(i*i); + } + + callable_multiple_arg func; + + boost::thread callable3(func,"hello",x,1.2); + callable3.join(); + BOOST_CHECK(callable_multiple_arg::called_three); + BOOST_CHECK_EQUAL(callable_multiple_arg::called_three_arg1,"hello"); + BOOST_CHECK_EQUAL(callable_multiple_arg::called_three_arg2.size(),x.size()); + for(unsigned j=0;j<x.size();++j) + { + BOOST_CHECK_EQUAL(callable_multiple_arg::called_three_arg2.at(j),x[j]); + } + + BOOST_CHECK_EQUAL(callable_multiple_arg::called_three_arg3,1); + + double const dbl=1.234; + + boost::thread callable2(func,19,dbl); + callable2.join(); + BOOST_CHECK(callable_multiple_arg::called_two); + BOOST_CHECK_EQUAL(callable_multiple_arg::called_two_arg1,19); + BOOST_CHECK_EQUAL(callable_multiple_arg::called_two_arg2,dbl); +} + +struct X +{ + bool function_called; + int arg_value; + + X(): + function_called(false), + arg_value(0) + {} + + + void f0() + { + function_called=true; + } + + void f1(int i) + { + arg_value=i; + } + +}; + +void test_thread_member_function_no_arguments() +{ + X x; + + boost::thread function(&X::f0,&x); + function.join(); + BOOST_CHECK(x.function_called); +} + + +void test_thread_member_function_one_argument() +{ + X x; + boost::thread function(&X::f1,&x,42); + function.join(); + BOOST_CHECK_EQUAL(42,x.arg_value); +} + + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: thread launching test suite"); + + test->add(BOOST_TEST_CASE(test_thread_function_no_arguments)); + test->add(BOOST_TEST_CASE(test_thread_function_one_argument)); + test->add(BOOST_TEST_CASE(test_thread_callable_object_no_arguments)); + test->add(BOOST_TEST_CASE(test_thread_callable_object_ref_no_arguments)); + test->add(BOOST_TEST_CASE(test_thread_callable_object_one_argument)); + test->add(BOOST_TEST_CASE(test_thread_callable_object_multiple_arguments)); + test->add(BOOST_TEST_CASE(test_thread_member_function_no_arguments)); + test->add(BOOST_TEST_CASE(test_thread_member_function_one_argument)); + return test; +} diff --git a/libs/thread/test/test_thread_mf.cpp b/libs/thread/test/test_thread_mf.cpp new file mode 100644 index 0000000000..aa5a5dc1e3 --- /dev/null +++ b/libs/thread/test/test_thread_mf.cpp @@ -0,0 +1,134 @@ +// +// Copyright (C) 2008 Peter Dimov +// +// 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 <boost/thread/thread.hpp> +#include <boost/detail/lightweight_test.hpp> + +struct X +{ + mutable unsigned int hash; + + X(): hash(0) {} + + int f0() { f1(17); return 0; } + int g0() const { g1(17); return 0; } + + int f1(int a1) { hash = (hash * 17041 + a1) % 32768; return 0; } + int g1(int a1) const { hash = (hash * 17041 + a1 * 2) % 32768; return 0; } + + int f2(int a1, int a2) { f1(a1); f1(a2); return 0; } + int g2(int a1, int a2) const { g1(a1); g1(a2); return 0; } + + int f3(int a1, int a2, int a3) { f2(a1, a2); f1(a3); return 0; } + int g3(int a1, int a2, int a3) const { g2(a1, a2); g1(a3); return 0; } + + int f4(int a1, int a2, int a3, int a4) { f3(a1, a2, a3); f1(a4); return 0; } + int g4(int a1, int a2, int a3, int a4) const { g3(a1, a2, a3); g1(a4); return 0; } + + int f5(int a1, int a2, int a3, int a4, int a5) { f4(a1, a2, a3, a4); f1(a5); return 0; } + int g5(int a1, int a2, int a3, int a4, int a5) const { g4(a1, a2, a3, a4); g1(a5); return 0; } + + int f6(int a1, int a2, int a3, int a4, int a5, int a6) { f5(a1, a2, a3, a4, a5); f1(a6); return 0; } + int g6(int a1, int a2, int a3, int a4, int a5, int a6) const { g5(a1, a2, a3, a4, a5); g1(a6); return 0; } + + int f7(int a1, int a2, int a3, int a4, int a5, int a6, int a7) { f6(a1, a2, a3, a4, a5, a6); f1(a7); return 0; } + int g7(int a1, int a2, int a3, int a4, int a5, int a6, int a7) const { g6(a1, a2, a3, a4, a5, a6); g1(a7); return 0; } + + int f8(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) { f7(a1, a2, a3, a4, a5, a6, a7); f1(a8); return 0; } + int g8(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) const { g7(a1, a2, a3, a4, a5, a6, a7); g1(a8); return 0; } +}; + +int main() +{ + X x; + + // 0 + + boost::thread( &X::f0, &x ).join(); + boost::thread( &X::f0, boost::ref(x) ).join(); + + boost::thread( &X::g0, &x ).join(); + boost::thread( &X::g0, x ).join(); + boost::thread( &X::g0, boost::ref(x) ).join(); + + // 1 + + boost::thread( &X::f1, &x, 1 ).join(); + boost::thread( &X::f1, boost::ref(x), 1 ).join(); + + boost::thread( &X::g1, &x, 1 ).join(); + boost::thread( &X::g1, x, 1 ).join(); + boost::thread( &X::g1, boost::ref(x), 1 ).join(); + + // 2 + + boost::thread( &X::f2, &x, 1, 2 ).join(); + boost::thread( &X::f2, boost::ref(x), 1, 2 ).join(); + + boost::thread( &X::g2, &x, 1, 2 ).join(); + boost::thread( &X::g2, x, 1, 2 ).join(); + boost::thread( &X::g2, boost::ref(x), 1, 2 ).join(); + + // 3 + + boost::thread( &X::f3, &x, 1, 2, 3 ).join(); + boost::thread( &X::f3, boost::ref(x), 1, 2, 3 ).join(); + + boost::thread( &X::g3, &x, 1, 2, 3 ).join(); + boost::thread( &X::g3, x, 1, 2, 3 ).join(); + boost::thread( &X::g3, boost::ref(x), 1, 2, 3 ).join(); + + // 4 + + boost::thread( &X::f4, &x, 1, 2, 3, 4 ).join(); + boost::thread( &X::f4, boost::ref(x), 1, 2, 3, 4 ).join(); + + boost::thread( &X::g4, &x, 1, 2, 3, 4 ).join(); + boost::thread( &X::g4, x, 1, 2, 3, 4 ).join(); + boost::thread( &X::g4, boost::ref(x), 1, 2, 3, 4 ).join(); + + // 5 + + boost::thread( &X::f5, &x, 1, 2, 3, 4, 5 ).join(); + boost::thread( &X::f5, boost::ref(x), 1, 2, 3, 4, 5 ).join(); + + boost::thread( &X::g5, &x, 1, 2, 3, 4, 5 ).join(); + boost::thread( &X::g5, x, 1, 2, 3, 4, 5 ).join(); + boost::thread( &X::g5, boost::ref(x), 1, 2, 3, 4, 5 ).join(); + + // 6 + + boost::thread( &X::f6, &x, 1, 2, 3, 4, 5, 6 ).join(); + boost::thread( &X::f6, boost::ref(x), 1, 2, 3, 4, 5, 6 ).join(); + + boost::thread( &X::g6, &x, 1, 2, 3, 4, 5, 6 ).join(); + boost::thread( &X::g6, x, 1, 2, 3, 4, 5, 6 ).join(); + boost::thread( &X::g6, boost::ref(x), 1, 2, 3, 4, 5, 6 ).join(); + + // 7 + + boost::thread( &X::f7, &x, 1, 2, 3, 4, 5, 6, 7).join(); + boost::thread( &X::f7, boost::ref(x), 1, 2, 3, 4, 5, 6, 7).join(); + + boost::thread( &X::g7, &x, 1, 2, 3, 4, 5, 6, 7).join(); + boost::thread( &X::g7, x, 1, 2, 3, 4, 5, 6, 7).join(); + boost::thread( &X::g7, boost::ref(x), 1, 2, 3, 4, 5, 6, 7).join(); + + // 8 + + boost::thread( &X::f8, &x, 1, 2, 3, 4, 5, 6, 7, 8 ).join(); + boost::thread( &X::f8, boost::ref(x), 1, 2, 3, 4, 5, 6, 7, 8 ).join(); + + boost::thread( &X::g8, &x, 1, 2, 3, 4, 5, 6, 7, 8 ).join(); + boost::thread( &X::g8, x, 1, 2, 3, 4, 5, 6, 7, 8 ).join(); + boost::thread( &X::g8, boost::ref(x), 1, 2, 3, 4, 5, 6, 7, 8 ).join(); + + BOOST_TEST( x.hash == 23558 ); + + return boost::report_errors(); +} diff --git a/libs/thread/test/test_thread_move.cpp b/libs/thread/test/test_thread_move.cpp new file mode 100644 index 0000000000..f644e1ebd8 --- /dev/null +++ b/libs/thread/test/test_thread_move.cpp @@ -0,0 +1,56 @@ +// Copyright (C) 2007-9 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 <boost/thread/thread.hpp> +#include <boost/test/unit_test.hpp> + +void do_nothing(boost::thread::id* my_id) +{ + *my_id=boost::this_thread::get_id(); +} + +void test_move_on_construction() +{ + boost::thread::id the_id; + boost::thread x=boost::thread(do_nothing,&the_id); + boost::thread::id x_id=x.get_id(); + x.join(); + BOOST_CHECK_EQUAL(the_id,x_id); +} + +boost::thread make_thread(boost::thread::id* the_id) +{ + return boost::thread(do_nothing,the_id); +} + +void test_move_from_function_return() +{ + boost::thread::id the_id; + boost::thread x=make_thread(&the_id); + boost::thread::id x_id=x.get_id(); + x.join(); + BOOST_CHECK_EQUAL(the_id,x_id); +} + +void test_move_assign() +{ + boost::thread::id the_id; + boost::thread x(do_nothing,&the_id); + boost::thread y; + y=boost::move(x); + boost::thread::id y_id=y.get_id(); + y.join(); + BOOST_CHECK_EQUAL(the_id,y_id); +} + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::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)); + test->add(BOOST_TEST_CASE(test_move_assign)); + return test; +} diff --git a/libs/thread/test/test_thread_move_return.cpp b/libs/thread/test/test_thread_move_return.cpp new file mode 100644 index 0000000000..51f0b3faee --- /dev/null +++ b/libs/thread/test/test_thread_move_return.cpp @@ -0,0 +1,35 @@ +// Copyright (C) 2009 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 <boost/thread/thread.hpp> +#include <boost/test/unit_test.hpp> + +void do_nothing(boost::thread::id* my_id) +{ + *my_id=boost::this_thread::get_id(); +} + +boost::thread make_thread_move_return(boost::thread::id* the_id) +{ + boost::thread t(do_nothing,the_id); + return boost::move(t); +} + +void test_move_from_function_move_return() +{ + boost::thread::id the_id; + boost::thread x=make_thread_move_return(&the_id); + boost::thread::id x_id=x.get_id(); + x.join(); + BOOST_CHECK_EQUAL(the_id,x_id); +} + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: thread move test suite"); + + test->add(BOOST_TEST_CASE(test_move_from_function_move_return)); + return test; +} diff --git a/libs/thread/test/test_thread_return_local.cpp b/libs/thread/test/test_thread_return_local.cpp new file mode 100644 index 0000000000..be2aec5137 --- /dev/null +++ b/libs/thread/test/test_thread_return_local.cpp @@ -0,0 +1,35 @@ +// Copyright (C) 2009 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 <boost/thread/thread.hpp> +#include <boost/test/unit_test.hpp> + +void do_nothing(boost::thread::id* my_id) +{ + *my_id=boost::this_thread::get_id(); +} + +boost::thread make_thread_return_local(boost::thread::id* the_id) +{ + boost::thread t(do_nothing,the_id); + return t; +} + +void test_move_from_function_return_local() +{ + boost::thread::id the_id; + boost::thread x=make_thread_return_local(&the_id); + boost::thread::id x_id=x.get_id(); + x.join(); + BOOST_CHECK_EQUAL(the_id,x_id); +} + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: thread move test suite"); + + test->add(BOOST_TEST_CASE(test_move_from_function_return_local)); + return test; +} diff --git a/libs/thread/test/test_tss.cpp b/libs/thread/test/test_tss.cpp new file mode 100644 index 0000000000..894875fe6c --- /dev/null +++ b/libs/thread/test/test_tss.cpp @@ -0,0 +1,359 @@ +// 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) + +#include <boost/thread/detail/config.hpp> + +#include <boost/thread/tss.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> + +#include <boost/test/unit_test.hpp> + +#include <libs/thread/test/util.inl> + +#include <iostream> + +#if defined(BOOST_HAS_WINTHREADS) + #define WIN32_LEAN_AND_MEAN + #include <windows.h> +#endif + +boost::mutex check_mutex; +boost::mutex tss_mutex; +int tss_instances = 0; +int tss_total = 0; + +struct tss_value_t +{ + tss_value_t() + { + boost::mutex::scoped_lock lock(tss_mutex); + ++tss_instances; + ++tss_total; + value = 0; + } + ~tss_value_t() + { + boost::mutex::scoped_lock lock(tss_mutex); + --tss_instances; + } + int value; +}; + +boost::thread_specific_ptr<tss_value_t> tss_value; + +void test_tss_thread() +{ + tss_value.reset(new tss_value_t()); + for (int i=0; i<1000; ++i) + { + int& n = tss_value->value; + // Don't call BOOST_CHECK_EQUAL directly, as it doesn't appear to + // be thread safe. Must evaluate further. + if (n != i) + { + boost::mutex::scoped_lock lock(check_mutex); + BOOST_CHECK_EQUAL(n, i); + } + ++n; + } +} + +#if defined(BOOST_THREAD_PLATFORM_WIN32) + typedef HANDLE native_thread_t; + + DWORD WINAPI test_tss_thread_native(LPVOID /*lpParameter*/) + { + test_tss_thread(); + return 0; + } + + native_thread_t create_native_thread(void) + { + native_thread_t const res=CreateThread( + 0, //security attributes (0 = not inheritable) + 0, //stack size (0 = default) + &test_tss_thread_native, //function to execute + 0, //parameter to pass to function + 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) + { + DWORD res = WaitForSingleObject(thread, INFINITE); + BOOST_CHECK(res == WAIT_OBJECT_0); + + res = CloseHandle(thread); + BOOST_CHECK(SUCCEEDED(res)); + } +#elif defined(BOOST_THREAD_PLATFORM_PTHREAD) + typedef pthread_t native_thread_t; + +extern "C" +{ + void* test_tss_thread_native(void* lpParameter) + { + test_tss_thread(); + return 0; + } +} + + native_thread_t create_native_thread() + { + native_thread_t thread_handle; + + int const res = pthread_create(&thread_handle, 0, &test_tss_thread_native, 0); + BOOST_CHECK(!res); + return thread_handle; + } + + void join_native_thread(native_thread_t thread) + { + void* result=0; + int const res=pthread_join(thread,&result); + BOOST_CHECK(!res); + } +#endif + +void do_test_tss() +{ + tss_instances = 0; + tss_total = 0; + + const int NUMTHREADS=5; + boost::thread_group threads; + try + { + for (int i=0; i<NUMTHREADS; ++i) + threads.create_thread(&test_tss_thread); + threads.join_all(); + } + catch(...) + { + threads.interrupt_all(); + threads.join_all(); + throw; + } + + + std::cout + << "tss_instances = " << tss_instances + << "; tss_total = " << tss_total + << "\n"; + std::cout.flush(); + + BOOST_CHECK_EQUAL(tss_instances, 0); + BOOST_CHECK_EQUAL(tss_total, 5); + + tss_instances = 0; + tss_total = 0; + + native_thread_t thread1 = create_native_thread(); + native_thread_t thread2 = create_native_thread(); + native_thread_t thread3 = create_native_thread(); + native_thread_t thread4 = create_native_thread(); + native_thread_t thread5 = create_native_thread(); + + join_native_thread(thread5); + join_native_thread(thread4); + join_native_thread(thread3); + join_native_thread(thread2); + join_native_thread(thread1); + + std::cout + << "tss_instances = " << tss_instances + << "; tss_total = " << tss_total + << "\n"; + std::cout.flush(); + + // The following is not really an error. TSS cleanup support still is available for boost threads. + // Also this usually will be triggered only when bound to the static version of thread lib. + // 2006-10-02 Roland Schwarz + //BOOST_CHECK_EQUAL(tss_instances, 0); + BOOST_CHECK_MESSAGE(tss_instances == 0, "Support of automatic tss cleanup for native threading API not available"); + BOOST_CHECK_EQUAL(tss_total, 5); +} + +void test_tss() +{ + timed_test(&do_test_tss, 2); +} + +bool tss_cleanup_called=false; + +struct Dummy +{}; + +void tss_custom_cleanup(Dummy* d) +{ + delete d; + tss_cleanup_called=true; +} + +boost::thread_specific_ptr<Dummy> tss_with_cleanup(tss_custom_cleanup); + +void tss_thread_with_custom_cleanup() +{ + tss_with_cleanup.reset(new Dummy); +} + +void do_test_tss_with_custom_cleanup() +{ + boost::thread t(tss_thread_with_custom_cleanup); + try + { + t.join(); + } + catch(...) + { + t.interrupt(); + t.join(); + throw; + } + + BOOST_CHECK(tss_cleanup_called); +} + + +void test_tss_with_custom_cleanup() +{ + timed_test(&do_test_tss_with_custom_cleanup, 2); +} + +Dummy* tss_object=new Dummy; + +void tss_thread_with_custom_cleanup_and_release() +{ + tss_with_cleanup.reset(tss_object); + tss_with_cleanup.release(); +} + +void do_test_tss_does_no_cleanup_after_release() +{ + tss_cleanup_called=false; + boost::thread t(tss_thread_with_custom_cleanup_and_release); + try + { + t.join(); + } + catch(...) + { + t.interrupt(); + t.join(); + throw; + } + + BOOST_CHECK(!tss_cleanup_called); + if(!tss_cleanup_called) + { + delete tss_object; + } +} + +struct dummy_class_tracks_deletions +{ + static unsigned deletions; + + ~dummy_class_tracks_deletions() + { + ++deletions; + } + +}; + +unsigned dummy_class_tracks_deletions::deletions=0; + +boost::thread_specific_ptr<dummy_class_tracks_deletions> tss_with_null_cleanup(NULL); + +void tss_thread_with_null_cleanup(dummy_class_tracks_deletions* delete_tracker) +{ + tss_with_null_cleanup.reset(delete_tracker); +} + +void do_test_tss_does_no_cleanup_with_null_cleanup_function() +{ + dummy_class_tracks_deletions* delete_tracker=new dummy_class_tracks_deletions; + boost::thread t(tss_thread_with_null_cleanup,delete_tracker); + try + { + t.join(); + } + catch(...) + { + t.interrupt(); + t.join(); + throw; + } + + BOOST_CHECK(!dummy_class_tracks_deletions::deletions); + if(!dummy_class_tracks_deletions::deletions) + { + delete delete_tracker; + } +} + +void test_tss_does_no_cleanup_after_release() +{ + timed_test(&do_test_tss_does_no_cleanup_after_release, 2); +} + +void test_tss_does_no_cleanup_with_null_cleanup_function() +{ + timed_test(&do_test_tss_does_no_cleanup_with_null_cleanup_function, 2); +} + +void thread_with_local_tss_ptr() +{ + { + boost::thread_specific_ptr<Dummy> local_tss(tss_custom_cleanup); + + local_tss.reset(new Dummy); + } + BOOST_CHECK(tss_cleanup_called); + tss_cleanup_called=false; +} + + +void test_tss_does_not_call_cleanup_after_ptr_destroyed() +{ + boost::thread t(thread_with_local_tss_ptr); + t.join(); + BOOST_CHECK(!tss_cleanup_called); +} + +void test_tss_cleanup_not_called_for_null_pointer() +{ + boost::thread_specific_ptr<Dummy> local_tss(tss_custom_cleanup); + local_tss.reset(new Dummy); + tss_cleanup_called=false; + local_tss.reset(0); + BOOST_CHECK(tss_cleanup_called); + tss_cleanup_called=false; + local_tss.reset(new Dummy); + BOOST_CHECK(!tss_cleanup_called); +} + + + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: tss test suite"); + + test->add(BOOST_TEST_CASE(test_tss)); + test->add(BOOST_TEST_CASE(test_tss_with_custom_cleanup)); + test->add(BOOST_TEST_CASE(test_tss_does_no_cleanup_after_release)); + test->add(BOOST_TEST_CASE(test_tss_does_no_cleanup_with_null_cleanup_function)); + test->add(BOOST_TEST_CASE(test_tss_does_not_call_cleanup_after_ptr_destroyed)); + test->add(BOOST_TEST_CASE(test_tss_cleanup_not_called_for_null_pointer)); + + return test; +} diff --git a/libs/thread/test/test_xtime.cpp b/libs/thread/test/test_xtime.cpp new file mode 100644 index 0000000000..0dff620094 --- /dev/null +++ b/libs/thread/test/test_xtime.cpp @@ -0,0 +1,109 @@ +// Copyright (C) 2001-2003 +// William E. Kempf +// Copyright (C) 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) + +#include <boost/thread/detail/config.hpp> + +#include <boost/thread/xtime.hpp> + +#include <boost/test/unit_test.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/condition.hpp> + +void test_xtime_cmp() +{ + boost::xtime xt1, xt2, cur; + BOOST_CHECK_EQUAL( + boost::xtime_get(&cur, boost::TIME_UTC), + static_cast<int>(boost::TIME_UTC)); + + xt1 = xt2 = cur; + xt1.nsec -= 1; + xt2.nsec += 1; + + BOOST_CHECK(boost::xtime_cmp(xt1, cur) < 0); + BOOST_CHECK(boost::xtime_cmp(xt2, cur) > 0); + BOOST_CHECK(boost::xtime_cmp(cur, cur) == 0); + + xt1 = xt2 = cur; + xt1.sec -= 1; + xt2.sec += 1; + + BOOST_CHECK(boost::xtime_cmp(xt1, cur) < 0); + BOOST_CHECK(boost::xtime_cmp(xt2, cur) > 0); + BOOST_CHECK(boost::xtime_cmp(cur, cur) == 0); +} + +void test_xtime_get() +{ + boost::xtime orig, cur, old; + BOOST_CHECK_EQUAL( + boost::xtime_get(&orig, + boost::TIME_UTC), static_cast<int>(boost::TIME_UTC)); + old = orig; + + for (int x=0; x < 100; ++x) + { + BOOST_CHECK_EQUAL( + boost::xtime_get(&cur, boost::TIME_UTC), + static_cast<int>(boost::TIME_UTC)); + BOOST_CHECK(boost::xtime_cmp(cur, orig) >= 0); + BOOST_CHECK(boost::xtime_cmp(cur, old) >= 0); + old = cur; + } +} + +void test_xtime_mutex_backwards_compatibility() +{ + boost::timed_mutex m; + BOOST_CHECK(m.timed_lock(boost::get_xtime(boost::get_system_time()+boost::posix_time::milliseconds(10)))); + m.unlock(); + boost::timed_mutex::scoped_timed_lock lk(m,boost::get_xtime(boost::get_system_time()+boost::posix_time::milliseconds(10))); + BOOST_CHECK(lk.owns_lock()); + if(lk.owns_lock()) + { + lk.unlock(); + } + BOOST_CHECK(lk.timed_lock(boost::get_xtime(boost::get_system_time()+boost::posix_time::milliseconds(10)))); + if(lk.owns_lock()) + { + lk.unlock(); + } +} + +bool predicate() +{ + return false; +} + + +void test_xtime_condvar_backwards_compatibility() +{ + boost::condition_variable cond; + boost::condition_variable_any cond_any; + boost::mutex m; + + boost::mutex::scoped_lock lk(m); + cond.timed_wait(lk,boost::get_xtime(boost::get_system_time()+boost::posix_time::milliseconds(10))); + cond.timed_wait(lk,boost::get_xtime(boost::get_system_time()+boost::posix_time::milliseconds(10)),predicate); + cond_any.timed_wait(lk,boost::get_xtime(boost::get_system_time()+boost::posix_time::milliseconds(10))); + cond_any.timed_wait(lk,boost::get_xtime(boost::get_system_time()+boost::posix_time::milliseconds(10)),predicate); +} + + + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: xtime test suite"); + + test->add(BOOST_TEST_CASE(&test_xtime_cmp)); + test->add(BOOST_TEST_CASE(&test_xtime_get)); + test->add(BOOST_TEST_CASE(&test_xtime_mutex_backwards_compatibility)); + test->add(BOOST_TEST_CASE(&test_xtime_condvar_backwards_compatibility)); + + return test; +} diff --git a/libs/thread/test/util.inl b/libs/thread/test/util.inl new file mode 100644 index 0000000000..5c761d506d --- /dev/null +++ b/libs/thread/test/util.inl @@ -0,0 +1,183 @@ +// Copyright (C) 2001-2003 +// William E. Kempf +// Copyright (C) 2007-8 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#if !defined(UTIL_INL_WEK01242003) +#define UTIL_INL_WEK01242003 + +#include <boost/thread/xtime.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/condition.hpp> +#include <boost/thread/thread.hpp> + +#ifndef DEFAULT_EXECUTION_MONITOR_TYPE +# define DEFAULT_EXECUTION_MONITOR_TYPE execution_monitor::use_condition +#endif + +// boostinspect:nounnamed + +namespace +{ +inline boost::xtime delay(int secs, int msecs=0, int nsecs=0) +{ + const int MILLISECONDS_PER_SECOND = 1000; + const int NANOSECONDS_PER_SECOND = 1000000000; + const int NANOSECONDS_PER_MILLISECOND = 1000000; + + boost::xtime xt; + if (boost::TIME_UTC != boost::xtime_get (&xt, boost::TIME_UTC)) + BOOST_ERROR ("boost::xtime_get != boost::TIME_UTC"); + + nsecs += xt.nsec; + msecs += nsecs / NANOSECONDS_PER_MILLISECOND; + secs += msecs / MILLISECONDS_PER_SECOND; + nsecs += (msecs % MILLISECONDS_PER_SECOND) * NANOSECONDS_PER_MILLISECOND; + xt.nsec = nsecs % NANOSECONDS_PER_SECOND; + xt.sec += secs + (nsecs / NANOSECONDS_PER_SECOND); + + return xt; +} + +inline bool in_range(const boost::xtime& xt, int secs=1) +{ + boost::xtime min = delay(-secs); + boost::xtime max = delay(0); + return (boost::xtime_cmp(xt, min) >= 0) && + (boost::xtime_cmp(xt, max) <= 0); +} + +class execution_monitor +{ +public: + enum wait_type { use_sleep_only, use_mutex, use_condition }; + + execution_monitor(wait_type type, int secs) + : done(false), type(type), secs(secs) { } + void start() + { + if (type != use_sleep_only) { + boost::mutex::scoped_lock lock(mutex); done = false; + } else { + done = false; + } + } + void finish() + { + if (type != use_sleep_only) { + boost::mutex::scoped_lock lock(mutex); + done = true; + if (type == use_condition) + cond.notify_one(); + } else { + done = true; + } + } + bool wait() + { + boost::xtime xt = delay(secs); + if (type != use_condition) + boost::thread::sleep(xt); + if (type != use_sleep_only) { + boost::mutex::scoped_lock lock(mutex); + while (type == use_condition && !done) { + if (!cond.timed_wait(lock, xt)) + break; + } + return done; + } + return done; + } + +private: + boost::mutex mutex; + boost::condition cond; + bool done; + wait_type type; + int secs; +}; + +template <typename F> +class indirect_adapter +{ +public: + indirect_adapter(F func, execution_monitor& monitor) + : func(func), monitor(monitor) { } + void operator()() const + { + try + { + boost::thread thrd(func); + thrd.join(); + } + catch (...) + { + monitor.finish(); + throw; + } + monitor.finish(); + } + +private: + F func; + execution_monitor& monitor; + void operator=(indirect_adapter&); +}; + +template <typename F> +void timed_test(F func, int secs, + execution_monitor::wait_type type=DEFAULT_EXECUTION_MONITOR_TYPE) +{ + execution_monitor monitor(type, secs); + indirect_adapter<F> ifunc(func, monitor); + monitor.start(); + boost::thread thrd(ifunc); + BOOST_REQUIRE_MESSAGE(monitor.wait(), + "Timed test didn't complete in time, possible deadlock."); +} + +template <typename F, typename T> +class thread_binder +{ +public: + thread_binder(const F& func, const T& param) + : func(func), param(param) { } + void operator()() const { func(param); } + +private: + F func; + T param; +}; + +template <typename F, typename T> +thread_binder<F, T> bind(const F& func, const T& param) +{ + return thread_binder<F, T>(func, param); +} + +template <typename R, typename T> +class thread_member_binder +{ +public: + thread_member_binder(R (T::*func)(), T& param) + : func(func), param(param) { } + void operator()() const { (param.*func)(); } + +private: + void operator=(thread_member_binder&); + + R (T::*func)(); + T& param; +}; + + +template <typename R, typename T> +thread_member_binder<R, T> bind(R (T::*func)(), T& param) +{ + return thread_member_binder<R, T>(func, param); +} +} // namespace + +#endif |