}
}
+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);
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
}
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);
+}
+
}
}
}
namespace isc {
namespace util {
namespace thread {
+class CondVar;
/// \brief Mutex with very simple interface
///
/// \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:
// 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 {
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";
+ }
+}
+
}