]> git.lizzy.rs Git - dragonfireclient.git/blob - src/threading/thread.cpp
Optimize string (mis)handling (#8128)
[dragonfireclient.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(), name.c_str());
223
224 #elif defined(__APPLE__)
225
226         pthread_setname_np(name.c_str());
227
228 #elif defined(_MSC_VER)
229
230         // Windows itself doesn't support thread names,
231         // but the MSVC debugger does...
232         THREADNAME_INFO info;
233
234         info.dwType = 0x1000;
235         info.szName = name.c_str();
236         info.dwThreadID = -1;
237         info.dwFlags = 0;
238
239         __try {
240                 RaiseException(0x406D1388, 0,
241                         sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
242         } __except (EXCEPTION_CONTINUE_EXECUTION) {
243         }
244
245 #elif defined(_WIN32) || defined(__GNU__)
246
247         // These platforms are known to not support thread names.
248         // Silently ignore the request.
249
250 #else
251         #warning "Unrecognized platform, thread names will not be available."
252 #endif
253 }
254
255
256 unsigned int Thread::getNumberOfProcessors()
257 {
258         return std::thread::hardware_concurrency();
259 }
260
261
262 bool Thread::bindToProcessor(unsigned int proc_number)
263 {
264 #if defined(__ANDROID__)
265
266         return false;
267
268 #elif _MSC_VER
269
270         return SetThreadAffinityMask(getThreadHandle(), 1 << proc_number);
271
272 #elif __MINGW32__
273
274         return SetThreadAffinityMask(pthread_gethandle(getThreadHandle()), 1 << proc_number);
275
276 #elif __FreeBSD_version >= 702106 || defined(__linux__) || defined(__DragonFly__)
277
278         cpu_set_t cpuset;
279
280         CPU_ZERO(&cpuset);
281         CPU_SET(proc_number, &cpuset);
282
283         return pthread_setaffinity_np(getThreadHandle(), sizeof(cpuset), &cpuset) == 0;
284
285 #elif defined(__sun) || defined(sun)
286
287         return processor_bind(P_LWPID, P_MYID, proc_number, NULL) == 0
288
289 #elif defined(_AIX)
290
291         return bindprocessor(BINDTHREAD, m_kernel_thread_id, proc_number) == 0;
292
293 #elif defined(__hpux) || defined(hpux)
294
295         pthread_spu_t answer;
296
297         return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
298                         &answer, proc_number, getThreadHandle()) == 0;
299
300 #elif defined(__APPLE__)
301
302         struct thread_affinity_policy tapol;
303
304         thread_port_t threadport = pthread_mach_thread_np(getThreadHandle());
305         tapol.affinity_tag = proc_number + 1;
306         return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
307                         (thread_policy_t)&tapol,
308                         THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;
309
310 #else
311
312         return false;
313
314 #endif
315 }
316
317
318 bool Thread::setPriority(int prio)
319 {
320 #ifdef _MSC_VER
321
322         return SetThreadPriority(getThreadHandle(), prio);
323
324 #elif __MINGW32__
325
326         return SetThreadPriority(pthread_gethandle(getThreadHandle()), prio);
327
328 #else
329
330         struct sched_param sparam;
331         int policy;
332
333         if (pthread_getschedparam(getThreadHandle(), &policy, &sparam) != 0)
334                 return false;
335
336         int min = sched_get_priority_min(policy);
337         int max = sched_get_priority_max(policy);
338
339         sparam.sched_priority = min + prio * (max - min) / THREAD_PRIORITY_HIGHEST;
340         return pthread_setschedparam(getThreadHandle(), policy, &sparam) == 0;
341
342 #endif
343 }
344