Browse Source

[2202] Tests for mutex

Michal 'vorner' Vaner 12 years ago
parent
commit
ff7d2f5f77
2 changed files with 123 additions and 0 deletions
  1. 1 0
      src/lib/util/threads/tests/Makefile.am
  2. 122 0
      src/lib/util/threads/tests/lock_unittest.cc

+ 1 - 0
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)

+ 122 - 0
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 <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";
+}
+
+}