Browse Source

[2332] supported wait() and signal()

JINMEI Tatuya 12 years ago
parent
commit
43959b29a6

+ 60 - 2
src/lib/util/threads/lock.cc

@@ -110,19 +110,35 @@ Mutex::~Mutex() {
 }
 
 void
+Mutex::postLockAction() {
+    if (impl_->locked_count != 0) {
+        isc_throw(isc::InvalidOperation, "Lock attempt for locked mutex");
+    }
+    ++impl_->locked_count;
+}
+
+void
 Mutex::lock() {
     assert(impl_ != NULL);
     const int result = pthread_mutex_lock(&impl_->mutex);
     if (result != 0) {
         isc_throw(isc::InvalidOperation, std::strerror(result));
     }
-    ++impl_->locked_count; // Only in debug mode
+    postLockAction();           // Only in debug mode
+}
+
+void
+Mutex::preUnlockAction() {
+    if (impl_->locked_count == 0) {
+        isc_throw(isc::InvalidOperation, "Unlock attempt for unlocked mutex");
+    }
+    --impl_->locked_count;
 }
 
 void
 Mutex::unlock() {
     assert(impl_ != NULL);
-    --impl_->locked_count; // Only in debug mode
+    preUnlockAction();          // Only in debug mode
     const int result = pthread_mutex_unlock(&impl_->mutex);
     assert(result == 0); // This should never be possible
 }
@@ -133,6 +149,48 @@ Mutex::locked() const {
     return (impl_->locked_count != 0);
 }
 
+class CondVar::Impl {
+public:
+    Impl() {
+        const int result = pthread_cond_init(&cond_, NULL);
+        if (result != 0) {
+            isc_throw(isc::Unexpected, "pthread_cond_init failed: "
+                      << std::strerror(result));
+        }
+    }
+    ~Impl() {
+        const int result = pthread_cond_destroy(&cond_);
+        assert(result == 0);
+    }
+
+    // For convenience allow the main class to access this directly.
+    pthread_cond_t cond_;
+};
+
+CondVar::CondVar() : impl_(new Impl)
+{}
+
+CondVar::~CondVar() {
+    delete impl_;
+}
+
+void
+CondVar::wait(Mutex& mutex) {
+    mutex.preUnlockAction();    // Only in debug mode
+    const int result = pthread_cond_wait(&impl_->cond_, &mutex.impl_->mutex);
+    mutex.postLockAction();     // Only in debug mode
+    assert(result == 0);
+}
+
+void
+CondVar::signal() {
+    const int result = pthread_cond_signal(&impl_->cond_);
+
+    // pthread_cond_signal() can only fail when if cond_ is invalid.  It
+    //should be impossible as long as this is a valid CondVar object.
+    assert(result == 0);
+}
+
 }
 }
 }

+ 28 - 3
src/lib/util/threads/lock.h

@@ -22,6 +22,7 @@
 namespace isc {
 namespace util {
 namespace thread {
+class CondVar;
 
 /// \brief Mutex with very simple interface
 ///
@@ -114,15 +115,39 @@ public:
     /// \todo Disable in non-debug build
     bool locked() const;
 private:
+    friend class CondVar;
+
+    // Commonly called after acquiring the lock, checking and updating
+    // internal state for debug.
+    void postLockAction();
+
+    // Commonly called before releasing the lock, checking and updating
+    // internal state for debug.
+    void preUnlockAction();
+
     class Impl;
     Impl* impl_;
     void lock();
     void unlock();
 };
 
+class CondVar : boost::noncopyable {
+public:
+    CondVar();
+    ~CondVar();
+    void wait(Mutex& mutex);
+    void signal();
+private:
+    class Impl;
+    Impl* impl_;
+};
 
-}
-}
-}
+} // namespace thread
+} // namespace util
+} // namespace isc
 
 #endif
+
+// Local Variables:
+// mode: c++
+// End:

+ 48 - 1
src/lib/util/threads/tests/condvar_unittest.cc

@@ -13,10 +13,17 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <util/threads/lock.h>
-#include <util/threads/condvar.h>
+#include <util/threads/thread.h>
 
 #include <gtest/gtest.h>
 
+#include <boost/bind.hpp>
+
+#include <cstring>
+
+#include <unistd.h>
+#include <signal.h>
+
 using namespace isc::util::thread;
 
 namespace {
@@ -25,4 +32,44 @@ TEST(CondVarTest, create) {
     EXPECT_NO_THROW(CondVar condvar);
 }
 
+// Running on a separate thread, just updating the argument and waking up
+// the other thread via the condition variable passed.
+void
+ringSignal(CondVar* condvar, Mutex* mutex, bool* arg) {
+    assert(*arg == false);
+    Mutex::Locker locker(*mutex);
+    *arg = true;
+    condvar->signal();
+}
+
+void
+no_handler(int) {}
+
+// A simple wait-signal operation on a condition variable.
+TEST(CondVarTest, waitAndSignal) {
+    CondVar condvar;
+    Mutex mutex;
+
+    struct sigaction ignored, original;
+    std::memset(&ignored, 0, sizeof(ignored));
+    ignored.sa_handler = no_handler;
+    if (sigaction(SIGALRM, &ignored, &original) != 0) {
+        FAIL() << "Couldn't set alarm";
+    }
+    alarm(10);                  // 10sec duration: arbitrary choice
+
+    Mutex::Locker locker(mutex);
+    bool must_become_true = false; // let the other thread reset it to true
+    Thread t(boost::bind(&ringSignal, &condvar, &mutex, &must_become_true));
+    condvar.wait(mutex);
+    t.wait();
+    EXPECT_TRUE(must_become_true);
+
+    // Cancel the alarm and return the original handler
+    alarm(0);
+    if (sigaction(SIGALRM, &original, NULL)) {
+        FAIL() << "Couldn't restore alarm";
+    }
+}
+
 }