|
@@ -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 <gtest/gtest.h>
|
|
|
+
|
|
|
+#include <boost/bind.hpp>
|
|
|
+
|
|
|
+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> 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";
|
|
|
+}
|
|
|
+
|
|
|
+}
|