blob: 74995bd677fa856b5063b49eabccd92f0fa414c5 [file] [log] [blame]
// Copyright (C) 2001-2003
// William E. Kempf
//
// Copyright Frank Mori Hess 2009
//
// Use, modification and
// distribution is subject to the Boost Software License, Version
// 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// This is a simplified/modified version of libs/thread/test/test_mutex.cpp
// added to test boost::signals2::mutex.
// For more information, see http://www.boost.org
// note boost/test/minimal.hpp can cause windows.h to get included, which
// can screw up our checks of _WIN32_WINNT if it is included
// after boost/signals2/mutex.hpp. Frank Hess 2009-03-07.
#include <boost/test/minimal.hpp>
#include <boost/bind.hpp>
#include <boost/signals2/dummy_mutex.hpp>
#include <boost/signals2/mutex.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/thread_time.hpp>
#include <boost/thread/condition.hpp>
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::posix_time::time_duration timeout = boost::posix_time::seconds(secs);
if (type != use_condition)
boost::this_thread::sleep(timeout);
if (type != use_sleep_only) {
boost::mutex::scoped_lock lock(mutex);
while (type == use_condition && !done) {
if (!cond.timed_wait(lock, timeout))
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 = execution_monitor::use_sleep_only)
{
execution_monitor monitor(type, secs);
indirect_adapter<F> ifunc(func, monitor);
monitor.start();
boost::thread thrd(ifunc);
BOOST_REQUIRE(monitor.wait()); // Timed test didn't complete in time, possible deadlock
}
template <typename M>
struct test_lock
{
typedef M mutex_type;
typedef typename boost::unique_lock<M> 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 a fast time out.
boost::posix_time::time_duration timeout = 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));
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 boost::unique_lock<M> lock_type;
void operator()()
{
mutex_type mutex;
boost::condition condition;
// Test the lock's constructors.
{
lock_type lock(mutex, boost::try_to_lock);
BOOST_CHECK(lock ? true : false);
}
{
lock_type lock(mutex, boost::defer_lock);
BOOST_CHECK(!lock);
}
lock_type lock(mutex, boost::try_to_lock);
BOOST_CHECK(lock ? true : false);
// Construct a fast time out.
boost::posix_time::time_duration timeout = 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));
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_exclusion
{
typedef boost::unique_lock<Mutex> Lock;
Mutex m;
boost::mutex done_mutex;
bool done;
bool locked;
boost::condition_variable done_cond;
test_lock_exclusion():
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;
}
typedef test_lock_exclusion<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(1),
boost::bind(&this_type::is_done,this)));
}
lock.unlock();
{
boost::mutex::scoped_lock lk(done_mutex);
BOOST_CHECK(done_cond.timed_wait(lk, boost::posix_time::seconds(1),
boost::bind(&this_type::is_done,this)));
}
t.join();
BOOST_CHECK(locked);
}
catch(...)
{
lock.unlock();
t.join();
throw;
}
}
void operator()()
{
do_test(&this_type::locking_thread);
}
};
void do_test_mutex()
{
test_lock<boost::signals2::mutex>()();
// try_lock not supported on old versions of windows
#if !defined(BOOST_HAS_WINTHREADS) || (defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400))
test_trylock<boost::signals2::mutex>()();
#endif
test_lock_exclusion<boost::signals2::mutex>()();
}
void test_mutex()
{
timed_test(&do_test_mutex, 3);
}
void do_test_dummy_mutex()
{
test_lock<boost::signals2::dummy_mutex>()();
test_trylock<boost::signals2::dummy_mutex>()();
}
void test_dummy_mutex()
{
timed_test(&do_test_dummy_mutex, 2);
}
int test_main(int, char*[])
{
test_mutex();
test_dummy_mutex();
return 0;
}