BIND 10 trac2202, updated. 3559ddcbe67e91941139c8c74ed77bb9be501dc4 [2202] Implement Thread wrapper

BIND 10 source code commits bind10-changes at lists.isc.org
Mon Sep 3 12:14:15 UTC 2012


The branch, trac2202 has been updated
       via  3559ddcbe67e91941139c8c74ed77bb9be501dc4 (commit)
       via  53ea5695fbd2a596ffda7c9b7bf0dd503b101462 (commit)
      from  ec9e57a32cefe7db94c2b8b8bebc782b8e872fa9 (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 3559ddcbe67e91941139c8c74ed77bb9be501dc4
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Mon Sep 3 14:13:14 2012 +0200

    [2202] Implement Thread wrapper

commit 53ea5695fbd2a596ffda7c9b7bf0dd503b101462
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Mon Sep 3 13:04:12 2012 +0200

    Tests for the Thread class

-----------------------------------------------------------------------

Summary of changes:
 src/lib/util/threads/tests/Makefile.am |    1 +
 src/lib/util/threads/thread.cc         |  136 ++++++++++++++++++++++++++++++++
 src/lib/util/threads/thread.h          |   10 ++-
 3 files changed, 145 insertions(+), 2 deletions(-)

-----------------------------------------------------------------------
diff --git a/src/lib/util/threads/tests/Makefile.am b/src/lib/util/threads/tests/Makefile.am
index b549b95..435e8e8 100644
--- a/src/lib/util/threads/tests/Makefile.am
+++ b/src/lib/util/threads/tests/Makefile.am
@@ -21,6 +21,7 @@ TESTS =
 if HAVE_GTEST
 TESTS += run_unittests
 run_unittests_SOURCES  = run_unittests.cc
+run_unittests_SOURCES += thread_unittest.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
diff --git a/src/lib/util/threads/thread.cc b/src/lib/util/threads/thread.cc
index 052566f..1393c9a 100644
--- a/src/lib/util/threads/thread.cc
+++ b/src/lib/util/threads/thread.cc
@@ -13,3 +13,139 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include "thread.h"
+#include "lock.h"
+
+#include <memory>
+#include <string>
+#include <cstring>
+#include <cerrno>
+
+#include <pthread.h>
+
+using namespace std;
+
+namespace isc {
+namespace util {
+namespace thread {
+
+// 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) :
+        waiting_(2),
+        main_(main),
+        exception_(false)
+    {}
+    // 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 = reinterpret_cast<Impl*>(impl_raw);
+        try {
+            impl->main_();
+        }
+        catch (const exception& e) {
+            Mutex::Locker locker(impl->mutex);
+            impl->exception_ = true;
+            impl->exception_text_ = e.what();
+        }
+        catch (...) {
+            Mutex::Locker locker(impl->mutex);
+            impl->exception_ = true;
+        }
+        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_;
+    Mutex mutex;
+    // Which thread are we talking about anyway?
+    pthread_t tid;
+};
+
+Thread::Thread(const boost::function<void ()>& main) :
+    impl_(NULL)
+{
+    auto_ptr<Impl> impl(new Impl(main));
+    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, strerror(result));
+    }
+}
+
+Thread::~ Thread() {
+    if (impl_ != NULL) {
+        // In case we didn't call wait yet
+        int result = pthread_detach(impl_->tid);
+        Impl::done(impl_);
+        impl_ = NULL;
+        if (result != 0) {
+            // Yes, really throwing from destructor. But this would
+            // mean someone really messed up the internal state, so
+            // we need to do something about it, even if it causes
+            // application to terminate.
+            isc_throw(isc::InvalidOperation, strerror(result));
+        }
+    }
+}
+
+void
+Thread::wait() {
+    if (impl_ == NULL) {
+        isc_throw(isc::InvalidOperation, "Wait called and no thread to wait for");
+    }
+
+    int result = pthread_join(impl_->tid, NULL);
+    if (result != 0) {
+        isc_throw(isc::InvalidOperation, strerror(result));
+    }
+
+    // Was there an exception in the thread?
+    auto_ptr<UncaughtException> ex;
+    if (impl_->exception_) {
+        ex.reset(new UncaughtException(__FILE__, __LINE__,
+                                       impl_->exception_text_.c_str()));
+    }
+    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
index 16aa7a5..0ddae72 100644
--- a/src/lib/util/threads/thread.h
+++ b/src/lib/util/threads/thread.h
@@ -31,6 +31,11 @@ namespace thread {
 /// live peacefully.
 ///
 /// The interface is minimalistic 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.
@@ -56,11 +61,12 @@ public:
     /// is considered an error. You should generally catch any exceptions form
     /// within there and handle them somehow.
     ///
-    /// \param body The code to run inside the thread.
+    /// \param main The code to run inside the thread.
     ///
     /// \throw std::bad_alloc if allocation of the new thread or other resources
     ///     fails.
-    Thread(boost::function<void()> body);
+    /// \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



More information about the bind10-changes mailing list