summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorFrancis Dupont <fdupont@isc.org>2019-09-21 08:52:27 +0200
committerFrancis Dupont <fdupont@isc.org>2019-09-27 11:07:21 +0200
commit469f43a8285f84d014a1315a45232100f4343d1b (patch)
treefc22f937132d995deb63746c05774bc00df87ed1 /src/lib
parent[918-check-c-11-thread-support-in-configure] Added ChangeLog entry for thread... (diff)
downloadkea-469f43a8285f84d014a1315a45232100f4343d1b.tar.xz
kea-469f43a8285f84d014a1315a45232100f4343d1b.zip
[907-remove-kea-thread-library] Checkpoint (todo finish netconf)
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/testutils/threaded_test.cc14
-rw-r--r--src/lib/testutils/threaded_test.h12
-rw-r--r--src/lib/util/Makefile.am7
-rw-r--r--src/lib/util/threads/Makefile.am16
-rw-r--r--src/lib/util/threads/sync.cc252
-rw-r--r--src/lib/util/threads/sync.h257
-rw-r--r--src/lib/util/threads/tests/.gitignore1
-rw-r--r--src/lib/util/threads/tests/Makefile.am38
-rw-r--r--src/lib/util/threads/tests/condvar_unittest.cc170
-rw-r--r--src/lib/util/threads/tests/lock_unittest.cc161
-rw-r--r--src/lib/util/threads/tests/run_unittests.cc22
-rw-r--r--src/lib/util/threads/tests/thread_unittest.cc262
-rw-r--r--src/lib/util/threads/thread.cc194
-rw-r--r--src/lib/util/threads/thread.h107
14 files changed, 13 insertions, 1500 deletions
diff --git a/src/lib/testutils/threaded_test.cc b/src/lib/testutils/threaded_test.cc
index 2302771caa..1a0126f9c7 100644
--- a/src/lib/testutils/threaded_test.cc
+++ b/src/lib/testutils/threaded_test.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -6,8 +6,6 @@
#include <testutils/threaded_test.h>
-using namespace isc::util::thread;
-
namespace isc {
namespace test {
@@ -19,10 +17,10 @@ ThreadedTest::ThreadedTest()
void
ThreadedTest::doSignal(bool& flag) {
{
- Mutex::Locker lock(mutex_);
+ std::lock_guard<std::mutex> lock(mutex_);
flag = true;
}
- condvar_.signal();
+ condvar_.notify_one();
}
void
@@ -42,9 +40,9 @@ ThreadedTest::signalStopped() {
void
ThreadedTest::doWait(bool& flag) {
- Mutex::Locker lock(mutex_);
+ std::unique_lock<std::mutex> lock(mutex_);
while (!flag) {
- condvar_.wait(mutex_);
+ condvar_.wait(lock);
}
}
@@ -65,7 +63,7 @@ ThreadedTest::waitStopped() {
bool
ThreadedTest::isStopping() {
- Mutex::Locker lock(mutex_);
+ std::lock_guard<std::mutex> lock(mutex_);
return (stopping_);
}
diff --git a/src/lib/testutils/threaded_test.h b/src/lib/testutils/threaded_test.h
index 537aec8efe..1ff4909c2e 100644
--- a/src/lib/testutils/threaded_test.h
+++ b/src/lib/testutils/threaded_test.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -7,10 +7,10 @@
#ifndef THREADED_TEST_H
#define THREADED_TEST_H
-#include <util/threads/thread.h>
-#include <util/threads/sync.h>
#include <boost/shared_ptr.hpp>
#include <gtest/gtest.h>
+#include <thread>
+#include <mutex>
namespace isc {
namespace test {
@@ -62,13 +62,13 @@ protected:
bool isStopping();
/// @brief Pointer to server thread.
- boost::shared_ptr<util::thread::Thread> thread_;
+ boost::shared_ptr<std::thread> thread_;
/// @brief Mutex used to synchronize threads.
- util::thread::Mutex mutex_;
+ std::mutex mutex_;
/// Condtional variable for thread waits.
- util::thread::CondVar condvar_;
+ std::condition_variable condvar_;
/// Flag indicating that the thread is ready.
bool ready_;
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
index 0c78964aae..4280b608b3 100644
--- a/src/lib/util/Makefile.am
+++ b/src/lib/util/Makefile.am
@@ -1,6 +1,6 @@
AUTOMAKE_OPTIONS = subdir-objects
-SUBDIRS = . io unittests tests python threads
+SUBDIRS = . io unittests tests python
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
@@ -96,8 +96,3 @@ libkea_util_random_includedir = $(pkgincludedir)/util/random
libkea_util_random_include_HEADERS = \
random/qid_gen.h \
random/random_number_generator.h
-
-libkea_util_threads_includedir = $(pkgincludedir)/util/threads
-libkea_util_threads_include_HEADERS = \
- threads/sync.h \
- threads/thread.h
diff --git a/src/lib/util/threads/Makefile.am b/src/lib/util/threads/Makefile.am
deleted file mode 100644
index 337dc0a9d7..0000000000
--- a/src/lib/util/threads/Makefile.am
+++ /dev/null
@@ -1,16 +0,0 @@
-SUBDIRS = . tests
-AM_CXXFLAGS = $(KEA_CXXFLAGS)
-
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES)
-
-lib_LTLIBRARIES = libkea-threads.la
-libkea_threads_la_SOURCES = sync.h sync.cc
-libkea_threads_la_SOURCES += thread.h thread.cc
-libkea_threads_la_LIBADD = $(top_builddir)/src/lib/util/libkea-util.la
-libkea_threads_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
-
-libkea_threads_la_LDFLAGS = -no-undefined -version-info 3:0:0
-
-
-CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/threads/sync.cc b/src/lib/util/threads/sync.cc
deleted file mode 100644
index 06463f95e9..0000000000
--- a/src/lib/util/threads/sync.cc
+++ /dev/null
@@ -1,252 +0,0 @@
-// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <util/threads/sync.h>
-
-#include <exceptions/exceptions.h>
-
-#include <cstring>
-#include <memory>
-#include <cerrno>
-#include <cassert>
-
-#include <pthread.h>
-
-using std::unique_ptr;
-
-namespace isc {
-namespace util {
-namespace thread {
-
-class Mutex::Impl {
-public:
- Impl()
-#ifdef ENABLE_DEBUG
- : locked_count(0)
-#endif // ENABLE_DEBUG
- {}
-
- pthread_mutex_t mutex;
-#ifdef ENABLE_DEBUG
- size_t locked_count;
-#endif // ENABLE_DEBUG
-};
-
-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);
-
- // If debug mode is enabled in compilation, use the slower
- // error-checking mutexes that detect deadlocks. Otherwise, use fast
- // mutexes which don't. See the pthread_mutexattr_settype() POSIX
- // documentation which describes these type attributes.
-#ifdef ENABLE_DEBUG
- result = pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_ERRORCHECK);
-#else
- result = pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_NORMAL);
-#endif // ENABLE_DEBUG
- if (result != 0) {
- isc_throw(isc::InvalidOperation, std::strerror(result));
- }
-
- unique_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);
-
-#ifdef ENABLE_DEBUG
- const bool locked = impl_->locked_count != 0;
-#endif // ENABLE_DEBUG
-
- 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);
-
-#ifdef ENABLE_DEBUG
- // 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);
-#endif // ENABLE_DEBUG
- }
-}
-
-#ifdef ENABLE_DEBUG
-
-void
-Mutex::postLockAction() {
- assert(impl_->locked_count == 0);
- ++impl_->locked_count;
-}
-
-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;
-}
-
-bool
-Mutex::locked() const {
- return (impl_->locked_count != 0);
-}
-
-#endif // ENABLE_DEBUG
-
-void
-Mutex::lock() {
- assert(impl_ != NULL);
- const int result = pthread_mutex_lock(&impl_->mutex);
- if (result != 0) {
- isc_throw(isc::InvalidOperation, std::strerror(result));
- }
-#ifdef ENABLE_DEBUG
- postLockAction(); // Only in debug mode
-#endif // ENABLE_DEBUG
-}
-
-bool
-Mutex::tryLock() {
- assert(impl_ != NULL);
- const int result = pthread_mutex_trylock(&impl_->mutex);
- // In the case of pthread_mutex_trylock(), if it is called on a
- // locked mutex from the same thread, some platforms (such as fedora
- // and debian) return EBUSY whereas others (such as centos 5) return
- // EDEADLK. We return false and don't pass the lock attempt in both
- // cases.
- if (result == EBUSY || result == EDEADLK) {
- return (false);
- } else if (result != 0) {
- isc_throw(isc::InvalidOperation, std::strerror(result));
- }
-#ifdef ENABLE_DEBUG
- postLockAction(); // Only in debug mode
-#endif // ENABLE_DEBUG
- return (true);
-}
-
-void
-Mutex::unlock() {
- assert(impl_ != NULL);
-#ifdef ENABLE_DEBUG
- preUnlockAction(false); // Only in debug mode. Ensure no throw.
-#endif // ENABLE_DEBUG
- const int result = pthread_mutex_unlock(&impl_->mutex);
- assert(result == 0); // This should never be possible
-}
-
-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) {
-#ifdef ENABLE_DEBUG
- mutex.preUnlockAction(true); // Only in debug mode
- const int result = pthread_cond_wait(&impl_->cond_, &mutex.impl_->mutex);
- mutex.postLockAction(); // Only in debug mode
-#else
- const int result = pthread_cond_wait(&impl_->cond_, &mutex.impl_->mutex);
-#endif
- // 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
deleted file mode 100644
index 15e78fcb6e..0000000000
--- a/src/lib/util/threads/sync.h
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#ifndef KEA_THREAD_SYNC_H
-#define KEA_THREAD_SYNC_H
-
-#include <exceptions/exceptions.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 dependent, 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-dependent 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 minimalist. 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 (unique_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 Exception thrown when the mutex is already locked and
- /// a non-blocking locker is attempted around it.
- struct AlreadyLocked : public isc::InvalidParameter {
- AlreadyLocked(const char* file, size_t line, const char* what) :
- isc::InvalidParameter(file, line, what)
- {}
- };
-
- /// \brief Constructor.
- ///
- /// Locks the mutex. May block for extended period of time if
- /// \c block is true.
- ///
- /// \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).
- /// \throw AlreadyLocked if \c block is false and the mutex is
- /// already locked.
- Locker(Mutex& mutex, bool block = true) :
- mutex_(mutex)
- {
- if (block) {
- mutex.lock();
- } else {
- if (!mutex.tryLock()) {
- isc_throw(AlreadyLocked, "The mutex is already locked");
- }
- }
- }
-
- /// \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 needlessly
- /// slow).
- ///
- /// \todo Disable in non-debug build
- bool locked() const;
-
-private:
- /// \brief Lock the mutex
- ///
- /// This method blocks until the mutex can be locked.
- void lock();
-
- /// \brief Try to lock the mutex
- ///
- /// This method doesn't block and returns immediately with a status
- /// on whether the lock operation was successful.
- ///
- /// \return true if the lock was successful, false otherwise.
- bool tryLock();
-
- /// \brief Unlock the mutex
- void unlock();
-
-private:
- friend class CondVar;
-
- // Commonly called after acquiring the lock, checking and updating
- // internal state for debug.
- //
- // Note that this method is only available when the build is
- // configured with debugging support.
- 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).
- //
- // Note that this method is only available when the build is
- // configured with debugging support.
- void preUnlockAction(bool throw_ok);
-
- class Impl;
- Impl* impl_;
-};
-
-/// \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 was 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. Also, Kea likely to not use threading model, so the usefulness
-/// of this class is uncertain.
-///
-/// \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/.gitignore b/src/lib/util/threads/tests/.gitignore
deleted file mode 100644
index d6d1ec87a1..0000000000
--- a/src/lib/util/threads/tests/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/run_unittests
diff --git a/src/lib/util/threads/tests/Makefile.am b/src/lib/util/threads/tests/Makefile.am
deleted file mode 100644
index 81c5e5ef08..0000000000
--- a/src/lib/util/threads/tests/Makefile.am
+++ /dev/null
@@ -1,38 +0,0 @@
-SUBDIRS = .
-
-AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES)
-# XXX: we'll pollute the top builddir for creating a temporary test file
-# # used to bind a UNIX domain socket so we can minimize the risk of exceeding
-# # the limit of file name path size.
-AM_CPPFLAGS += -DTEST_DATA_TOPBUILDDIR=\"$(abs_top_builddir)\"
-AM_CXXFLAGS = $(KEA_CXXFLAGS)
-
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
-CLEANFILES = *.gcno *.gcda
-
-TESTS_ENVIRONMENT = \
- $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
-
-TESTS =
-if HAVE_GTEST
-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)
-
-run_unittests_LDADD = $(top_builddir)/src/lib/util/threads/libkea-threads.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
-run_unittests_LDADD += $(GTEST_LDADD)
-endif
-
-noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/util/threads/tests/condvar_unittest.cc b/src/lib/util/threads/tests/condvar_unittest.cc
deleted file mode 100644
index 856c9cda3f..0000000000
--- a/src/lib/util/threads/tests/condvar_unittest.cc
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <exceptions/exceptions.h>
-#include <util/unittests/check_valgrind.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) {
- if (!isc::util::unittests::runningOnValgrind()) {
- 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);
-}
-
-#ifdef HAS_UNDEFINED_PTHREAD_BEHAVIOR
-TEST_F(CondVarTest, DISABLED_destroyWhileWait) {
-#else
-// This tests had to be disabled because it hangs on most of the OS used in lab
-// TODO fix destroyWhileWait test
-TEST_F(CondVarTest, DISABLED_destroyWhileWait) {
-#endif
- // We'll destroy a CondVar object while the thread is still waiting
- // on it. This will trigger an assertion failure.
- if (!isc::util::unittests::runningOnValgrind()) {
- EXPECT_DEATH_IF_SUPPORTED({
- CondVar cond;
- Mutex::Locker locker(mutex_);
- Thread t(boost::bind(&signalAndWait, &cond, &mutex_));
- cond.wait(mutex_);
- }, "");
- }
-}
-
-#ifdef ENABLE_DEBUG
-
-TEST_F(CondVarTest, badWait) {
- // In our implementation, wait() requires acquiring the lock beforehand.
- EXPECT_THROW(condvar_.wait(mutex_), isc::InvalidOperation);
-}
-
-#endif // ENABLE_DEBUG
-
-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
deleted file mode 100644
index 0ed9be0c13..0000000000
--- a/src/lib/util/threads/tests/lock_unittest.cc
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <gtest/gtest.h>
-
-#include <util/threads/sync.h>
-#include <util/threads/thread.h>
-#include <util/unittests/check_valgrind.h>
-
-#include <boost/bind.hpp>
-#include <unistd.h>
-#include <signal.h>
-
-using namespace isc::util::thread;
-
-namespace {
-
-#ifdef ENABLE_DEBUG
-
-// If we try to lock the debug mutex multiple times, it should
-// throw. This test will complete properly only when pthread debugging
-// facilities are enabled by configuring the code for debug build.
-TEST(MutexTest, lockMultiple) {
- Mutex mutex;
- EXPECT_FALSE(mutex.locked()); // Debug-only build
-
- Mutex::Locker l1(mutex);
- EXPECT_TRUE(mutex.locked()); // Debug-only build
-
- EXPECT_THROW({
- Mutex::Locker l2(mutex); // Attempt to lock again.
- }, isc::InvalidOperation);
- EXPECT_TRUE(mutex.locked()); // Debug-only build
-
- // block=true explicitly.
- Mutex mutex2;
- EXPECT_FALSE(mutex2.locked()); // Debug-only build
- Mutex::Locker l12(mutex2, true);
- EXPECT_TRUE(mutex2.locked()); // Debug-only build
-}
-
-void
-testThread(Mutex* mutex)
-{
- // block=false (tryLock). This should not block indefinitely, but
- // throw AlreadyLocked. If block were true, this would block
- // indefinitely here.
- EXPECT_THROW({
- Mutex::Locker l3(*mutex, false);
- }, Mutex::Locker::AlreadyLocked);
-
- EXPECT_TRUE(mutex->locked()); // Debug-only build
-}
-
-// Test the non-blocking variant using a second thread.
-TEST(MutexTest, lockNonBlocking) {
- // block=false (tryLock).
- Mutex mutex;
- Mutex::Locker l1(mutex, false);
- EXPECT_TRUE(mutex.locked()); // Debug-only build
-
- // First, try another locker from the same thread.
- EXPECT_THROW({
- Mutex::Locker l2(mutex, false);
- }, Mutex::Locker::AlreadyLocked);
-
- EXPECT_TRUE(mutex.locked()); // Debug-only build
-
- // Now try another locker from a different thread.
- Thread thread(boost::bind(&testThread, &mutex));
- thread.wait();
-}
-
-#endif // ENABLE_DEBUG
-
-// Destroying a locked mutex is a bad idea as well
-#ifdef HAS_UNDEFINED_PTHREAD_BEHAVIOR
-TEST(MutexTest, DISABLED_destroyLocked) {
-#else
-TEST(MutexTest, destroyLocked) {
-#endif
- if (!isc::util::unittests::runningOnValgrind()) {
- EXPECT_DEATH_IF_SUPPORTED({
- Mutex* mutex = new Mutex;
- new Mutex::Locker(*mutex);
- delete mutex;
- // This'll leak the locker, but inside the slave process, it should
- // not be an issue.
- }, "");
- }
-}
-
-// In this test, we try to check if a mutex really locks. We could try that
-// with a deadlock, but that's not practical (the test would not end).
-//
-// Instead, we try do to some operation on the same data from multiple threads
-// that's likely to break if not locked. Also, the test must run for a while
-// to have an opportunity to manifest.
-//
-// Currently we try incrementing a double variable. That one is large enough
-// and complex enough so it should not be possible for the CPU to do it as an
-// atomic operation, at least on common architectures.
-const size_t iterations = 100000;
-
-void
-performIncrement(volatile double* canary, volatile bool* ready_me,
- volatile bool* ready_other, Mutex* mutex)
-{
- // Loosely (busy) wait for the other thread so both will start
- // approximately at the same time.
- *ready_me = true;
- while (!*ready_other) {}
-
- for (size_t i = 0; i < iterations; ++i) {
- Mutex::Locker lock(*mutex);
- *canary += 1;
- }
-}
-
-void
-noHandler(int) {}
-
-TEST(MutexTest, swarm) {
- if (!isc::util::unittests::runningOnValgrind()) {
- // Create a timeout in case something got stuck here
- struct sigaction ignored, original;
- memset(&ignored, 0, sizeof(ignored));
- ignored.sa_handler = noHandler;
- if (sigaction(SIGALRM, &ignored, &original)) {
- FAIL() << "Couldn't set alarm";
- }
- alarm(10);
- // This type has a low chance of being atomic itself, further raising
- // the chance of problems appearing.
- double canary = 0;
- Mutex mutex;
- // Run two parallel threads
- bool ready1 = false;
- bool ready2 = false;
- Thread t1(boost::bind(&performIncrement, &canary, &ready1, &ready2,
- &mutex));
- Thread t2(boost::bind(&performIncrement, &canary, &ready2, &ready1,
- &mutex));
- t1.wait();
- t2.wait();
- // Check it the sum is the expected value.
- EXPECT_EQ(iterations * 2, canary) << "Threads are badly synchronized";
- // Cancel the alarm and return the original handler
- alarm(0);
- if (sigaction(SIGALRM, &original, NULL)) {
- FAIL() << "Couldn't restore alarm";
- }
- }
-}
-
-}
diff --git a/src/lib/util/threads/tests/run_unittests.cc b/src/lib/util/threads/tests/run_unittests.cc
deleted file mode 100644
index d55ce68c6e..0000000000
--- a/src/lib/util/threads/tests/run_unittests.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <gtest/gtest.h>
-#include <util/unittests/run_all.h>
-#include <stdlib.h>
-
-// This file uses TEST_DATA_TOPBUILDDIR macro, which must point to a writable
-// directory. It will be used for creating a logger lockfile.
-
-int
-main(int argc, char* argv[]) {
- ::testing::InitGoogleTest(&argc, argv);
-
- setenv("KEA_LOCKFILE_DIR", TEST_DATA_TOPBUILDDIR, 1);
- return (isc::util::unittests::run_all());
-}
diff --git a/src/lib/util/threads/tests/thread_unittest.cc b/src/lib/util/threads/tests/thread_unittest.cc
deleted file mode 100644
index bf95d3c008..0000000000
--- a/src/lib/util/threads/tests/thread_unittest.cc
+++ /dev/null
@@ -1,262 +0,0 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <util/process_spawn.h>
-#include <util/threads/sync.h>
-#include <util/threads/thread.h>
-#include <util/unittests/check_valgrind.h>
-
-#include <boost/bind.hpp>
-#include <boost/scoped_ptr.hpp>
-
-#include <gtest/gtest.h>
-
-#include <signal.h>
-#include <unistd.h>
-
-// This file tests the Thread class. It's hard to test an actual thread is
-// started, but we at least check the function is run and exceptions are
-// propagated as they should.
-//
-// We run some tests multiple times to see if there happen to be a race
-// condition (then it would have better chance showing up).
-//
-// The detached tests are not run as many times to prevent many threads being
-// started in parallel (the other tests wait for the previous one to terminate
-// before starting new one).
-
-using namespace isc::util;
-using namespace isc::util::thread;
-
-namespace {
-const size_t iterations = 200;
-const size_t detached_iterations = 25;
-
-/// @brief Implements a thread which can be stopped.
-///
-/// This class implements a worker thread which can be stopped by calling
-/// StoppableThread::stop. The call to this function blocks until the thread
-/// terminates. This class is useful for testing scenarios when the thread
-/// needs to be run for a specific amount of time.
-class StoppableThread {
-public:
-
- /// @brief Constructor.
- ///
- /// It doesn't run the thread yet. It merely initializes required
- /// class members.
- StoppableThread()
- : stopping_(false), mutex_(), thread_() {
- }
-
- /// @brief Destructor.
- ///
- /// Detaches the thread.
- ~StoppableThread() {
- }
-
- /// @brief Starts the execution of the thread.
- ///
- /// This method will not start the thread if the thread has been stopped.
- /// In order to start the thread again, @c StoppableThread::reset must be
- /// called first.
- void run() {
- if (!amStopping()) {
- thread_.reset(new Thread(boost::bind(&StoppableThread::loop, this)));
- }
- }
-
- /// @brief Stops the thread as soon as possible.
- void stop() {
- if (!amStopping()) {
- setStopping(true);
- if (thread_) {
- thread_->wait();
- thread_.reset();
- }
- }
- }
-
- /// @brief Resets the thread state so as it can be ran again.
- void reset() {
- setStopping(false);
- }
-
-private:
-
- /// @brief Checks if the thread is being stopped.
- ///
- /// @return true if the thread is being stopped.
- bool amStopping() {
- Mutex::Locker lock(mutex_);
- return (stopping_);
- }
-
- /// @brief Sets the stopping state.
- ///
- /// @param stopping New value for @c stopping_ state.
- void setStopping(const bool stopping) {
- Mutex::Locker lock(mutex_);
- stopping_ = stopping;
- }
-
- /// @brief Worker thread loop.
- ///
- /// It runs until the @c StoppableThread::stop is called.
- void loop() {
- for (;;) {
- if (amStopping()) {
- break;
- }
- usleep(100);
- }
- }
-
- /// @brief Flag indicating if the thread is being stopped.
- bool stopping_;
- /// @brief Mutex used for protecting @c stopping_ flag.
- Mutex mutex_;
- /// @brief Pointer to the thread instance.
- boost::scoped_ptr<Thread> thread_;
-};
-
-/// @brief Static instance of the stoppable thread.
-boost::scoped_ptr<StoppableThread> thread;
-
-/// @brief Test fixture class for testing @c Thread.
-class ThreadTest : public ::testing::Test {
-public:
-
- /// @brief Destructor.
- ///
- /// Stops the thread and resets the static pointer to
- /// @c StoppableThread.
- virtual ~ThreadTest() {
- if (thread) {
- thread->stop();
- }
- thread.reset();
- }
-
- /// @brief No-op method.
- static void doSomething(int*) { }
-
- /// @brief Marks specified boolean value as true to indicate that the
- /// function has been run.
- static void markRun(bool* mark) {
- EXPECT_FALSE(*mark);
- *mark = true;
- }
-
- /// @brief Throws 42.
- static void throwSomething() {
- throw 42; // Throw something really unusual, to see everything is caught.
- }
-
- /// @brief Throws standard exception.
- static void throwException() {
- throw std::exception();
- }
-
- /// @brief Returns signal mask set for a thread.
- ///
- /// @parm mask Pointer to signal mask set for the calling thread.
- static void getSignalMask(sigset_t* mask) {
- pthread_sigmask(SIG_SETMASK, 0, mask);
- }
-};
-
-
-
-// We just test that we can forget about the thread and nothing
-// bad will happen on our side.
-TEST_F(ThreadTest, detached) {
- if (!isc::util::unittests::runningOnValgrind()) {
- int x;
- for (size_t i = 0; i < detached_iterations; ++i) {
- Thread thread(boost::bind(&ThreadTest::doSomething, &x));
- }
- }
-}
-
-// Wait for a thread to end first. The variable must be set at the time.
-TEST_F(ThreadTest, wait) {
- if (!isc::util::unittests::runningOnValgrind()) {
- for (size_t i = 0; i < iterations; ++i) {
- bool mark = false;
- Thread thread(boost::bind(&ThreadTest::markRun, &mark));
- thread.wait();
- ASSERT_TRUE(mark) << "Not finished yet in " << i << "th iteration";
- // Can't wait second time
- ASSERT_THROW(thread.wait(), isc::InvalidOperation);
- }
- }
-}
-
-// Exception in the thread we forget about should not do anything to us
-TEST_F(ThreadTest, detachedException) {
- if (!isc::util::unittests::runningOnValgrind()) {
- for (size_t i = 0; i < detached_iterations; ++i) {
- Thread thread(&ThreadTest::throwSomething);
- }
- for (size_t i = 0; i < detached_iterations; ++i) {
- Thread thread(&ThreadTest::throwException);
- }
- }
-}
-
-// An uncaught exception in the thread should propagate through wait
-TEST_F(ThreadTest, exception) {
- if (!isc::util::unittests::runningOnValgrind()) {
- for (size_t i = 0; i < iterations; ++i) {
- Thread thread(throwSomething);
- Thread thread2(throwException);
- ASSERT_THROW(thread.wait(), Thread::UncaughtException);
- ASSERT_THROW(thread2.wait(), Thread::UncaughtException);
- }
- }
-}
-
-// Verify that all signals are blocked.
-TEST_F(ThreadTest, sigmask) {
- sigset_t mask;
- sigemptyset(&mask);
- Thread thread(boost::bind(&ThreadTest::getSignalMask, &mask));
- ASSERT_NO_THROW(thread.wait());
- EXPECT_EQ(1, sigismember(&mask, SIGHUP));
- EXPECT_EQ(1, sigismember(&mask, SIGINT));
- EXPECT_EQ(1, sigismember(&mask, SIGTERM));
-}
-
-
-/// This test verifies using threads and spawning child processes
-/// work together.
-TEST_F(ThreadTest, spawnProcessWithThread) {
- // Initialize and run the stoppable thread. Note that the 'thread'
- // is a static variable, which will be 'cloned' into the child
- // process. Its destructor must not be called when the child process
- // terminates with EXIT_FAILURE status.
- thread.reset(new StoppableThread());
- thread->run();
-
- // Spawn the new process, using some non-existing executable. The
- // current process will fork but the execvp should fail.
- ProcessSpawn process_spawn("kea-dhcp4-a86570943h");
- pid_t pid = process_spawn.spawn();
- // Wait for the process to terminate.
- while (process_spawn.isRunning(pid)) {
- usleep(100);
- }
- // When the child process terminates it will call _exit() so
- // nothing bad should happen from the child.
- EXPECT_EQ(EXIT_FAILURE, process_spawn.getExitStatus(pid));
- // The thread is still there.
- EXPECT_TRUE(thread);
-}
-
-}
diff --git a/src/lib/util/threads/thread.cc b/src/lib/util/threads/thread.cc
deleted file mode 100644
index 54f67da6c8..0000000000
--- a/src/lib/util/threads/thread.cc
+++ /dev/null
@@ -1,194 +0,0 @@
-// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <util/threads/thread.h>
-#include <util/threads/sync.h>
-
-#include <memory>
-#include <string>
-#include <cstring>
-#include <cerrno>
-
-#include <pthread.h>
-#include <signal.h>
-
-#include <boost/noncopyable.hpp>
-#include <boost/scoped_ptr.hpp>
-
-using std::string;
-using std::exception;
-using std::unique_ptr;
-using boost::scoped_ptr;
-
-namespace isc {
-namespace util {
-namespace thread {
-
-namespace {
-
-// Signal blocker class.
-class Blocker : boost::noncopyable {
-public:
- // Constructor blocks all signals
- Blocker() {
- sigset_t new_mask;
- sigfillset(&new_mask);
- pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_);
- }
-
- // Destructor restores the previous signal mask
- ~Blocker() {
- pthread_sigmask(SIG_SETMASK, &old_mask_, 0);
- }
-
-private:
- // The previous signal mask
- sigset_t old_mask_;
-};
-
-}
-
-// The implementation of the Thread class.
-//
-// This internal state is not deleted until the thread terminates and is either
-// waited for or detached. We could do this with shared_ptr (or, shared_ptr and
-// weak_ptr), but we plan on compiling boost without thread support, so it
-// might not be safe. Therefore we use an explicit mutex. It is being locked
-// only 2-3 times in the lifetime of the thread, which should be negligible
-// overhead anyway.
-class Thread::Impl {
-public:
- Impl(const boost::function<void ()>& main) :
- // Two things to happen before destruction - thread needs to terminate
- // and the creating thread needs to release it.
- waiting_(2),
- main_(main),
- exception_(false),
- tid_(0)
- {}
- // Another of the waiting events is done. If there are no more, delete
- // impl.
- static void done(Impl* impl) {
- bool should_delete(false);
- { // We need to make sure the mutex is unlocked before it is deleted
- Mutex::Locker locker(impl->mutex_);
- if (--impl->waiting_ == 0) {
- should_delete = true;
- }
- }
- if (should_delete) {
- delete impl;
- }
- }
- // Run the thread. The type of parameter is because the pthread API.
- static void* run(void* impl_raw) {
- Impl* impl = static_cast<Impl*>(impl_raw);
- try {
- impl->main_();
- } catch (const exception& e) {
- impl->exception_ = true;
- impl->exception_text_ = e.what();
- } catch (...) {
- impl->exception_ = true;
- impl->exception_text_ = "Unknown exception";
- }
- done(impl);
- return (NULL);
- }
- // How many events are waiting? One is for the thread to finish, one
- // for the destructor of Thread or wait. Once both happen, this is
- // no longer needed.
- size_t waiting_;
- // The main function of the thread.
- boost::function<void ()> main_;
- // Was there an exception?
- bool exception_;
- string exception_text_;
- // The mutex protects the waiting_ member, which ensures there are
- // no race conditions and collisions when terminating. The other members
- // should be safe, because:
- // * tid_ is read only.
- // * exception_ and exception_text_ is accessed outside of the thread
- // only after join, by that time the thread must have terminated.
- // * main_ is used in a read-only way here. If there are any shared
- // resources used inside, it is up to the main_ itself to take care.
- Mutex mutex_;
- // Which thread are we talking about anyway?
- pthread_t tid_;
-};
-
-Thread::Thread(const boost::function<void ()>& main) :
- impl_(NULL)
-{
- unique_ptr<Impl> impl(new Impl(main));
- Blocker blocker;
- const int result = pthread_create(&impl->tid_, NULL, &Impl::run,
- impl.get());
- // Any error here?
- switch (result) {
- case 0: // All 0K
- impl_ = impl.release();
- break;
- case EAGAIN:
- throw std::bad_alloc();
- default: // Other errors. They should not happen.
- isc_throw(isc::InvalidOperation, std::strerror(result));
- }
-}
-
-Thread::~Thread() {
- if (impl_ != NULL) {
- // In case we didn't call wait yet
- const int result = pthread_detach(impl_->tid_);
- Impl::done(impl_);
- impl_ = NULL;
- // If the detach ever fails, something is screwed rather badly.
- assert(result == 0);
- }
-}
-
-void
-Thread::wait() {
- if (impl_ == NULL) {
- isc_throw(isc::InvalidOperation,
- "Wait called and no thread to wait for");
- }
-
- const int result = pthread_join(impl_->tid_, NULL);
- if (result != 0) {
- isc_throw(isc::InvalidOperation, std::strerror(result));
- }
-
- // Was there an exception in the thread?
- scoped_ptr<UncaughtException> ex;
- // Something here could in theory throw. But we already terminated the thread, so
- // we need to make sure we are in consistent state even in such situation (like
- // releasing the mutex and impl_).
- try {
- if (impl_->exception_) {
- ex.reset(new UncaughtException(__FILE__, __LINE__,
- impl_->exception_text_.c_str()));
- }
- } catch (...) {
- Impl::done(impl_);
- impl_ = NULL;
- // We have eaten the UncaughtException by now, but there's another
- // exception instead, so we have at least something.
- throw;
- }
-
- Impl::done(impl_);
- impl_ = NULL;
- if (ex.get() != NULL) {
- throw UncaughtException(*ex);
- }
-}
-
-}
-}
-}
diff --git a/src/lib/util/threads/thread.h b/src/lib/util/threads/thread.h
deleted file mode 100644
index b64621a094..0000000000
--- a/src/lib/util/threads/thread.h
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#ifndef KEA_THREAD_H
-#define KEA_THREAD_H
-
-#include <exceptions/exceptions.h>
-
-#include <boost/noncopyable.hpp>
-#include <boost/function.hpp>
-
-namespace isc {
-namespace util {
-/// \brief Wrappers for thread related functionality
-///
-/// We provide our own wrappers, currently around pthreads. We tried using
-/// the boost thread support, but it gave us some trouble, so we implemented
-/// in-house ones.
-namespace thread {
-
-/// \brief A separate thread.
-///
-/// A thread of execution. When created, starts running in the background.
-/// You can wait for it then or just forget it ever existed and leave it
-/// live peacefully.
-///
-/// The interface is minimalist for now. We may need to extend it later.
-///
-/// \note While the objects of this class represent another thread, they
-/// are not thread-safe. You're not supposed to call wait() on the same
-/// object from multiple threads or so. They are reentrant (you can
-/// wait for different threads from different threads).
-class Thread : public boost::noncopyable {
-public:
- /// \brief There's an uncaught exception in a thread.
- ///
- /// When a thread terminates because it the main function of the thread
- /// throws, this one is re-thrown out of wait() and contains the what
- /// of the original exception.
- class UncaughtException : public isc::Exception {
- public:
- UncaughtException(const char* file, size_t line, const char* what) :
- Exception(file, line, what)
- {}
- };
-
- /// \brief Create and start a thread.
- ///
- /// Create a new thread and run body inside it.
- ///
- /// If you need to pass parameters to body, or return some result, you
- /// may just want to use boost::bind or alike to store them within the
- /// body functor.
- ///
- /// \note The main functor will be copied internally. You need to consider
- /// this when returning the result.
- ///
- /// The body should terminate by exiting the function. If it throws, it
- /// is considered an error. You should generally catch any exceptions form
- /// within there and handle them somehow.
- ///
- /// \param main The code to run inside the thread.
- ///
- /// \throw std::bad_alloc if allocation of the new thread or other
- /// resources fails.
- /// \throw isc::InvalidOperation for other errors (should not happen).
- Thread(const boost::function<void()>& main);
-
- /// \brief Destructor.
- ///
- /// It is completely legitimate to destroy the thread without calling
- /// wait() before. In such case, the thread will just live on until it
- /// terminates. However, if the thread dies due to exception, for example,
- /// it's up to you to detect that, no error is reported from this class.
- ///
- /// \throw isc::InvalidOperation in the rare case of OS reporting a
- /// problem. This should not happen unless you messed up with the raw
- /// thread by the low-level API.
- ~Thread();
-
- /// \brief Wait for the thread to terminate.
- ///
- /// Waits until the thread terminates. Must be called at most once.
- ///
- /// \throw isc::InvalidOperation if the OS API returns error. This usually
- /// mean a programmer error (like two threads trying to wait on each
- /// other).
- /// \throw isc::InvalidOperation calling wait a second time.
- /// \throw UncaughtException if the thread terminated by throwing an
- /// exception instead of just returning from the function.
- void wait();
-private:
- class Impl;
- Impl* impl_;
-};
-
-/// @brief Thread pointer type.
-typedef boost::shared_ptr<Thread> ThreadPtr;
-
-}
-}
-}
-
-#endif