]> git.lizzy.rs Git - minetest.git/blob - src/threading/thread.cpp
NetBSD build fix proposal. (#10308)
[minetest.git] / src / threading / thread.cpp
1 /*
2 This file is a part of the JThread package, which contains some object-
3 oriented thread wrappers for different thread implementations.
4
5 Copyright (c) 2000-2006  Jori Liesenborgs (jori.liesenborgs@gmail.com)
6
7 Permission is hereby granted, free of charge, to any person obtaining a
8 copy of this software and associated documentation files (the "Software"),
9 to deal in the Software without restriction, including without limitation
10 the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 and/or sell copies of the Software, and to permit persons to whom the
12 Software is furnished to do so, subject to the following conditions:
13
14 The above copyright notice and this permission notice shall be included in
15 all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 DEALINGS IN THE SOFTWARE.
24 */
25
26 #include "threading/thread.h"
27 #include "threading/mutex_auto_lock.h"
28 #include "log.h"
29 #include "porting.h"
30
31 // for setName
32 #if defined(__linux__)
33         #include <sys/prctl.h>
34 #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
35         #include <pthread_np.h>
36 #elif defined(_MSC_VER)
37         struct THREADNAME_INFO {
38                 DWORD dwType;     // Must be 0x1000
39                 LPCSTR szName;    // Pointer to name (in user addr space)
40                 DWORD dwThreadID; // Thread ID (-1=caller thread)
41                 DWORD dwFlags;    // Reserved for future use, must be zero
42         };
43 #endif
44
45 // for bindToProcessor
46 #if __FreeBSD_version >= 702106
47         typedef cpuset_t cpu_set_t;
48 #elif defined(__sun) || defined(sun)
49         #include <sys/types.h>
50         #include <sys/processor.h>
51         #include <sys/procset.h>
52 #elif defined(_AIX)
53         #include <sys/processor.h>
54         #include <sys/thread.h>
55 #elif defined(__APPLE__)
56         #include <mach/mach_init.h>
57         #include <mach/thread_act.h>
58 #endif
59
60
61 Thread::Thread(const std::string &name) :
62         m_name(name),
63         m_request_stop(false),
64         m_running(false)
65 {
66 #ifdef _AIX
67         m_kernel_thread_id = -1;
68 #endif
69 }
70
71
72 Thread::~Thread()
73 {
74         kill();
75
76         // Make sure start finished mutex is unlocked before it's destroyed
77         if (m_start_finished_mutex.try_lock())
78                 m_start_finished_mutex.unlock();
79
80 }
81
82
83 bool Thread::start()
84 {
85         MutexAutoLock lock(m_mutex);
86
87         if (m_running)
88                 return false;
89
90         m_request_stop = false;
91
92         // The mutex may already be locked if the thread is being restarted
93         m_start_finished_mutex.try_lock();
94
95         try {
96                 m_thread_obj = new std::thread(threadProc, this);
97         } catch (const std::system_error &e) {
98                 return false;
99         }
100
101         // Allow spawned thread to continue
102         m_start_finished_mutex.unlock();
103
104         while (!m_running)
105                 sleep_ms(1);
106
107         m_joinable = true;
108
109         return true;
110 }
111
112
113 bool Thread::stop()
114 {
115         m_request_stop = true;
116         return true;
117 }
118
119
120 bool Thread::wait()
121 {
122         MutexAutoLock lock(m_mutex);
123
124         if (!m_joinable)
125                 return false;
126
127
128         m_thread_obj->join();
129
130         delete m_thread_obj;
131         m_thread_obj = nullptr;
132
133         assert(m_running == false);
134         m_joinable = false;
135         return true;
136 }
137
138
139 bool Thread::kill()
140 {
141         if (!m_running) {
142                 wait();
143                 return false;
144         }
145
146         m_running = false;
147
148 #if defined(_WIN32)
149         // See https://msdn.microsoft.com/en-us/library/hh920601.aspx#thread__native_handle_method
150         TerminateThread((HANDLE) m_thread_obj->native_handle(), 0);
151         CloseHandle((HANDLE) m_thread_obj->native_handle());
152 #else
153         // We need to pthread_kill instead on Android since NDKv5's pthread
154         // implementation is incomplete.
155 # ifdef __ANDROID__
156         pthread_kill(getThreadHandle(), SIGKILL);
157 # else
158         pthread_cancel(getThreadHandle());
159 # endif
160         wait();
161 #endif
162
163         m_retval       = nullptr;
164         m_joinable     = false;
165         m_request_stop = false;
166
167         return true;
168 }
169
170
171 bool Thread::getReturnValue(void **ret)
172 {
173         if (m_running)
174                 return false;
175
176         *ret = m_retval;
177         return true;
178 }
179
180
181 void Thread::threadProc(Thread *thr)
182 {
183 #ifdef _AIX
184         thr->m_kernel_thread_id = thread_self();
185 #endif
186
187         thr->setName(thr->m_name);
188
189         g_logger.registerThread(thr->m_name);
190         thr->m_running = true;
191
192         // Wait for the thread that started this one to finish initializing the
193         // thread handle so that getThreadId/getThreadHandle will work.
194         thr->m_start_finished_mutex.lock();
195
196         thr->m_retval = thr->run();
197
198         thr->m_running = false;
199         // Unlock m_start_finished_mutex to prevent data race condition on Windows.
200         // On Windows with VS2017 build TerminateThread is called and this mutex is not
201         // released. We try to unlock it from caller thread and it's refused by system.
202         thr->m_start_finished_mutex.unlock();
203         g_logger.deregisterThread();
204 }
205
206
207 void Thread::setName(const std::string &name)
208 {
209 #if defined(__linux__)
210
211         // It would be cleaner to do this with pthread_setname_np,
212         // which was added to glibc in version 2.12, but some major
213         // distributions are still runing 2.11 and previous versions.
214         prctl(PR_SET_NAME, name.c_str());
215
216 #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
217
218         pthread_set_name_np(pthread_self(), name.c_str());
219
220 #elif defined(__NetBSD__)
221
222         pthread_setname_np(pthread_self(), "%s", const_cast<char*>(name.c_str()));
223
224 #elif defined(__APPLE__)
225
226         pthread_setname_np(name.c_str());
227
228 #elif defined(__HAIKU__)
229
230         rename_thread(find_thread(NULL), name.c_str());
231
232 #elif defined(_MSC_VER)
233
234         // Windows itself doesn't support thread names,
235         // but the MSVC debugger does...
236         THREADNAME_INFO info;
237
238         info.dwType = 0x1000;
239         info.szName = name.c_str();
240         info.dwThreadID = -1;
241         info.dwFlags = 0;
242
243         __try {
244                 RaiseException(0x406D1388, 0,
245                         sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
246         } __except (EXCEPTION_CONTINUE_EXECUTION) {
247         }
248
249 #elif defined(_WIN32) || defined(__GNU__)
250
251         // These platforms are known to not support thread names.
252         // Silently ignore the request.
253
254 #else
255         #warning "Unrecognized platform, thread names will not be available."
256 #endif
257 }
258
259
260 unsigned int Thread::getNumberOfProcessors()
261 {
262         return std::thread::hardware_concurrency();
263 }
264
265
266 bool Thread::bindToProcessor(unsigned int proc_number)
267 {
268 #if defined(__ANDROID__)
269
270         return false;
271
272 #elif _MSC_VER
273
274         return SetThreadAffinityMask(getThreadHandle(), 1 << proc_number);
275
276 #elif __MINGW32__
277
278         return SetThreadAffinityMask(pthread_gethandle(getThreadHandle()), 1 << proc_number);
279
280 #elif __FreeBSD_version >= 702106 || defined(__linux__) || defined(__DragonFly__)
281
282         cpu_set_t cpuset;
283
284         CPU_ZERO(&cpuset);
285         CPU_SET(proc_number, &cpuset);
286
287         return pthread_setaffinity_np(getThreadHandle(), sizeof(cpuset), &cpuset) == 0;
288
289 #elif defined(__sun) || defined(sun)
290
291         return processor_bind(P_LWPID, P_MYID, proc_number, NULL) == 0
292
293 #elif defined(_AIX)
294
295         return bindprocessor(BINDTHREAD, m_kernel_thread_id, proc_number) == 0;
296
297 #elif defined(__hpux) || defined(hpux)
298
299         pthread_spu_t answer;
300
301         return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
302                         &answer, proc_number, getThreadHandle()) == 0;
303
304 #elif defined(__APPLE__)
305
306         struct thread_affinity_policy tapol;
307
308         thread_port_t threadport = pthread_mach_thread_np(getThreadHandle());
309         tapol.affinity_tag = proc_number + 1;
310         return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
311                         (thread_policy_t)&tapol,
312                         THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;
313
314 #else
315
316         return false;
317
318 #endif
319 }
320
321
322 bool Thread::setPriority(int prio)
323 {
324 #ifdef _MSC_VER
325
326         return SetThreadPriority(getThreadHandle(), prio);
327
328 #elif __MINGW32__
329
330         return SetThreadPriority(pthread_gethandle(getThreadHandle()), prio);
331
332 #else
333
334         struct sched_param sparam;
335         int policy;
336
337         if (pthread_getschedparam(getThreadHandle(), &policy, &sparam) != 0)
338                 return false;
339
340         int min = sched_get_priority_min(policy);
341         int max = sched_get_priority_max(policy);
342
343         sparam.sched_priority = min + prio * (max - min) / THREAD_PRIORITY_HIGHEST;
344         return pthread_setschedparam(getThreadHandle(), policy, &sparam) == 0;
345
346 #endif
347 }
348