susumu.yata
null+****@clear*****
Tue Apr 30 22:04:33 JST 2013
susumu.yata 2013-04-30 22:04:33 +0900 (Tue, 30 Apr 2013) New Revision: c5b2f4aeba3cb9d6218d9f1ee93c425179f8361b https://github.com/groonga/grnxx/commit/c5b2f4aeba3cb9d6218d9f1ee93c425179f8361b Message: Implement grnxx::Thread. Modified files: lib/grnxx/thread.cpp lib/grnxx/thread.hpp Modified: lib/grnxx/thread.cpp (+157 -0) =================================================================== --- lib/grnxx/thread.cpp 2013-04-30 16:38:46 +0900 (5d02d79) +++ lib/grnxx/thread.cpp 2013-04-30 22:04:33 +0900 (8df15dd) @@ -18,7 +18,11 @@ #include "grnxx/thread.hpp" #ifdef GRNXX_WINDOWS +# include <errno.h> +# include <process.h> # include <windows.h> +#else // GRNXX_WINDOWS +# include <pthread.h> #endif // GRNXX_WINDOWS #ifdef GRNXX_HAS_SCHED_YIELD @@ -33,9 +37,162 @@ //#include <chrono> //#include <thread> +#include "grnxx/error.hpp" +#include "grnxx/logger.hpp" #include "grnxx/time/system_clock.hpp" namespace grnxx { +namespace { + +class ThreadImpl : public Thread { + public: + using Routine = Thread::Routine; + + ThreadImpl(); + ~ThreadImpl(); + + bool start(const Routine &routine); + + bool join(); + bool detach(); + + private: +#ifdef GRNXX_WINDOWS + HANDLE thread_; +#else // GRNXX_WINDOWS + pthread_t thread_; +#endif // GRNXX_WINDOWS + bool joinable_; + +#ifdef GRNXX_WINDOWS + static unsigned thread_main(void *arg); +#else // GRNXX_WINDOWS + static void *thread_main(void *arg); +#endif // GRNXX_WINDOWS +}; + +ThreadImpl::ThreadImpl() : thread_(), joinable_(false) {} + +ThreadImpl::~ThreadImpl() { + // A thread must be join()ed or detach()ed. + if (joinable_) { + GRNXX_ERROR() << "running thread"; + } +} + +bool 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; + } +#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' " + << Error(errno); + return false; + } + 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' " + << Error(error); + return false; + } +#endif // GRNXX_WINDOWS + routine_clone.release(); + joinable_ = true; + return true; +} + +bool ThreadImpl::join() { + if (!joinable_) { + GRNXX_ERROR() << "invalid operation: joinable = false"; + return false; + } + bool result = true; +#ifdef GRNXX_WINDOWS + if (::WaitForSingleObject(thread_, INFINITE) == WAIT_FAILED) { + GRNXX_ERROR() << "failed to join thread: '::WaitForSingleObject' " + << Error(::GetLastError()); + result = false; + } + if (::CloseHandle(thread_) != 0) { + GRNXX_ERROR() << "failed to close thread: '::CloseHandle' " + << Error(::GetLastError()); + result = false; + } +#else // GRNXX_WINDOWS + const int error = ::pthread_join(thread_, nullptr); + if (error != 0) { + GRNXX_ERROR() << "failed to join thread: '::pthread_join' " + << Error(error); + result = false; + } +#endif // GRNXX_WINDOWS + joinable_ = false; + return result; +} + +bool ThreadImpl::detach() { + if (!joinable_) { + GRNXX_ERROR() << "invalid operation: joinable = false"; + return false; + } + bool result = true; +#ifdef GRNXX_WINDOWS + if (::CloseHandle(thread_) != 0) { + GRNXX_ERROR() << "failed to detach thread: '::CloseHandle' " + << Error(::GetLastError()); + result = false; + } +#else // GRNXX_WINDOWS + const int error = ::pthread_detach(thread_); + if (error != 0) { + GRNXX_ERROR() << "failed to detach thread: '::pthread_detach' " + << Error(error); + result = false; + } +#endif // GRNXX_WINDOWS + joinable_ = false; + return result; +} + +#ifdef GRNXX_WINDOWS +unsigned ThreadImpl::thread_main(void *arg) { + std::unique_ptr<Routine> routine(static_cast<Routine *>(arg)); + (*routine)(); + ::_endthreadex(0); + return 0; +} +#else // GRNXX_WINDOWS +void *ThreadImpl::thread_main(void *arg) { + std::unique_ptr<Routine> routine(static_cast<Routine *>(arg)); + (*routine)(); + return nullptr; +} +#endif // GRNXX_WINDOWS + +} // namespace + +Thread::Thread() {} +Thread::~Thread() {} + +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; + } + return thread.release(); +} void Thread::yield() { #ifdef GRNXX_WINDOWS Modified: lib/grnxx/thread.hpp (+17 -3) =================================================================== --- lib/grnxx/thread.hpp 2013-04-30 16:38:46 +0900 (de7e851) +++ lib/grnxx/thread.hpp 2013-04-30 22:04:33 +0900 (fd81cb8) @@ -18,6 +18,8 @@ #ifndef GRNXX_THREAD_HPP #define GRNXX_THREAD_HPP +#include <functional> + #include "grnxx/basic.hpp" #include "grnxx/time/time.hpp" @@ -25,14 +27,26 @@ namespace grnxx { class Thread { public: + using Routine = std::function<void()>; + + Thread(); + virtual ~Thread(); + + // Create a thread. + static Thread *create(const std::function<void()> &routine); + + // Yield the processor/core associated with the current thread. static void yield(); + // Sleep for "duration". static void sleep_for(Duration duration); + // Sleep until "time". static void sleep_until(Time time); - private: - Thread(const Thread &); - Thread &operator=(const Thread &); + // Wait until the thread finishes. + virtual bool join() = 0; + // Separate the thread from this object. + virtual bool detach() = 0; }; } // namespace grnxx -------------- next part -------------- HTML����������������������������...Download