1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "base/thread.h"
31 
32 #ifdef OS_WIN
33 #include <windows.h>
34 #include <process.h>  // for _beginthreadex
35 #else
36 #include <pthread.h>
37 #ifdef OS_DRAGONFLY
38 #include <pthread_np.h>
39 #endif  // OS_DRAGONFLY
40 #endif  // OS_WIN
41 
42 #include <atomic>
43 #include <memory>
44 
45 #include "base/logging.h"
46 
47 namespace mozc {
48 
49 #ifdef OS_WIN
50 // Win32-based Thread implementation.
51 
52 namespace {
53 
WrapperForWindows(void * ptr)54 unsigned __stdcall WrapperForWindows(void *ptr) {
55   Thread *p = static_cast<Thread *>(ptr);
56   p->Run();
57   return 0;
58 }
59 
60 }  // namespace
61 
62 struct ThreadInternalState {
63  public:
ThreadInternalStatemozc::ThreadInternalState64   ThreadInternalState()
65     : handle(nullptr),
66       joinable(true) {}
67 
68   HANDLE handle;
69   bool joinable;
70 };
71 
Start(const string & thread_name)72 void Thread::Start(const string &thread_name) {
73   // TODO(mozc-dev): Set thread name.
74   if (IsRunning()) {
75     return;
76   }
77 
78   Detach();
79   state_->handle = reinterpret_cast<HANDLE>(_beginthreadex(
80       nullptr, 0, WrapperForWindows, this, 0, nullptr));
81 }
82 
IsRunning() const83 bool Thread::IsRunning() const {
84   DWORD result = 0;
85   if (state_->handle == nullptr ||
86       !::GetExitCodeThread(state_->handle, &result)) {
87     return false;
88   }
89   return (STILL_ACTIVE == result);
90 }
91 
Detach()92 void Thread::Detach() {
93   if (state_->handle != nullptr) {
94     ::CloseHandle(state_->handle);
95     state_->handle = nullptr;
96   }
97 }
98 
Join()99 void Thread::Join() {
100   if (!state_->joinable) {
101     return;
102   }
103   if (state_->handle == nullptr) {
104     return;
105   }
106   ::WaitForSingleObject(state_->handle, INFINITE);
107   ::CloseHandle(state_->handle);
108   state_->handle = nullptr;
109 }
110 
Terminate()111 void Thread::Terminate() {
112   if (state_->handle != nullptr) {
113     ::TerminateThread(state_->handle, 0);
114     state_->handle = nullptr;
115   }
116 }
117 
118 #else  // OS_WIN
119 // Thread implementation for pthread-based platforms. Currently all the
120 // platforms except for Windows use pthread.
121 
122 struct ThreadInternalState {
123  public:
124   ThreadInternalState() : is_running(false), joinable(true) {}
125 
126   // As pthread_t is an opaque object, we use (pthread_t *)nullptr to
127   // indicate that no thread is attached to this object.
128   // When |handle != nullptr|, |*handle| should indicate a
129   // valid thread id.
130   std::unique_ptr<pthread_t> handle;
131   std::atomic<bool> is_running;
132   bool joinable;
133 };
134 
135 void Thread::Start(const string &thread_name) {
136   if (IsRunning()) {
137     return;
138   }
139 
140   Detach();
141   state_->is_running = true;
142   state_->handle.reset(new pthread_t);
143   if (0 != pthread_create(state_->handle.get(), nullptr,
144                           &Thread::WrapperForPOSIX,
145                           static_cast<void *>(this))) {
146     state_->is_running = false;
147     state_->handle.reset();
148   } else {
149 #if defined(OS_NACL)
150     // NaCl doesn't support setname.
151 #elif defined(OS_MACOSX)
152     pthread_setname_np(thread_name.c_str());
153 #elif defined(OS_DRAGONFLY)
154     pthread_set_name_np(*state_->handle, thread_name.c_str());
155 #else  // !(OS_NACL | OS_MACOSX | OS_DRAGONFLY)
156     pthread_setname_np(*state_->handle_, thread_name.c_str());
157 #endif  // !(OS_NACL | OS_MACOSX | OS_DRAGONFLY)
158   }
159 }
160 
161 bool Thread::IsRunning() const {
162   return state_->is_running;
163 }
164 
165 void Thread::Detach() {
166   if (state_->handle != nullptr) {
167     pthread_detach(*state_->handle);
168     state_->handle.reset();
169   }
170 }
171 
172 void Thread::Join() {
173   if (!state_->joinable) {
174     return;
175   }
176   if (state_->handle == nullptr) {
177     return;
178   }
179   pthread_join(*state_->handle, nullptr);
180   state_->handle.reset();
181 }
182 
183 namespace {
184 
185 #ifdef OS_ANDROID
186 
187 void ExitThread(int sig) {
188   pthread_exit(0);
189 }
190 
191 // We don't have pthread_cancel for Android, so we'll use SIGUSR1 as
192 // work around.
193 void InitPThreadCancel() {
194   struct sigaction actions;
195   memset(&actions, 0, sizeof(actions));
196   sigemptyset(&actions.sa_mask);
197   actions.sa_flags = 0;
198   actions.sa_handler = ExitThread;
199   sigaction(SIGUSR1, &actions, nullptr);
200 }
201 
202 void PThreadCancel(pthread_t thread_id) {
203   const int pthread_kill_result = pthread_kill(thread_id, SIGUSR1);
204   if (pthread_kill_result != 0) {
205     // pthread_kill fails if
206     //  EINVAL: in case that the specified handle is invalid
207     //  ESRCH: in case that the thread is already terminated
208     LOG(ERROR) << "Failed to kill a thread. error = " << pthread_kill_result
209                 << "(" << strerror(pthread_kill_result) << ")";
210   }
211 }
212 
213 #elif defined(OS_NACL)
214 
215 void InitPThreadCancel() {
216   // Nothing is required.
217 }
218 
219 void PThreadCancel(pthread_t thread_id) {
220   LOG(ERROR) << "In NaCl we have no way to cancel a thread.";
221 }
222 
223 #else
224 
225 void InitPThreadCancel() {
226   // Nothing is required.
227 }
228 
229 void PThreadCancel(pthread_t thread_id) {
230   pthread_cancel(thread_id);
231 }
232 
233 #endif  // OS_ANDROID or OS_NACL or others
234 
235 void PThreadCleanupRoutine(void *ptr) {
236   auto *is_running = static_cast<std::atomic<bool> *>(ptr);
237   *is_running = false;
238 }
239 
240 }  // namespace
241 
242 void *Thread::WrapperForPOSIX(void *ptr) {
243   Thread *p = static_cast<Thread *>(ptr);
244   InitPThreadCancel();
245   {
246     // Caveat: the pthread_cleanup_push/pthread_cleanup_pop pair should be put
247     //     in the same function. Never move them into any other function.
248     pthread_cleanup_push(PThreadCleanupRoutine,
249                          static_cast<void *>(&p->state_->is_running));
250     p->Run();
251     pthread_cleanup_pop(1);
252   }
253   return nullptr;
254 }
255 
256 void Thread::Terminate() {
257   if (state_->handle != nullptr) {
258     PThreadCancel(*state_->handle);
259     // pthread_cancel (or pthread_kill in PThreadCancel on Android) is
260     // asynchronous. Join the thread to behave like TerminateThread on Windows.
261     Join();
262     state_->handle.reset();
263   }
264 }
265 
266 #endif  // OS_WIN
267 
Thread()268 Thread::Thread() : state_(new ThreadInternalState) {}
269 
~Thread()270 Thread::~Thread() {
271   Detach();
272 }
273 
SetJoinable(bool joinable)274 void Thread::SetJoinable(bool joinable) {
275   state_->joinable = joinable;
276 }
277 
278 }  // namespace mozc
279