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