From: JINMEI Tatuya Date: Thu, 11 Oct 2012 03:09:58 +0000 (-0700) Subject: [2332] supported wait() and signal() X-Git-Tag: trac2402_base~19^2~14 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=43959b29a67ba17054bd4bb70b6fe3b8be3b2f2a;p=thirdparty%2Fkea.git [2332] supported wait() and signal() --- diff --git a/src/lib/util/threads/lock.cc b/src/lib/util/threads/lock.cc index 6a165aa531..c33ec4202c 100644 --- a/src/lib/util/threads/lock.cc +++ b/src/lib/util/threads/lock.cc @@ -109,6 +109,14 @@ 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); @@ -116,13 +124,21 @@ Mutex::lock() { 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); +} + } } } diff --git a/src/lib/util/threads/lock.h b/src/lib/util/threads/lock.h index f634bf8faa..baa3fa4665 100644 --- a/src/lib/util/threads/lock.h +++ b/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: diff --git a/src/lib/util/threads/tests/condvar_unittest.cc b/src/lib/util/threads/tests/condvar_unittest.cc index 555a86a0e1..5a533c2c09 100644 --- a/src/lib/util/threads/tests/condvar_unittest.cc +++ b/src/lib/util/threads/tests/condvar_unittest.cc @@ -13,10 +13,17 @@ // PERFORMANCE OF THIS SOFTWARE. #include -#include +#include #include +#include + +#include + +#include +#include + 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"; + } +} + }