]> git.lizzy.rs Git - minetest.git/blob - src/threading/thread.cpp
5cfc609957eb5ed999afddb932c634a4f3f39be4
[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(__NetBSD__)
37         #include <sched.h>
38 #elif defined(_MSC_VER)
39         struct THREADNAME_INFO {
40                 DWORD dwType;     // Must be 0x1000
41                 LPCSTR szName;    // Pointer to name (in user addr space)
42                 DWORD dwThreadID; // Thread ID (-1=caller thread)
43                 DWORD dwFlags;    // Reserved for future use, must be zero
44         };
45 #endif
46
47 // for bindToProcessor
48 #if __FreeBSD_version >= 702106
49         typedef cpuset_t cpu_set_t;
50 #elif defined(__sun) || defined(sun)
51         #include <sys/types.h>
52         #include <sys/processor.h>
53         #include <sys/procset.h>
54 #elif defined(_AIX)
55         #include <sys/processor.h>
56         #include <sys/thread.h>
57 #elif defined(__APPLE__)
58         #include <mach/mach_init.h>
59         #include <mach/thread_act.h>
60 #endif
61
62
63 Thread::Thread(const std::string &name) :
64         m_name(name),
65         m_request_stop(false),
66         m_running(false)
67 {
68 #ifdef _AIX
69         m_kernel_thread_id = -1;
70 #endif
71 }
72
73
74 Thread::~Thread()
75 {
76         // kill the thread if running
77         if (!m_running) {
78                 wait();
79         } else {
80
81                 m_running = false;
82
83 #if defined(_WIN32)
84                 // See https://msdn.microsoft.com/en-us/library/hh920601.aspx#thread__native_handle_method
85                 TerminateThread((HANDLE) m_thread_obj->native_handle(), 0);
86                 CloseHandle((HANDLE) m_thread_obj->native_handle());
87 #else
88                 // We need to pthread_kill instead on Android since NDKv5's pthread
89                 // implementation is incomplete.
90 # ifdef __ANDROID__
91                 pthread_kill(getThreadHandle(), SIGKILL);
92 # else
93                 pthread_cancel(getThreadHandle());
94 # endif
95                 wait();
96 #endif
97         }
98
99         // Make sure start finished mutex is unlocked before it's destroyed
100         if (m_start_finished_mutex.try_lock())
101                 m_start_finished_mutex.unlock();
102
103 }
104
105
106 bool Thread::start()
107 {
108         MutexAutoLock lock(m_mutex);
109
110         if (m_running)
111                 return false;
112
113         m_request_stop = false;
114
115         // The mutex may already be locked if the thread is being restarted
116         m_start_finished_mutex.try_lock();
117
118         try {
119                 m_thread_obj = new std::thread(threadProc, this);
120         } catch (const std::system_error &e) {
121                 return false;
122         }
123
124         // Allow spawned thread to continue
125         m_start_finished_mutex.unlock();
126
127         while (!m_running)
128                 sleep_ms(1);
129
130         m_joinable = true;
131
132         return true;
133 }
134
135
136 bool Thread::stop()
137 {
138         m_request_stop = true;
139         return true;
140 }
141
142
143 bool Thread::wait()
144 {
145         MutexAutoLock lock(m_mutex);
146
147         if (!m_joinable)
148                 return false;
149
150
151         m_thread_obj->join();
152
153         delete m_thread_obj;
154         m_thread_obj = nullptr;
155
156         assert(m_running == false);
157         m_joinable = false;
158         return true;
159 }
160
161
162
163 bool Thread::getReturnValue(void **ret)
164 {
165         if (m_running)
166                 return false;
167
168         *ret = m_retval;
169         return true;
170 }
171
172
173 void Thread::threadProc(Thread *thr)
174 {
175 #ifdef _AIX
176         thr->m_kernel_thread_id = thread_self();
177 #endif
178
179         thr->setName(thr->m_name);
180
181         g_logger.registerThread(thr->m_name);
182         thr->m_running = true;
183
184         // Wait for the thread that started this one to finish initializing the
185         // thread handle so that getThreadId/getThreadHandle will work.
186         thr->m_start_finished_mutex.lock();
187
188         thr->m_retval = thr->run();
189
190         thr->m_running = false;
191         // Unlock m_start_finished_mutex to prevent data race condition on Windows.
192         // On Windows with VS2017 build TerminateThread is called and this mutex is not
193         // released. We try to unlock it from caller thread and it's refused by system.
194         thr->m_start_finished_mutex.unlock();
195         g_logger.deregisterThread();
196 }
197
198
199 void Thread::setName(const std::string &name)
200 {
201 #if defined(__linux__)
202
203         // It would be cleaner to do this with pthread_setname_np,
204         // which was added to glibc in version 2.12, but some major
205         // distributions are still runing 2.11 and previous versions.
206         prctl(PR_SET_NAME, name.c_str());
207
208 #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
209
210         pthread_set_name_np(pthread_self(), name.c_str());
211
212 #elif defined(__NetBSD__)
213
214         pthread_setname_np(pthread_self(), "%s", const_cast<char*>(name.c_str()));
215
216 #elif defined(__APPLE__)
217
218         pthread_setname_np(name.c_str());
219
220 #elif defined(__HAIKU__)
221
222         rename_thread(find_thread(NULL), name.c_str());
223
224 #elif defined(_MSC_VER)
225
226         // Windows itself doesn't support thread names,
227         // but the MSVC debugger does...
228         THREADNAME_INFO info;
229
230         info.dwType = 0x1000;
231         info.szName = name.c_str();
232         info.dwThreadID = -1;
233         info.dwFlags = 0;
234
235         __try {
236                 RaiseException(0x406D1388, 0,
237                         sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
238         } __except (EXCEPTION_CONTINUE_EXECUTION) {
239         }
240
241 #elif defined(_WIN32) || defined(__GNU__)
242
243         // These platforms are known to not support thread names.
244         // Silently ignore the request.
245
246 #else
247         #warning "Unrecognized platform, thread names will not be available."
248 #endif
249 }
250
251
252 unsigned int Thread::getNumberOfProcessors()
253 {
254         return std::thread::hardware_concurrency();
255 }
256
257
258 bool Thread::bindToProcessor(unsigned int proc_number)
259 {
260 #if defined(__ANDROID__)
261
262         return false;
263
264 #elif _MSC_VER
265
266         return SetThreadAffinityMask(getThreadHandle(), 1 << proc_number);
267
268 #elif __MINGW32__
269
270         return SetThreadAffinityMask(pthread_gethandle(getThreadHandle()), 1 << proc_number);
271
272 #elif __FreeBSD_version >= 702106 || defined(__linux__) || defined(__DragonFly__)
273
274         cpu_set_t cpuset;
275
276         CPU_ZERO(&cpuset);
277         CPU_SET(proc_number, &cpuset);
278
279         return pthread_setaffinity_np(getThreadHandle(), sizeof(cpuset), &cpuset) == 0;
280 #elif defined(__NetBSD__)
281
282         cpuset_t *cpuset = cpuset_create();
283         if (cpuset == NULL)
284                 return false;
285         int r = pthread_setaffinity_np(getThreadHandle(), cpuset_size(cpuset), cpuset);
286         cpuset_destroy(cpuset);
287         return r == 0;
288 #elif defined(__sun) || defined(sun)
289
290         return processor_bind(P_LWPID, P_MYID, proc_number, NULL) == 0
291
292 #elif defined(_AIX)
293
294         return bindprocessor(BINDTHREAD, m_kernel_thread_id, proc_number) == 0;
295
296 #elif defined(__hpux) || defined(hpux)
297
298         pthread_spu_t answer;
299
300         return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
301                         &answer, proc_number, getThreadHandle()) == 0;
302
303 #elif defined(__APPLE__)
304
305         struct thread_affinity_policy tapol;
306
307         thread_port_t threadport = pthread_mach_thread_np(getThreadHandle());
308         tapol.affinity_tag = proc_number + 1;
309         return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
310                         (thread_policy_t)&tapol,
311                         THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;
312
313 #else
314
315         return false;
316
317 #endif
318 }
319
320
321 bool Thread::setPriority(int prio)
322 {
323 #ifdef _MSC_VER
324
325         return SetThreadPriority(getThreadHandle(), prio);
326
327 #elif __MINGW32__
328
329         return SetThreadPriority(pthread_gethandle(getThreadHandle()), prio);
330
331 #else
332
333         struct sched_param sparam;
334         int policy;
335
336         if (pthread_getschedparam(getThreadHandle(), &policy, &sparam) != 0)
337                 return false;
338
339         int min = sched_get_priority_min(policy);
340         int max = sched_get_priority_max(policy);
341
342         sparam.sched_priority = min + prio * (max - min) / THREAD_PRIORITY_HIGHEST;
343         return pthread_setschedparam(getThreadHandle(), policy, &sparam) == 0;
344
345 #endif
346 }
347