From: Michal 'vorner' Vaner Date: Mon, 3 Sep 2012 13:10:06 +0000 (+0200) Subject: [2202] Tests for mutex X-Git-Tag: trac2402_base~79^2~23 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ff7d2f5f77fab130de8db0d59ce48a483a0a8b45;p=thirdparty%2Fkea.git [2202] Tests for mutex --- diff --git a/src/lib/util/threads/tests/Makefile.am b/src/lib/util/threads/tests/Makefile.am index 435e8e87e4..e86aedccc3 100644 --- a/src/lib/util/threads/tests/Makefile.am +++ b/src/lib/util/threads/tests/Makefile.am @@ -22,6 +22,7 @@ if HAVE_GTEST TESTS += run_unittests run_unittests_SOURCES = run_unittests.cc run_unittests_SOURCES += thread_unittest.cc +run_unittests_SOURCES += lock_unittest.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/util/threads/tests/lock_unittest.cc b/src/lib/util/threads/tests/lock_unittest.cc new file mode 100644 index 0000000000..2d90ee7301 --- /dev/null +++ b/src/lib/util/threads/tests/lock_unittest.cc @@ -0,0 +1,122 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include "../lock.h" +#include "../thread.h" + +#include + +#include + +using namespace std; +using namespace isc::util::thread; + +namespace { + +// Test a recursive mutex can be locked multiple times +TEST(MutexTest, recursiveLockMultiple) { + Mutex mutex(true); + Mutex::Locker l1(mutex); + Mutex::Locker l2(mutex); + Mutex::Locker l3(mutex); + Mutex::Locker l4(mutex); + Mutex::Locker l5(mutex); +} + +// If we try to lock the debug mutex multiple times, it should throw. +TEST(MutexTest, lockMultiple) { + // TODO: Once we support non-debug mutexes, disable the test if we compile + // with them. + Mutex mutex; + Mutex::Locker l1(mutex); + EXPECT_THROW({ + Mutex::Locker l2(mutex); // Attempt to lock again. + }, isc::InvalidOperation); +} + +// Destroying a locked mutex is a bad idea as well +TEST(MutexTest, destroyLocked) { + // TODO: This probably won't work for non-debug mutexes. Disable on non-debug + // compilation. + auto_ptr mutex(new Mutex); + Mutex::Locker locker(*mutex); + // Note: This maybe leaks somewhere. But this is a test for development aid + // exception. The exception won't happen in normal build anyway and seeing + // it means there's a bug. + EXPECT_THROW(mutex.reset(), isc::InvalidOperation); +} + +// This test tries if a mutex really locks. We could try that with a deadlock, +// but that's not practical (the test would not end). +// +// So instead, we try to do some operations from multiple threads that are +// likely to break if not locked. Also, they must run for some time so all +// threads are started and the operation must be complicated enough so the +// compiler won't turn it into some kind of single atomic instruction. +// +// So, we'll have an array of numbers. Each thread will try to repeatedly +// find a number large at least as half of the average, take the number, +// distribute the value across the rest of the positions of the array and +// zero the found position. This operation keeps the sum of the array +// the same. But if two threads access it at the same time, it is likely +// one will add something to the position another one have chosen and +// the other one will then zero it, not taking the new value into account. +// That'd lower the total value of the array. +const unsigned long long length = 100000; +const unsigned long long iterations = 1000000; +const unsigned long long value = 200000; +void +performStrangeOperation(long long unsigned* array, int direction, + Mutex* mutex) +{ + unsigned long long position = 0; + for (size_t i = 0; i < iterations; i ++) { + Mutex::Locker lock(*mutex); + while (array[position % length] < value) { + position += direction; + } + unsigned long long value = array[position % length]; + unsigned long long p2 = position; + while (value > 0) { + p2 += direction; + if (p2 == position) { + continue; + } + array[p2 % length] ++; + value --; + } + array[position % length] = 0; + } +} + +TEST(MutexTest, swarm) { + // This type has a low chance of being atomic itself, further raising + // the chance of problems appearing. + long long unsigned array[length]; + for (size_t i = 0; i < length; ++ i) { + array[i] = value; + } + Mutex mutex; + Thread t1(boost::bind(&performStrangeOperation, array, 1, &mutex)); + Thread t2(boost::bind(&performStrangeOperation, array, -1, &mutex)); + t1.wait(); + t2.wait(); + long long unsigned sum = 0; + for (size_t i = 0; i < length; ++ i) { + sum += array[i]; + } + EXPECT_EQ(length * value, sum) << "Threads are badly synchronized"; +} + +}