[Groonga-commit] groonga/grnxx at 10cf17c [master] Update grnxx::Thread.

Back to archive index

susumu.yata null+****@clear*****
Mon Jul 1 18:08:56 JST 2013


susumu.yata	2013-07-01 18:08:56 +0900 (Mon, 01 Jul 2013)

  New Revision: 10cf17c436cdf4589535694b6c5138784c9f363e
  https://github.com/groonga/grnxx/commit/10cf17c436cdf4589535694b6c5138784c9f363e

  Message:
    Update grnxx::Thread.
    
    Throw exceptions on failure, instead of returning false.

  Modified files:
    lib/grnxx/periodic_clock.cpp
    lib/grnxx/thread.cpp
    lib/grnxx/thread.hpp
    test/test_thread.cpp

  Modified: lib/grnxx/periodic_clock.cpp (+10 -5)
===================================================================
--- lib/grnxx/periodic_clock.cpp    2013-07-01 18:08:06 +0900 (e33a1ed)
+++ lib/grnxx/periodic_clock.cpp    2013-07-01 18:08:56 +0900 (96a5615)
@@ -19,6 +19,7 @@
 
 #include "grnxx/thread.hpp"
 #include "grnxx/lock.hpp"
+#include "grnxx/logger.hpp"
 #include "grnxx/mutex.hpp"
 
 namespace grnxx {
@@ -39,11 +40,11 @@ Time PeriodicClock::now_ = Time::min();
 PeriodicClock::PeriodicClock() {
   // Start the internal thread iff this is the first object.
   Lock lock(&mutex);
-  if (++ref_count == 1) {
+  if (++ref_count == 1) try {
     thread = grnxx::Thread::create(routine);
-    if (thread) {
-      now_ = SystemClock::now();
-    }
+    now_ = SystemClock::now();
+  } catch (...) {
+    GRNXX_WARNING() << "failed to create thread for PeriodicClock";
   }
 }
 
@@ -51,7 +52,11 @@ PeriodicClock::~PeriodicClock() {
   // Stop the running thread iff this is the last object.
   Lock lock(&mutex);
   if (--ref_count == 0) {
-    thread->join();
+    try {
+      thread->detach();
+    } catch (...) {
+      GRNXX_WARNING() << "failed to detach thread for PeriodicClock";
+    }
     delete thread;
     thread = nullptr;
     now_ = Time::min();

  Modified: lib/grnxx/thread.cpp (+85 -52)
===================================================================
--- lib/grnxx/thread.cpp    2013-07-01 18:08:06 +0900 (e8cca39)
+++ lib/grnxx/thread.cpp    2013-07-01 18:08:56 +0900 (b321d6c)
@@ -36,17 +36,50 @@
 # include <cerrno>
 #endif  // GRNXX_WINDOWS
 
+#include <memory>
+#include <new>
+
 // TODO: Use the following in future.
 //#include <chrono>
 //#include <thread>
 
 #include "grnxx/errno.hpp"
+#include "grnxx/exception.hpp"
 #include "grnxx/logger.hpp"
+#include "grnxx/string_builder.hpp"
 #include "grnxx/system_clock.hpp"
+#include "grnxx/types.hpp"
 
 namespace grnxx {
 namespace {
 
+enum ThreadStatus : uint32_t {
+  THREAD_INITIAL  = 0,
+  THREAD_JOINABLE = 1,
+  THREAD_JOINED   = 2,
+  THREAD_DETACHED = 3
+};
+
+StringBuilder &operator<<(StringBuilder &builder, ThreadStatus status) {
+  switch (status) {
+    case THREAD_INITIAL: {
+      return builder << "THREAD_INITIAL";
+    }
+    case THREAD_JOINABLE: {
+      return builder << "THREAD_JOINABLE";
+    }
+    case THREAD_JOINED: {
+      return builder << "THREAD_JOINED";
+    }
+    case THREAD_DETACHED: {
+      return builder << "THREAD_DETACHED";
+    }
+    default: {
+      return builder << "n/a";
+    }
+  }
+}
+
 class ThreadImpl : public Thread {
  public:
   using Routine = Thread::Routine;
@@ -54,10 +87,10 @@ class ThreadImpl : public Thread {
   ThreadImpl();
   ~ThreadImpl();
 
-  bool start(const Routine &routine);
+  void start(const Routine &routine);
 
-  bool join();
-  bool detach();
+  void join();
+  void detach();
 
  private:
 #ifdef GRNXX_WINDOWS
@@ -65,7 +98,7 @@ class ThreadImpl : public Thread {
 #else  // GRNXX_WINDOWS
   pthread_t thread_;
 #endif  // GRNXX_WINDOWS
-  bool joinable_;
+  ThreadStatus status_;
 
 #ifdef GRNXX_WINDOWS
   static unsigned thread_main(void *arg);
@@ -74,95 +107,97 @@ class ThreadImpl : public Thread {
 #endif  // GRNXX_WINDOWS
 };
 
-ThreadImpl::ThreadImpl() : thread_(), joinable_(false) {}
+ThreadImpl::ThreadImpl() : thread_(), status_(THREAD_INITIAL) {}
 
 ThreadImpl::~ThreadImpl() {
-  // A thread must be join()ed or detach()ed.
-  if (joinable_) {
-    GRNXX_ERROR() << "running thread";
+  // A thread must be joined or detached before destruction.
+  if (status_ == THREAD_JOINABLE) {
+    GRNXX_WARNING() << "Bad thread destruction: status = " << status_;
   }
 }
 
-bool ThreadImpl::start(const Routine &routine) {
+void ThreadImpl::start(const Routine &routine) {
   std::unique_ptr<Routine> routine_clone(new (std::nothrow) Routine(routine));
   if (!routine_clone) {
-    GRNXX_ERROR() << "new std::function<void()> failed";
-    return false;
+    GRNXX_ERROR() << "new grnxx::Thread::Routine failed";
+    throw MemoryError();
   }
 #ifdef GRNXX_WINDOWS
   const uintptr_t handle = ::_beginthreadex(nullptr, 0, thread_main,
                                             routine_clone.get(), 0, nullptr);
   if (handle == 0) {
-    GRNXX_ERROR() << "failed to create thread: '_beginthreadex' "
-                  << Errno(errno);
-    return false;
+    const Errno error_code(errno);
+    GRNXX_ERROR() << "failed to create thread: call = _beginthreadex"
+                  << ", errno = " << error_code;
+    throw SystemError(error_code);
   }
   thread_ = reinterpret_cast<HANDLE>(handle);
 #else  // GRNXX_WINDOWS
   const int error = ::pthread_create(&thread_, nullptr, thread_main,
                                      routine_clone.get());
   if (error != 0) {
-    GRNXX_ERROR() << "failed to create thread: '::pthread_create' "
-                  << Errno(error);
-    return false;
+    const Errno error_code(error);
+    GRNXX_ERROR() << "failed to create thread: call = ::pthread_create"
+                  << ", errno = " << error_code;
+    throw SystemError(error_code);
   }
 #endif  // GRNXX_WINDOWS
   routine_clone.release();
-  joinable_ = true;
-  return true;
+  status_ = THREAD_JOINABLE;
 }
 
-bool ThreadImpl::join() {
-  if (!joinable_) {
-    GRNXX_ERROR() << "invalid operation: joinable = false";
-    return false;
+void ThreadImpl::join() {
+  if (status_ != THREAD_JOINABLE) {
+    GRNXX_ERROR() << "invalid operation: status = " << status_;
+    throw LogicError();
   }
-  bool result = true;
+  status_ = THREAD_JOINED;
 #ifdef GRNXX_WINDOWS
   if (::WaitForSingleObject(thread_, INFINITE) == WAIT_FAILED) {
-    GRNXX_ERROR() << "failed to join thread: '::WaitForSingleObject' "
-                  << Errno(::GetLastError());
-    result = false;
+    const Errno error_code(::GetLastError());
+    GRNXX_ERROR() << "failed to join thread: call = ::WaitForSingleObject"
+                  << ", errno = " << error_code;
+    throw SystemError(error_code);
   }
   if (::CloseHandle(thread_) != 0) {
-    GRNXX_ERROR() << "failed to close thread: '::CloseHandle' "
-                  << Errno(::GetLastError());
-    result = false;
+    const Errno error_code(::GetLastError());
+    GRNXX_ERROR() << "failed to close thread: call = ::CloseHandle"
+                  << ", errno = " << error_code;
+    throw SystemError(error_code);
   }
 #else  // GRNXX_WINDOWS
   const int error = ::pthread_join(thread_, nullptr);
   if (error != 0) {
-    GRNXX_ERROR() << "failed to join thread: '::pthread_join' "
-                  << Errno(error);
-    result = false;
+    const Errno error_code(error);
+    GRNXX_ERROR() << "failed to join thread: call = ::pthread_join"
+                  << ", errno = " << error_code;
+    throw SystemError(error_code);
   }
 #endif  // GRNXX_WINDOWS
-  joinable_ = false;
-  return result;
 }
 
-bool ThreadImpl::detach() {
-  if (!joinable_) {
-    GRNXX_ERROR() << "invalid operation: joinable = false";
-    return false;
+void ThreadImpl::detach() {
+  if (status_ != THREAD_JOINABLE) {
+    GRNXX_ERROR() << "invalid operation: status = " << status_;
+    throw LogicError();
   }
-  bool result = true;
+  status_ = THREAD_DETACHED;
 #ifdef GRNXX_WINDOWS
   if (::CloseHandle(thread_) != 0) {
-    GRNXX_ERROR() << "failed to detach thread: '::CloseHandle' "
-                  << Errno(::GetLastError());
-    result = false;
+    const Errno error_code(::GetLastError());
+    GRNXX_ERROR() << "failed to detach thread: call = ::CloseHandle"
+                  << ", errno = " << error_code;
+    throw SystemError(error_code);
   }
 #else  // GRNXX_WINDOWS
   const int error = ::pthread_detach(thread_);
   if (error != 0) {
-    GRNXX_ERROR() << "failed to detach thread: '::pthread_detach' "
-                  << Errno(error);
-    result = false;
+    const Errno error_code(error);
+    GRNXX_ERROR() << "failed to detach thread: call = ::pthread_detach"
+                  << ", errno = " << error_code;
+    throw SystemError(error_code);
   }
 #endif  // GRNXX_WINDOWS
-  joinable_ = false;
-  return result;
 }
 
 #ifdef GRNXX_WINDOWS
@@ -189,11 +224,9 @@ Thread *Thread::create(const Routine &routine) {
   std::unique_ptr<ThreadImpl> thread(new (std::nothrow) ThreadImpl);
   if (!thread) {
     GRNXX_ERROR() << "new grnxx::ThreadImpl failed";
-    return nullptr;
-  }
-  if (!thread->start(routine)) {
-    return nullptr;
+    throw MemoryError();
   }
+  thread->start(routine);
   return thread.release();
 }
 

  Modified: lib/grnxx/thread.hpp (+3 -3)
===================================================================
--- lib/grnxx/thread.hpp    2013-07-01 18:08:06 +0900 (1c419f0)
+++ lib/grnxx/thread.hpp    2013-07-01 18:08:56 +0900 (36e923e)
@@ -35,7 +35,7 @@ class Thread {
   virtual ~Thread();
 
   // Create a thread.
-  static Thread *create(const std::function<void()> &routine);
+  static Thread *create(const Routine &routine);
 
   // Yield the processor/core associated with the current thread.
   static void yield();
@@ -46,9 +46,9 @@ class Thread {
   static void sleep_until(Time time);
 
   // Wait until the thread finishes.
-  virtual bool join() = 0;
+  virtual void join() = 0;
   // Separate the thread from this object.
-  virtual bool detach() = 0;
+  virtual void detach() = 0;
 };
 
 }  // namespace grnxx

  Modified: test/test_thread.cpp (+3 -3)
===================================================================
--- test/test_thread.cpp    2013-07-01 18:08:06 +0900 (3b55875)
+++ test/test_thread.cpp    2013-07-01 18:08:56 +0900 (ee11521)
@@ -74,7 +74,7 @@ int main() {
   stopwatch.reset();
   std::unique_ptr<grnxx::Thread> thread(grnxx::Thread::create(thread_routine));
   assert(thread);
-  assert(thread->join());
+  thread->join();
   elapsed = stopwatch.elapsed();
   GRNXX_NOTICE() << "thread + join: elapsed [ns] = "
                  << (1000.0 * elapsed.count());
@@ -84,7 +84,7 @@ int main() {
     grnxx::Thread::sleep_for(grnxx::Duration::milliseconds(10));
   }));
   assert(thread);
-  assert(thread->join());
+  thread->join();
   elapsed = stopwatch.elapsed();
   GRNXX_NOTICE() << "thread + join: elapsed [ns] = "
                  << (1000.0 * elapsed.count());
@@ -94,7 +94,7 @@ int main() {
     grnxx::Thread::sleep_for(grnxx::Duration::milliseconds(10));
   }));
   assert(thread);
-  assert(thread->detach());
+  thread->detach();
   elapsed = stopwatch.elapsed();
   GRNXX_NOTICE() << "thread + detach: elapsed [ns] = "
                  << (1000.0 * elapsed.count());
-------------- next part --------------
HTML����������������������������...
Download 



More information about the Groonga-commit mailing list
Back to archive index