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