lock_unittest.cc 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. // Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <gtest/gtest.h>
  16. #include <util/threads/sync.h>
  17. #include <util/threads/thread.h>
  18. #include <util/unittests/check_valgrind.h>
  19. #include <boost/bind.hpp>
  20. #include <unistd.h>
  21. #include <signal.h>
  22. using namespace isc::util::thread;
  23. namespace {
  24. #ifdef ENABLE_DEBUG
  25. // If we try to lock the debug mutex multiple times, it should
  26. // throw. This test will complete properly only when pthread debugging
  27. // facilities are enabled by configuring the code for debug build.
  28. TEST(MutexTest, lockMultiple) {
  29. Mutex mutex;
  30. EXPECT_FALSE(mutex.locked()); // Debug-only build
  31. Mutex::Locker l1(mutex);
  32. EXPECT_TRUE(mutex.locked()); // Debug-only build
  33. EXPECT_THROW({
  34. Mutex::Locker l2(mutex); // Attempt to lock again.
  35. }, isc::InvalidOperation);
  36. EXPECT_TRUE(mutex.locked()); // Debug-only build
  37. }
  38. #endif // ENABLE_DEBUG
  39. #ifndef HAS_UNDEFINED_PTHREAD_BEHAVIOR
  40. // Destroying a locked mutex is a bad idea as well
  41. TEST(MutexTest, destroyLocked) {
  42. if (!isc::util::unittests::runningOnValgrind()) {
  43. EXPECT_DEATH_IF_SUPPORTED({
  44. Mutex* mutex = new Mutex;
  45. new Mutex::Locker(*mutex);
  46. delete mutex;
  47. // This'll leak the locker, but inside the slave process, it should
  48. // not be an issue.
  49. }, "");
  50. }
  51. }
  52. #endif // !HAS_UNDEFINED_PTHREAD_BEHAVIOR
  53. // In this test, we try to check if a mutex really locks. We could try that
  54. // with a deadlock, but that's not practical (the test would not end).
  55. //
  56. // Instead, we try do to some operation on the same data from multiple threads
  57. // that's likely to break if not locked. Also, the test must run for a while
  58. // to have an opportunity to manifest.
  59. //
  60. // Currently we try incrementing a double variable. That one is large enough
  61. // and complex enough so it should not be possible for the CPU to do it as an
  62. // atomic operation, at least on common architectures.
  63. const size_t iterations = 100000;
  64. void
  65. performIncrement(volatile double* canary, volatile bool* ready_me,
  66. volatile bool* ready_other, Mutex* mutex)
  67. {
  68. // Loosely (busy) wait for the other thread so both will start
  69. // approximately at the same time.
  70. *ready_me = true;
  71. while (!*ready_other) {}
  72. for (size_t i = 0; i < iterations; ++i) {
  73. Mutex::Locker lock(*mutex);
  74. *canary += 1;
  75. }
  76. }
  77. void
  78. noHandler(int) {}
  79. TEST(MutexTest, swarm) {
  80. if (!isc::util::unittests::runningOnValgrind()) {
  81. // Create a timeout in case something got stuck here
  82. struct sigaction ignored, original;
  83. memset(&ignored, 0, sizeof(ignored));
  84. ignored.sa_handler = noHandler;
  85. if (sigaction(SIGALRM, &ignored, &original)) {
  86. FAIL() << "Couldn't set alarm";
  87. }
  88. alarm(10);
  89. // This type has a low chance of being atomic itself, further raising
  90. // the chance of problems appearing.
  91. double canary = 0;
  92. Mutex mutex;
  93. // Run two parallel threads
  94. bool ready1 = false;
  95. bool ready2 = false;
  96. Thread t1(boost::bind(&performIncrement, &canary, &ready1, &ready2,
  97. &mutex));
  98. Thread t2(boost::bind(&performIncrement, &canary, &ready2, &ready1,
  99. &mutex));
  100. t1.wait();
  101. t2.wait();
  102. // Check it the sum is the expected value.
  103. EXPECT_EQ(iterations * 2, canary) << "Threads are badly synchronized";
  104. // Cancel the alarm and return the original handler
  105. alarm(0);
  106. if (sigaction(SIGALRM, &original, NULL)) {
  107. FAIL() << "Couldn't restore alarm";
  108. }
  109. }
  110. }
  111. }