| // Copyright 2010-2015, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "base/thread.h" |
| |
| #ifdef OS_WIN |
| #include <windows.h> |
| #include <process.h> // for _beginthreadex |
| #else |
| #include <pthread.h> |
| #endif // OS_WIN |
| |
| #include "base/logging.h" |
| |
| namespace mozc { |
| |
| #ifdef OS_WIN |
| // Win32-based Thread implementation. |
| |
| namespace { |
| |
| unsigned __stdcall WrapperForWindows(void *ptr) { |
| Thread *p = static_cast<Thread *>(ptr); |
| p->Run(); |
| return 0; |
| } |
| |
| } // namespace |
| |
| struct ThreadInternalState { |
| public: |
| ThreadInternalState() |
| : handle_(NULL), |
| joinable_(true) {} |
| |
| HANDLE handle_; |
| bool joinable_; |
| }; |
| |
| void Thread::Start() { |
| if (IsRunning()) { |
| return; |
| } |
| |
| Detach(); |
| state_->handle_ = reinterpret_cast<HANDLE>(_beginthreadex( |
| NULL, 0, WrapperForWindows, this, 0, NULL)); |
| } |
| |
| bool Thread::IsRunning() const { |
| DWORD result = 0; |
| if (state_->handle_ == NULL || |
| !::GetExitCodeThread(state_->handle_, &result)) { |
| return false; |
| } |
| return (STILL_ACTIVE == result); |
| } |
| |
| void Thread::Detach() { |
| if (state_->handle_ != NULL) { |
| ::CloseHandle(state_->handle_); |
| state_->handle_ = NULL; |
| } |
| } |
| |
| void Thread::Join() { |
| if (!state_->joinable_) { |
| return; |
| } |
| if (state_->handle_ == NULL) { |
| return; |
| } |
| ::WaitForSingleObject(state_->handle_, INFINITE); |
| ::CloseHandle(state_->handle_); |
| state_->handle_ = NULL; |
| } |
| |
| void Thread::Terminate() { |
| if (state_->handle_ != NULL) { |
| ::TerminateThread(state_->handle_, 0); |
| state_->handle_ = NULL; |
| } |
| } |
| |
| #else |
| // Thread implementation for pthread-based platforms. Currently all the |
| // platforms except for Windows use pthread. |
| |
| struct ThreadInternalState { |
| public: |
| ThreadInternalState() : is_running_(false), joinable_(true) {} |
| |
| // As pthread_t is an opaque object, we use (pthread_t *)NULL to |
| // indicate that no thread is attached to this object. |
| // When |handle_.get() != NULL|, |*handle_| should indicate a |
| // valid thread id. |
| scoped_ptr<pthread_t> handle_; |
| bool is_running_; |
| bool joinable_; |
| }; |
| |
| void Thread::Start() { |
| if (IsRunning()) { |
| return; |
| } |
| |
| Detach(); |
| state_->is_running_ = true; |
| state_->handle_.reset(new pthread_t); |
| if (0 != pthread_create(state_->handle_.get(), 0, &Thread::WrapperForPOSIX, |
| static_cast<void *>(this))) { |
| state_->is_running_ = false; |
| state_->handle_.reset(NULL); |
| } |
| } |
| |
| bool Thread::IsRunning() const { |
| return state_->is_running_; |
| } |
| |
| void Thread::Detach() { |
| state_->handle_.reset(NULL); |
| } |
| |
| void Thread::Join() { |
| if (!state_->joinable_) { |
| return; |
| } |
| if (state_->handle_.get() == NULL) { |
| return; |
| } |
| pthread_join(*state_->handle_, NULL); |
| state_->handle_.reset(NULL); |
| } |
| |
| namespace { |
| |
| #ifdef OS_ANDROID |
| |
| void ExitThread(int sig) { |
| pthread_exit(0); |
| } |
| |
| // We don't have pthread_cancel for Android, so we'll use SIGUSR1 as |
| // work around. |
| void InitPThreadCancel() { |
| struct sigaction actions; |
| memset(&actions, 0, sizeof(actions)); |
| sigemptyset(&actions.sa_mask); |
| actions.sa_flags = 0; |
| actions.sa_handler = ExitThread; |
| sigaction(SIGUSR1, &actions, NULL); |
| } |
| |
| void PThreadCancel(pthread_t thread_id) { |
| const int pthread_kill_result = pthread_kill(thread_id, SIGUSR1); |
| if (pthread_kill_result != 0) { |
| // pthread_kill fails if |
| // EINVAL: in case that the specified handle_ is invalid |
| // ESRCH: in case that the thread is already terminated |
| LOG(ERROR) << "Failed to kill a thread. error = " << pthread_kill_result |
| << "(" << strerror(pthread_kill_result) << ")"; |
| } |
| } |
| |
| #elif defined(__native_client__) |
| |
| void InitPThreadCancel() { |
| // Nothing is required. |
| } |
| |
| void PThreadCancel(pthread_t thread_id) { |
| LOG(ERROR) << "In NaCl we have no way to cancel a thread."; |
| } |
| |
| #else |
| |
| void InitPThreadCancel() { |
| // Nothing is required. |
| } |
| |
| void PThreadCancel(pthread_t thread_id) { |
| pthread_cancel(thread_id); |
| } |
| |
| #endif // OS_ANDROID or __native_client__ or others |
| |
| #ifndef __native_client__ |
| |
| void PThreadCleanupRoutine(void *ptr) { |
| bool *is_running = static_cast<bool *>(ptr); |
| *is_running = false; |
| } |
| |
| #endif // !__native_client__ |
| |
| } // namespace |
| |
| void *Thread::WrapperForPOSIX(void *ptr) { |
| Thread *p = static_cast<Thread *>(ptr); |
| InitPThreadCancel(); |
| #ifdef __native_client__ |
| { |
| p->Run(); |
| // TODO(horo): In NaCl we can't use pthread_cleanup_push() and |
| // pthread_cleanup_pop(). So we set "is_running_ = false" here. |
| // This hack makes the meaning of IsRunning() different in NaCl. |
| p->state_->is_running_ = false; |
| } |
| #else |
| { |
| // Caveat: the pthread_cleanup_push/pthread_cleanup_pop pair should be put |
| // in the same function. Never move them into any other function. |
| pthread_cleanup_push(PThreadCleanupRoutine, |
| static_cast<void *>(&p->state_->is_running_)); |
| p->Run(); |
| pthread_cleanup_pop(1); |
| } |
| #endif // __native_client__ |
| return NULL; |
| } |
| |
| void Thread::Terminate() { |
| if (state_->handle_ != NULL) { |
| PThreadCancel(*state_->handle_); |
| // pthread_cancel (or pthread_kill in PThreadCancel on Android) is |
| // asynchronous. Join the thread to behave like TerminateThread on Windows. |
| Join(); |
| state_->handle_.reset(NULL); |
| } |
| } |
| |
| #endif // OS_WIN |
| |
| Thread::Thread() |
| : state_(new ThreadInternalState) {} |
| |
| Thread::~Thread() { |
| Detach(); |
| } |
| |
| void Thread::SetJoinable(bool joinable) { |
| state_->joinable_ = joinable; |
| } |
| |
| namespace { |
| |
| #ifdef OS_WIN |
| unsigned __stdcall DetachedThreadFuncWin(void *ptr) { |
| scoped_ptr<DetachedThread> p(static_cast<DetachedThread *>(ptr)); |
| p->Run(); |
| return 0; |
| } |
| #else |
| void *DetachedThreadFuncPosix(void *ptr) { |
| scoped_ptr<DetachedThread> p(static_cast<DetachedThread *>(ptr)); |
| p->Run(); |
| return 0; |
| } |
| #endif // OS_WIN |
| |
| } // namespace |
| |
| DetachedThread::DetachedThread() {} |
| |
| DetachedThread::~DetachedThread() {} |
| |
| bool DetachedThread::Start() { |
| #ifdef OS_WIN |
| HANDLE handle = reinterpret_cast<HANDLE>(_beginthreadex( |
| NULL, 0, DetachedThreadFuncWin, this, 0, NULL)); |
| if (0 == handle) { |
| delete this; |
| return false; |
| } |
| ::CloseHandle(handle); |
| return true; |
| #else |
| pthread_attr_t tattr; |
| pthread_attr_init(&tattr); |
| pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); |
| pthread_t thread_id; |
| if (0 != pthread_create(&thread_id, &tattr, DetachedThreadFuncPosix, this)) { |
| delete this; |
| return false; |
| } |
| return true; |
| #endif // OS_WIN |
| } |
| |
| } // namespace mozc |