// Copyright (C) 2010 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #include #include #include #include #include #include #include "tester.h" namespace { using namespace test; using namespace dlib; using namespace std; logger dlog("test.read_write_mutex"); class read_write_mutex_tester : public tester, multithreaded_object { public: read_write_mutex_tester ( ) : tester ("test_read_write_mutex", "Runs tests on the read_write_mutex component.") { register_thread(*this, &read_write_mutex_tester::thread_write); register_thread(*this, &read_write_mutex_tester::thread_write); register_thread(*this, &read_write_mutex_tester::thread_write); register_thread(*this, &read_write_mutex_tester::thread_readonly); register_thread(*this, &read_write_mutex_tester::thread_readonly); register_thread(*this, &read_write_mutex_tester::thread_readonly); register_thread(*this, &read_write_mutex_tester::thread_readonly2); register_thread(*this, &read_write_mutex_tester::thread_readonly2); register_thread(*this, &read_write_mutex_tester::thread_readonly2); } read_write_mutex m; dlib::mutex mut; int num_write; int num_read; int max_read; bool failure; void thread_write () { // do this so that the readonly threads can get into their loops first. This way // we can see if the mutex lets many readers into their area dlib::sleep(250); for (int i = 0; i < 6; ++i) { auto_mutex lock(m); mut.lock(); ++num_write; mut.unlock(); // only one write thread should ever be active at once if (num_write != 1) { failure = true; dlog << LERROR << "1"; } dlib::sleep(300); // only one write thread should ever be active at once if (num_write != 1) { failure = true; dlog << LERROR << "2"; } mut.lock(); --num_write; mut.unlock(); print_spinner(); } dlog << LINFO << "exit thread_write()"; } void do_readonly_stuff() { mut.lock(); ++num_read; max_read = max(num_read, max_read); mut.unlock(); if (num_write != 0) { failure = true; dlog << LERROR << "3"; } dlib::sleep(300); if (num_write != 0) { failure = true; dlog << LERROR << "4"; } mut.lock(); max_read = max(num_read, max_read); --num_read; mut.unlock(); print_spinner(); } void thread_readonly () { for (int i = 0; i < 6; ++i) { auto_mutex_readonly lock(m); DLIB_TEST(lock.has_read_lock()); DLIB_TEST(!lock.has_write_lock()); do_readonly_stuff(); lock.lock_readonly(); DLIB_TEST(lock.has_read_lock()); DLIB_TEST(!lock.has_write_lock()); lock.unlock(); DLIB_TEST(!lock.has_read_lock()); DLIB_TEST(!lock.has_write_lock()); lock.lock_readonly(); DLIB_TEST(lock.has_read_lock()); DLIB_TEST(!lock.has_write_lock()); lock.lock_write(); DLIB_TEST(!lock.has_read_lock()); DLIB_TEST(lock.has_write_lock()); lock.lock_write(); DLIB_TEST(!lock.has_read_lock()); DLIB_TEST(lock.has_write_lock()); } dlog << LINFO << "exit thread_readonly()"; } void thread_readonly2 () { for (int i = 0; i < 6; ++i) { m.lock_readonly(); auto_unlock_readonly unlock(m); do_readonly_stuff(); } dlog << LINFO << "exit thread_readonly2()"; } void perform_test ( ) { num_write = 0; num_read = 0; max_read = 0; failure = false; // doing this big block of weird stuff should have no effect. { m.unlock(); m.lock_readonly(); m.lock_readonly(); m.unlock(); m.unlock_readonly(); m.unlock(); m.unlock_readonly(); m.unlock(); m.unlock_readonly(); m.lock(); m.unlock_readonly(); m.unlock_readonly(); m.unlock(); } // start up our testing threads start(); // wait for the threads to finish wait(); DLIB_TEST(failure == false); DLIB_TEST_MSG(max_read == 6, "max_read: "<< max_read); } } a; }