BIND 10 master, updated. 5080ddf460167c252502db23b97cdb69fce7f240 [master] Merge branch 'trac2332' commit. commit.
BIND 10 source code commits
bind10-changes at lists.isc.org
Wed Oct 17 21:53:59 UTC 2012
The branch, master has been updated
via 5080ddf460167c252502db23b97cdb69fce7f240 (commit)
via 650655fd267d652ec1b0d5e57a261ab767ada447 (commit)
via c450d77e81322fb6a3c6753d721f5d3bb62fff71 (commit)
via 8c02f8a83f011816c3fb19f655a1e57edf0c1048 (commit)
via a439004524335feb296ee7477a5b450a2a7d7953 (commit)
via 410987bf3c9299053c07aee46591193c97af6ea4 (commit)
via 7f94f671e0da9c98cabcc38433852732294bee4a (commit)
via 671005eea2c18ad34bb0bad435b538447cdc7a50 (commit)
via b6d2a03ff5dfc0b1269e69e44defb2999e8a5d33 (commit)
via d504c06cbe387363d11ae091eeaa980444cc5190 (commit)
via d3983e1e68f66bbf6c69838aebe01a8f95b7bc33 (commit)
via ca61588dc2c1ad17200416ac7f07ac272adc8903 (commit)
via dc5492b8401c85daf1b0c8801e497bac3d5eeea5 (commit)
via 6962458ec49390d007dbf0fa9d5d3befa53deb23 (commit)
via 28b3efcf6100d2eab66c407a8f7ec54a87ac5f87 (commit)
via 43959b29a67ba17054bd4bb70b6fe3b8be3b2f2a (commit)
via c0d50a204134b57f36a9e83cc694abcb5924f357 (commit)
via bc7551cca83bf060410a5645f014ddb0eb233834 (commit)
via 8722bc208f5a232707824f3537fe0f6d073151f1 (commit)
via 1adba86b37b9c11a8120e31cc4eeb137f8cde6d9 (commit)
from 39faa67b7a8e9b3680792582bb65372ddf610b77 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 5080ddf460167c252502db23b97cdb69fce7f240
Merge: 39faa67 650655f
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed Oct 17 14:45:57 2012 -0700
[master] Merge branch 'trac2332'
commit.
commit.
-----------------------------------------------------------------------
Summary of changes:
src/bin/auth/auth_srv.cc | 2 +-
src/bin/auth/benchmarks/query_bench.cc | 2 +-
src/bin/auth/command.cc | 2 +-
src/bin/auth/main.cc | 2 +-
src/bin/auth/tests/auth_srv_unittest.cc | 2 +-
src/bin/auth/tests/command_unittest.cc | 2 +-
src/bin/auth/tests/datasrc_config_unittest.cc | 2 +-
src/lib/util/threads/Makefile.am | 2 +-
src/lib/util/threads/lock.h | 128 --------------
src/lib/util/threads/{lock.cc => sync.cc} | 81 ++++++++-
src/lib/util/threads/sync.h | 223 ++++++++++++++++++++++++
src/lib/util/threads/tests/Makefile.am | 1 +
src/lib/util/threads/tests/condvar_unittest.cc | 161 +++++++++++++++++
src/lib/util/threads/tests/lock_unittest.cc | 8 +-
src/lib/util/threads/thread.cc | 2 +-
15 files changed, 476 insertions(+), 144 deletions(-)
delete mode 100644 src/lib/util/threads/lock.h
rename src/lib/util/threads/{lock.cc => sync.cc} (62%)
create mode 100644 src/lib/util/threads/sync.h
create mode 100644 src/lib/util/threads/tests/condvar_unittest.cc
-----------------------------------------------------------------------
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index e73eb52..315a752 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -26,7 +26,7 @@
#include <exceptions/exceptions.h>
#include <util/buffer.h>
-#include <util/threads/lock.h>
+#include <util/threads/sync.h>
#include <dns/edns.h>
#include <dns/exceptions.h>
diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc
index 37976a3..44aeb8d 100644
--- a/src/bin/auth/benchmarks/query_bench.cc
+++ b/src/bin/auth/benchmarks/query_bench.cc
@@ -18,7 +18,7 @@
#include <bench/benchmark_util.h>
#include <util/buffer.h>
-#include <util/threads/lock.h>
+#include <util/threads/sync.h>
#include <dns/message.h>
#include <dns/name.h>
diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc
index d26cf09..efd60f2 100644
--- a/src/bin/auth/command.cc
+++ b/src/bin/auth/command.cc
@@ -21,7 +21,7 @@
#include <config/ccsession.h>
#include <exceptions/exceptions.h>
#include <dns/rrclass.h>
-#include <util/threads/lock.h>
+#include <util/threads/sync.h>
#include <string>
diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc
index 9908066..27ac04a 100644
--- a/src/bin/auth/main.cc
+++ b/src/bin/auth/main.cc
@@ -18,7 +18,7 @@
#include <util/buffer.h>
#include <util/io/socketsession.h>
-#include <util/threads/lock.h>
+#include <util/threads/sync.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index e248e81..396b247 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -39,7 +39,7 @@
#include <auth/datasrc_config.h>
#include <util/unittests/mock_socketsession.h>
-#include <util/threads/lock.h>
+#include <util/threads/sync.h>
#include <dns/tests/unittest_util.h>
#include <testutils/dnsmessage_test.h>
#include <testutils/srv_test.h>
diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc
index 8aa2322..a245c3b 100644
--- a/src/bin/auth/tests/command_unittest.cc
+++ b/src/bin/auth/tests/command_unittest.cc
@@ -16,7 +16,7 @@
#include "datasrc_util.h"
-#include <util/threads/lock.h>
+#include <util/threads/sync.h>
#include <auth/auth_srv.h>
#include <auth/auth_config.h>
diff --git a/src/bin/auth/tests/datasrc_config_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc
index 877f921..2dc70d1 100644
--- a/src/bin/auth/tests/datasrc_config_unittest.cc
+++ b/src/bin/auth/tests/datasrc_config_unittest.cc
@@ -16,7 +16,7 @@
#include <config/tests/fake_session.h>
#include <config/ccsession.h>
-#include <util/threads/lock.h>
+#include <util/threads/sync.h>
#include <gtest/gtest.h>
diff --git a/src/lib/util/threads/Makefile.am b/src/lib/util/threads/Makefile.am
index 7b9fb8f..121e4ab 100644
--- a/src/lib/util/threads/Makefile.am
+++ b/src/lib/util/threads/Makefile.am
@@ -5,7 +5,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
lib_LTLIBRARIES = libb10-threads.la
-libb10_threads_la_SOURCES = lock.h lock.cc
+libb10_threads_la_SOURCES = sync.h sync.cc
libb10_threads_la_SOURCES += thread.h thread.cc
libb10_threads_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
diff --git a/src/lib/util/threads/lock.cc b/src/lib/util/threads/lock.cc
deleted file mode 100644
index 6a165aa..0000000
--- a/src/lib/util/threads/lock.cc
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright (C) 2012 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 <exceptions/exceptions.h>
-
-#include <cstring>
-#include <memory>
-#include <cerrno>
-#include <cassert>
-
-#include <pthread.h>
-
-using std::auto_ptr;
-
-namespace isc {
-namespace util {
-namespace thread {
-
-class Mutex::Impl {
-public:
- Impl() :
- locked_count(0)
- {}
- pthread_mutex_t mutex;
- // Only in debug mode
- size_t locked_count;
-};
-
-namespace {
-
-struct Deinitializer {
- Deinitializer(pthread_mutexattr_t& attributes):
- attributes_(attributes)
- {}
- ~Deinitializer() {
- const int result = pthread_mutexattr_destroy(&attributes_);
- // This should never happen. According to the man page,
- // if there's error, it's our fault.
- assert(result == 0);
- }
- pthread_mutexattr_t& attributes_;
-};
-
-}
-
-Mutex::Mutex() :
- impl_(NULL)
-{
- pthread_mutexattr_t attributes;
- int result = pthread_mutexattr_init(&attributes);
- switch (result) {
- case 0: // All 0K
- break;
- case ENOMEM:
- throw std::bad_alloc();
- default:
- isc_throw(isc::InvalidOperation, std::strerror(result));
- }
- Deinitializer deinitializer(attributes);
- // TODO: Distinguish if debug mode is enabled in compilation.
- // If so, it should be PTHREAD_MUTEX_NORMAL or NULL
- result = pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_ERRORCHECK);
- if (result != 0) {
- isc_throw(isc::InvalidOperation, std::strerror(result));
- }
- auto_ptr<Impl> impl(new Impl);
- result = pthread_mutex_init(&impl->mutex, &attributes);
- switch (result) {
- case 0: // All 0K
- impl_ = impl.release();
- break;
- case ENOMEM:
- case EAGAIN:
- throw std::bad_alloc();
- default:
- isc_throw(isc::InvalidOperation, std::strerror(result));
- }
-}
-
-Mutex::~Mutex() {
- if (impl_ != NULL) {
- const int result = pthread_mutex_destroy(&impl_->mutex);
- const bool locked = impl_->locked_count != 0;
- delete impl_;
- // We don't want to throw from the destructor. Also, if this ever
- // fails, something is really screwed up a lot.
- assert(result == 0);
-
- // We should not try to destroy a locked mutex, bad threaded monsters
- // could get loose if we ever do and it is also forbidden by pthreads.
-
- // This should not be possible to happen, since the
- // pthread_mutex_destroy should check for it already. But it seems
- // there are systems that don't check it.
- assert(!locked);
- }
-}
-
-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
-}
-
-void
-Mutex::unlock() {
- assert(impl_ != NULL);
- --impl_->locked_count; // Only in debug mode
- const int result = pthread_mutex_unlock(&impl_->mutex);
- assert(result == 0); // This should never be possible
-}
-
-// TODO: Disable in non-debug build
-bool
-Mutex::locked() const {
- return (impl_->locked_count != 0);
-}
-
-}
-}
-}
diff --git a/src/lib/util/threads/lock.h b/src/lib/util/threads/lock.h
deleted file mode 100644
index fef537b..0000000
--- a/src/lib/util/threads/lock.h
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright (C) 2012 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.
-
-#ifndef B10_THREAD_LOCK_H
-#define B10_THREAD_LOCK_H
-
-#include <boost/noncopyable.hpp>
-
-#include <cstdlib> // for NULL.
-
-namespace isc {
-namespace util {
-namespace thread {
-
-/// \brief Mutex with very simple interface
-///
-/// Since mutexes are very system dependant, we create our own wrapper around
-/// whatever is available on the system and hide it.
-///
-/// To use this mutex, create it and then lock and unlock it by creating the
-/// Mutex::Locker object.
-///
-/// Also, as mutex is a low-level system object, an error might happen at any
-/// operation with it. We convert many errors to the isc::InvalidOperation,
-/// since the errors usually happen only when used in a wrong way. Any methods
-/// or constructors in this class can throw. Allocation errors are converted
-/// to std::bad_alloc (for example when OS-dependant limit of mutexes is
-/// exceeded). Some errors which usually mean a programmer error abort the
-/// program, since there could be no safe way to recover from them.
-///
-/// The current interface is somewhat minimalistic. If we ever need more, we
-/// can add it later.
-class Mutex : public boost::noncopyable {
-public:
- /// \brief Constructor.
- ///
- /// Creates a mutex. It is a non-recursive mutex (can be locked just once,
- /// if the same threads tries to lock it again, Bad Things Happen).
- ///
- /// Depending on compilation parameters and OS, the mutex may or may not
- /// do some error and sanity checking. However, such checking is meant
- /// only to aid development, not rely on it as a feature.
- ///
- /// \throw std::bad_alloc In case allocation of something (memory, the
- /// OS mutex) fails.
- /// \throw isc::InvalidOperation Other unspecified errors around the mutex.
- /// This should be rare.
- Mutex();
-
- /// \brief Destructor.
- ///
- /// Destroys the mutex. It is not allowed to destroy a mutex which is
- /// currently locked. This means a Locker created with this Mutex must
- /// never live longer than the Mutex itself.
- ~Mutex();
-
- /// \brief This holds a lock on a Mutex.
- ///
- /// To lock a mutex, create a locker. It'll get unlocked when the locker
- /// is destroyed.
- ///
- /// If you create the locker on the stack or using some other "garbage
- /// collecting" mechanism (auto_ptr, for example), it ensures exception
- /// safety with regards to the mutex - it'll get released on the exit
- /// of function no matter by what means.
- class Locker : public boost::noncopyable {
- public:
- /// \brief Constructor.
- ///
- /// Locks the mutex. May block for extended period of time.
- ///
- /// \throw isc::InvalidOperation when OS reports error. This usually
- /// means an attempt to use the mutex in a wrong way (locking
- /// a mutex second time from the same thread, for example).
- Locker(Mutex& mutex) :
- mutex_(NULL)
- {
- // Set the mutex_ after we acquire the lock. This is because of
- // exception safety. If lock() throws, it didn't work, so we must
- // not unlock when we are destroyed. In such case, mutex_ is
- // NULL and checked in the destructor.
- mutex.lock();
- mutex_ = &mutex;
- }
-
- /// \brief Destructor.
- ///
- /// Unlocks the mutex.
- ~Locker() {
- if (mutex_ != NULL) {
- mutex_->unlock();
- }
- }
- private:
- Mutex* mutex_;
- };
- /// \brief If the mutex is currently locked
- ///
- /// This is debug aiding method only. And it might be unavailable in
- /// non-debug build (because keeping the state might be needlesly
- /// slow).
- ///
- /// \todo Disable in non-debug build
- bool locked() const;
-private:
- class Impl;
- Impl* impl_;
- void lock();
- void unlock();
-};
-
-
-}
-}
-}
-
-#endif
diff --git a/src/lib/util/threads/sync.cc b/src/lib/util/threads/sync.cc
new file mode 100644
index 0000000..c98a7a6
--- /dev/null
+++ b/src/lib/util/threads/sync.cc
@@ -0,0 +1,213 @@
+// Copyright (C) 2012 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 "sync.h"
+
+#include <exceptions/exceptions.h>
+
+#include <cstring>
+#include <memory>
+#include <cerrno>
+#include <cassert>
+
+#include <pthread.h>
+
+using std::auto_ptr;
+
+namespace isc {
+namespace util {
+namespace thread {
+
+class Mutex::Impl {
+public:
+ Impl() :
+ locked_count(0)
+ {}
+ pthread_mutex_t mutex;
+ // Only in debug mode
+ size_t locked_count;
+};
+
+namespace {
+
+struct Deinitializer {
+ Deinitializer(pthread_mutexattr_t& attributes):
+ attributes_(attributes)
+ {}
+ ~Deinitializer() {
+ const int result = pthread_mutexattr_destroy(&attributes_);
+ // This should never happen. According to the man page,
+ // if there's error, it's our fault.
+ assert(result == 0);
+ }
+ pthread_mutexattr_t& attributes_;
+};
+
+}
+
+Mutex::Mutex() :
+ impl_(NULL)
+{
+ pthread_mutexattr_t attributes;
+ int result = pthread_mutexattr_init(&attributes);
+ switch (result) {
+ case 0: // All 0K
+ break;
+ case ENOMEM:
+ throw std::bad_alloc();
+ default:
+ isc_throw(isc::InvalidOperation, std::strerror(result));
+ }
+ Deinitializer deinitializer(attributes);
+ // TODO: Distinguish if debug mode is enabled in compilation.
+ // If so, it should be PTHREAD_MUTEX_NORMAL or NULL
+ result = pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_ERRORCHECK);
+ if (result != 0) {
+ isc_throw(isc::InvalidOperation, std::strerror(result));
+ }
+ auto_ptr<Impl> impl(new Impl);
+ result = pthread_mutex_init(&impl->mutex, &attributes);
+ switch (result) {
+ case 0: // All 0K
+ impl_ = impl.release();
+ break;
+ case ENOMEM:
+ case EAGAIN:
+ throw std::bad_alloc();
+ default:
+ isc_throw(isc::InvalidOperation, std::strerror(result));
+ }
+}
+
+Mutex::~Mutex() {
+ if (impl_ != NULL) {
+ const int result = pthread_mutex_destroy(&impl_->mutex);
+ const bool locked = impl_->locked_count != 0;
+ delete impl_;
+ // We don't want to throw from the destructor. Also, if this ever
+ // fails, something is really screwed up a lot.
+ assert(result == 0);
+
+ // We should not try to destroy a locked mutex, bad threaded monsters
+ // could get loose if we ever do and it is also forbidden by pthreads.
+
+ // This should not be possible to happen, since the
+ // pthread_mutex_destroy should check for it already. But it seems
+ // there are systems that don't check it.
+ assert(!locked);
+ }
+}
+
+void
+Mutex::postLockAction() {
+ // This assertion would fail only in non-debugging mode, in which case
+ // this method wouldn't be called either, so we simply assert the
+ // condition.
+ assert(impl_->locked_count == 0);
+ ++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));
+ }
+ postLockAction(); // Only in debug mode
+}
+
+void
+Mutex::preUnlockAction(bool throw_ok) {
+ if (impl_->locked_count == 0) {
+ if (throw_ok) {
+ isc_throw(isc::InvalidOperation,
+ "Unlock attempt for unlocked mutex");
+ } else {
+ assert(false);
+ }
+ }
+ --impl_->locked_count;
+}
+
+void
+Mutex::unlock() {
+ assert(impl_ != NULL);
+ preUnlockAction(false); // Only in debug mode. Ensure no throw.
+ const int result = pthread_mutex_unlock(&impl_->mutex);
+ assert(result == 0); // This should never be possible
+}
+
+// TODO: Disable in non-debug build
+bool
+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_);
+
+ // This can happen if we try to destroy cond_ while some other thread
+ // is waiting on it. assert() may be too strong for such a case,
+ // but we cannot safely destroy cond_ anyway. In order to avoid
+ // throwing from a destructor we simply let the process die.
+ 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(true); // Only in debug mode
+ const int result = pthread_cond_wait(&impl_->cond_, &mutex.impl_->mutex);
+ mutex.postLockAction(); // Only in debug mode
+
+ // pthread_cond_wait should normally succeed unless mutex is completely
+ // broken.
+ if (result != 0) {
+ isc_throw(isc::BadValue, "pthread_cond_wait failed unexpectedly: " <<
+ std::strerror(result));
+ }
+}
+
+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/sync.h b/src/lib/util/threads/sync.h
new file mode 100644
index 0000000..ff56999
--- /dev/null
+++ b/src/lib/util/threads/sync.h
@@ -0,0 +1,223 @@
+// Copyright (C) 2012 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.
+
+#ifndef B10_THREAD_SYNC_H
+#define B10_THREAD_SYNC_H
+
+#include <boost/noncopyable.hpp>
+
+#include <cstdlib> // for NULL.
+
+namespace isc {
+namespace util {
+namespace thread {
+class CondVar;
+
+/// \brief Mutex with very simple interface
+///
+/// Since mutexes are very system dependant, we create our own wrapper around
+/// whatever is available on the system and hide it.
+///
+/// To use this mutex, create it and then lock and unlock it by creating the
+/// Mutex::Locker object.
+///
+/// Also, as mutex is a low-level system object, an error might happen at any
+/// operation with it. We convert many errors to the isc::InvalidOperation,
+/// since the errors usually happen only when used in a wrong way. Any methods
+/// or constructors in this class can throw. Allocation errors are converted
+/// to std::bad_alloc (for example when OS-dependant limit of mutexes is
+/// exceeded). Some errors which usually mean a programmer error abort the
+/// program, since there could be no safe way to recover from them.
+///
+/// The current interface is somewhat minimalistic. If we ever need more, we
+/// can add it later.
+class Mutex : boost::noncopyable {
+public:
+ /// \brief Constructor.
+ ///
+ /// Creates a mutex. It is a non-recursive mutex (can be locked just once,
+ /// if the same threads tries to lock it again, Bad Things Happen).
+ ///
+ /// Depending on compilation parameters and OS, the mutex may or may not
+ /// do some error and sanity checking. However, such checking is meant
+ /// only to aid development, not rely on it as a feature.
+ ///
+ /// \throw std::bad_alloc In case allocation of something (memory, the
+ /// OS mutex) fails.
+ /// \throw isc::InvalidOperation Other unspecified errors around the mutex.
+ /// This should be rare.
+ Mutex();
+
+ /// \brief Destructor.
+ ///
+ /// Destroys the mutex. It is not allowed to destroy a mutex which is
+ /// currently locked. This means a Locker created with this Mutex must
+ /// never live longer than the Mutex itself.
+ ~Mutex();
+
+ /// \brief This holds a lock on a Mutex.
+ ///
+ /// To lock a mutex, create a locker. It'll get unlocked when the locker
+ /// is destroyed.
+ ///
+ /// If you create the locker on the stack or using some other "garbage
+ /// collecting" mechanism (auto_ptr, for example), it ensures exception
+ /// safety with regards to the mutex - it'll get released on the exit
+ /// of function no matter by what means.
+ class Locker : boost::noncopyable {
+ public:
+ /// \brief Constructor.
+ ///
+ /// Locks the mutex. May block for extended period of time.
+ ///
+ /// \throw isc::InvalidOperation when OS reports error. This usually
+ /// means an attempt to use the mutex in a wrong way (locking
+ /// a mutex second time from the same thread, for example).
+ Locker(Mutex& mutex) :
+ mutex_(mutex)
+ {
+ mutex.lock();
+ }
+
+ /// \brief Destructor.
+ ///
+ /// Unlocks the mutex.
+ ~Locker() {
+ mutex_.unlock();
+ }
+ private:
+ Mutex& mutex_;
+ };
+ /// \brief If the mutex is currently locked
+ ///
+ /// This is debug aiding method only. And it might be unavailable in
+ /// non-debug build (because keeping the state might be needlesly
+ /// slow).
+ ///
+ /// \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.
+ //
+ // If throw_ok is true, it throws \c isc::InvalidOperation when the check
+ // fails; otherwise it aborts the process. This parameter must be set
+ // to false if the call to this shouldn't result in an exception (e.g.
+ // when called from a destructor).
+ void preUnlockAction(bool throw_ok);
+
+ class Impl;
+ Impl* impl_;
+ void lock();
+ void unlock();
+};
+
+/// \brief Encapsulation for a condition variable.
+///
+/// This class provides a simple encapsulation of condition variable for
+/// inter-thread synchronization. It has similar but simplified interface as
+/// that for \c pthread_cond_ variants.
+///
+/// It uses the \c Mutex class object for the mutex used with the condition
+/// variable. Since for normal applications the internal \c Mutex::Locker
+/// class is the only available interface to acquire a lock, sample code
+/// for waiting on a condition variable would look like this:
+/// \code
+/// CondVar cond;
+/// Mutex mutex;
+/// {
+/// Mutex::Locker locker(mutex);
+/// while (some_condition) {
+/// cond.wait(mutex);
+/// }
+/// // do something under the protection of locker
+/// } // lock is released here
+/// \endcode
+/// Note that \c mutex passed to the \c wait() method must be the same one
+/// used to construct the \c locker.
+///
+/// Right now there is no equivalent to pthread_cond_broadcast() or
+/// pthread_cond_timedwait() in this class, because this class is meant
+/// for internal development of BIND 10 and we don't need these at the
+/// moment. If and when we need these interfaces they can be added at that
+/// point.
+///
+/// \note This class is defined as a friend class of \c Mutex and directly
+/// refers to and modifies private internals of the \c Mutex class. It breaks
+/// the assumption that the lock is only acquired or released via the
+/// \c Locker class and breaks other integrity assumption on \c Mutex,
+/// thereby making it more fragile, but we couldn't find other way to
+/// implement a safe and still simple realization of condition variables.
+/// So, this is a kind of compromise. If this class is needed to be
+/// extended, first consider a way to use public interfaces of \c Mutex;
+/// do not easily rely on the fact that this class is a friend of it.
+class CondVar : boost::noncopyable {
+public:
+ /// \brief Constructor.
+ ///
+ /// \throw std::bad_alloc memory allocation failure
+ /// \throw isc::Unexpected other unexpected shortage of system resource
+ CondVar();
+
+ /// \brief Destructor.
+ ///
+ /// An object of this class must not be destroyed while some thread
+ /// is waiting on it. If this condition isn't met the destructor will
+ /// terminate the program.
+ ~CondVar();
+
+ /// \brief Wait on the condition variable.
+ ///
+ /// This method works like \c pthread_cond_wait(). For mutex it takes
+ /// an \c Mutex class object. A lock for the mutex must have been
+ /// acquired. If this condition isn't met, it can throw an exception
+ /// (in the debug mode build) or result in undefined behavior.
+ ///
+ /// The lock will be automatically released within this method, and
+ /// will be re-acquired on the exit of this method.
+ ///
+ /// \throw isc::InvalidOperation mutex isn't locked
+ /// \throw isc::BadValue mutex is not a valid \c Mutex object
+ ///
+ /// \param mutex A \c Mutex object to be released on wait().
+ void wait(Mutex& mutex);
+
+ /// \brief Unblock a thread waiting for the condition variable.
+ ///
+ /// This method wakes one of other threads (if any) waiting on this object
+ /// via the \c wait() call.
+ ///
+ /// This method never throws; if some unexpected low level error happens
+ /// it terminates the program.
+ 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/Makefile.am b/src/lib/util/threads/tests/Makefile.am
index 2d9d4f9..a12d221 100644
--- a/src/lib/util/threads/tests/Makefile.am
+++ b/src/lib/util/threads/tests/Makefile.am
@@ -23,6 +23,7 @@ TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += thread_unittest.cc
run_unittests_SOURCES += lock_unittest.cc
+run_unittests_SOURCES += condvar_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(PTHREAD_LDFLAGS)
diff --git a/src/lib/util/threads/tests/condvar_unittest.cc b/src/lib/util/threads/tests/condvar_unittest.cc
new file mode 100644
index 0000000..e3a534b
--- /dev/null
+++ b/src/lib/util/threads/tests/condvar_unittest.cc
@@ -0,0 +1,161 @@
+// Copyright (C) 2012 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 <exceptions/exceptions.h>
+
+#include <util/threads/sync.h>
+#include <util/threads/thread.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <cstring>
+
+#include <unistd.h>
+#include <signal.h>
+
+using namespace isc::util::thread;
+
+namespace {
+// Used as a signal handler below.
+volatile bool do_exit; // use for emergency escape
+void
+alarmHandler(int) {
+ do_exit = true;
+}
+
+class CondVarTest : public ::testing::Test {
+protected:
+ // We use a signal in case some of the thread synchronization tests
+ // unexpectedly cause a deadlock.
+ void SetUp() {
+ do_exit = false;
+
+ std::memset(&handler_, 0, sizeof(handler_));
+ handler_.sa_handler = alarmHandler;
+ if (sigaction(SIGALRM, &handler_, &original_) != 0) {
+ FAIL() << "Couldn't set alarm";
+ }
+ alarm(10); // 10sec duration: arbitrary choice
+ }
+ void TearDown() {
+ // Cancel the alarm and return the original handler
+ alarm(0);
+ if (sigaction(SIGALRM, &original_, NULL)) {
+ FAIL() << "Couldn't restore alarm";
+ }
+ }
+
+ CondVar condvar_;
+ Mutex mutex_;
+private:
+ struct sigaction handler_, original_;
+};
+
+TEST(CondVarTest0, create) {
+ // Just construct and destruct it. Nothing unusual should happen.
+ 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, int* arg) {
+ assert(*arg == 0);
+ Mutex::Locker locker(*mutex);
+ ++*arg;
+ condvar->signal();
+}
+
+// A simple wait-signal operation on a condition variable.
+TEST_F(CondVarTest, waitAndSignal) {
+ Mutex::Locker locker(mutex_);
+ int shared_var = 0; // let the other thread increment this
+ Thread t(boost::bind(&ringSignal, &condvar_, &mutex_, &shared_var));
+ condvar_.wait(mutex_);
+ t.wait();
+ EXPECT_EQ(1, shared_var);
+}
+
+// Thread's main code for the next test
+void
+signalAndWait(CondVar* condvar1, CondVar* condvar2, Mutex* mutex, int* arg) {
+ Mutex::Locker locker(*mutex);
+ ++*arg;
+ condvar2->signal(); // let the main thread know this one is ready
+ condvar1->wait(*mutex);
+ ++*arg;
+}
+
+// Similar to the previous test, but having two threads wait for a condvar.
+TEST_F(CondVarTest, multiWaits) {
+ boost::scoped_ptr<Mutex::Locker> locker(new Mutex::Locker(mutex_));
+ CondVar condvar2; // separate cond var for initial synchronization
+ int shared_var = 0; // let the other thread increment this
+ Thread t1(boost::bind(&signalAndWait, &condvar_, &condvar2, &mutex_,
+ &shared_var));
+ Thread t2(boost::bind(&signalAndWait, &condvar_, &condvar2, &mutex_,
+ &shared_var));
+
+ // Wait until both threads are waiting on condvar_.
+ while (shared_var < 2 && !do_exit) {
+ condvar2.wait(mutex_);
+ }
+ // Check we exited from the loop successfully.
+ ASSERT_FALSE(do_exit);
+ ASSERT_EQ(2, shared_var);
+
+ // release the lock, wake up both threads, wait for them to die, and
+ // confirm they successfully woke up.
+ locker.reset();
+ condvar_.signal();
+ condvar_.signal();
+ t1.wait();
+ t2.wait();
+ EXPECT_EQ(4, shared_var);
+}
+
+// Similar to the previous version of the same function, but just do
+// condvar operations. It will never wake up.
+void
+signalAndWait(CondVar* condvar, Mutex* mutex) {
+ Mutex::Locker locker(*mutex);
+ condvar->signal();
+ condvar->wait(*mutex);
+}
+
+TEST_F(CondVarTest, destroyWhileWait) {
+ // We'll destroy a CondVar object while the thread is still waiting
+ // on it. This will trigger an assertion failure.
+ EXPECT_DEATH_IF_SUPPORTED({
+ CondVar cond;
+ Mutex::Locker locker(mutex_);
+ Thread t(boost::bind(&signalAndWait, &cond, &mutex_));
+ cond.wait(mutex_);
+ }, "");
+}
+
+TEST_F(CondVarTest, badWait) {
+ // In our implementation, wait() requires acquiring the lock beforehand.
+ EXPECT_THROW(condvar_.wait(mutex_), isc::InvalidOperation);
+}
+
+TEST_F(CondVarTest, emptySignal) {
+ // It's okay to call signal when no one waits.
+ EXPECT_NO_THROW(condvar_.signal());
+}
+
+}
diff --git a/src/lib/util/threads/tests/lock_unittest.cc b/src/lib/util/threads/tests/lock_unittest.cc
index 0b4d3ce..4ddb6b2 100644
--- a/src/lib/util/threads/tests/lock_unittest.cc
+++ b/src/lib/util/threads/tests/lock_unittest.cc
@@ -12,7 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <util/threads/lock.h>
+#include <util/threads/sync.h>
#include <util/threads/thread.h>
#include <gtest/gtest.h>
@@ -80,13 +80,13 @@ performIncrement(volatile double* canary, volatile bool* ready_me,
}
void
-no_handler(int) {}
+noHandler(int) {}
TEST(MutexTest, swarm) {
// Create a timeout in case something got stuck here
struct sigaction ignored, original;
- memset(&ignored, 0, sizeof ignored);
- ignored.sa_handler = no_handler;
+ memset(&ignored, 0, sizeof(ignored));
+ ignored.sa_handler = noHandler;
if (sigaction(SIGALRM, &ignored, &original)) {
FAIL() << "Couldn't set alarm";
}
diff --git a/src/lib/util/threads/thread.cc b/src/lib/util/threads/thread.cc
index c1c9cdf..5608e49 100644
--- a/src/lib/util/threads/thread.cc
+++ b/src/lib/util/threads/thread.cc
@@ -13,7 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include "thread.h"
-#include "lock.h"
+#include "sync.h"
#include <memory>
#include <string>
More information about the bind10-changes
mailing list