aboutsummaryrefslogtreecommitdiff
path: root/src/threading.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/threading.cpp351
1 files changed, 174 insertions, 177 deletions
diff --git a/src/threading.cpp b/src/threading.cpp
index acc79d8..254b2e3 100644
--- a/src/threading.cpp
+++ b/src/threading.cpp
@@ -6,7 +6,7 @@
6 * 6 *
7 * References: 7 * References:
8 * <http://www.cse.wustl.edu/~schmidt/win32-cv-1.html> 8 * <http://www.cse.wustl.edu/~schmidt/win32-cv-1.html>
9*/ 9 */
10 10
11/* 11/*
12=============================================================================== 12===============================================================================
@@ -36,35 +36,34 @@ THE SOFTWARE.
36*/ 36*/
37#if defined(__linux__) 37#if defined(__linux__)
38 38
39# ifndef _GNU_SOURCE // definition by the makefile can cause a redefinition error 39#ifndef _GNU_SOURCE // definition by the makefile can cause a redefinition error
40# define _GNU_SOURCE // must be defined before any include 40#define _GNU_SOURCE // must be defined before any include
41# endif // _GNU_SOURCE 41#endif // _GNU_SOURCE
42 42
43# ifdef __ANDROID__ 43#ifdef __ANDROID__
44# include <android/log.h> 44#include <android/log.h>
45# define LOG_TAG "LuaLanes" 45#define LOG_TAG "LuaLanes"
46# endif // __ANDROID__ 46#endif // __ANDROID__
47 47
48#endif // __linux__ 48#endif // __linux__
49 49
50#include "threading.h" 50#include "threading.h"
51 51
52#if !defined( PLATFORM_XBOX) && !defined( PLATFORM_WIN32) && !defined( PLATFORM_POCKETPC) 52#if !defined(PLATFORM_XBOX) && !defined(PLATFORM_WIN32) && !defined(PLATFORM_POCKETPC)
53# include <sys/time.h> 53#include <sys/time.h>
54#endif // non-WIN32 timing 54#endif // non-WIN32 timing
55 55
56
57#if defined(PLATFORM_LINUX) || defined(PLATFORM_CYGWIN) 56#if defined(PLATFORM_LINUX) || defined(PLATFORM_CYGWIN)
58# include <sys/types.h> 57#include <sys/types.h>
59# include <unistd.h> 58#include <unistd.h>
60#endif 59#endif
61 60
62#ifdef PLATFORM_OSX 61#ifdef PLATFORM_OSX
63# include "threading_osx.h" 62#include "threading_osx.h"
64#endif 63#endif
65 64
66/* Linux with older glibc (such as Debian) don't have pthread_setname_np, but have prctl 65/* Linux with older glibc (such as Debian) don't have pthread_setname_np, but have prctl
67*/ 66 */
68#if defined PLATFORM_LINUX 67#if defined PLATFORM_LINUX
69#if defined __GNU_LIBRARY__ && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 12 68#if defined __GNU_LIBRARY__ && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 12
70#define LINUX_USE_PTHREAD_SETNAME_NP 1 69#define LINUX_USE_PTHREAD_SETNAME_NP 1
@@ -76,41 +75,39 @@ THE SOFTWARE.
76 75
77#ifdef _MSC_VER 76#ifdef _MSC_VER
78// ".. selected for automatic inline expansion" (/O2 option) 77// ".. selected for automatic inline expansion" (/O2 option)
79# pragma warning( disable : 4711 ) 78#pragma warning(disable : 4711)
80// ".. type cast from function pointer ... to data pointer" 79// ".. type cast from function pointer ... to data pointer"
81# pragma warning( disable : 4054 ) 80#pragma warning(disable : 4054)
82#endif 81#endif
83 82
84/* 83/*
85* FAIL is for unexpected API return values - essentially programming 84 * FAIL is for unexpected API return values - essentially programming
86* error in _this_ code. 85 * error in _this_ code.
87*/ 86 */
88#if defined(PLATFORM_XBOX) || defined(PLATFORM_WIN32) || defined(PLATFORM_POCKETPC) 87#if defined(PLATFORM_XBOX) || defined(PLATFORM_WIN32) || defined(PLATFORM_POCKETPC)
89 static void FAIL(char const* funcname, int rc) 88static void FAIL(char const* funcname, int rc)
90 { 89{
91#if defined(PLATFORM_XBOX) 90#if defined(PLATFORM_XBOX)
92 fprintf(stderr, "%s() failed! (%d)\n", funcname, rc); 91 fprintf(stderr, "%s() failed! (%d)\n", funcname, rc);
93#else // PLATFORM_XBOX 92#else // PLATFORM_XBOX
94 char buf[256]; 93 char buf[256];
95 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, nullptr); 94 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, nullptr);
96 fprintf(stderr, "%s() failed! [GetLastError() -> %d] '%s'", funcname, rc, buf); 95 fprintf(stderr, "%s() failed! [GetLastError() -> %d] '%s'", funcname, rc, buf);
97#endif // PLATFORM_XBOX 96#endif // PLATFORM_XBOX
98#ifdef _MSC_VER 97#ifdef _MSC_VER
99 __debugbreak(); // give a chance to the debugger! 98 __debugbreak(); // give a chance to the debugger!
100#endif // _MSC_VER 99#endif // _MSC_VER
101 abort(); 100 abort();
102 } 101}
103#endif // win32 build 102#endif // win32 build
104 103
105
106/*---=== Threading ===---*/ 104/*---=== Threading ===---*/
107 105
108// ################################################################################################# 106// #################################################################################################
109// ################################################################################################# 107// #################################################################################################
110#if THREADAPI == THREADAPI_WINDOWS 108#if THREADAPI == THREADAPI_WINDOWS
111 109
112static int const gs_prio_remap[] = 110static int const gs_prio_remap[] = {
113{
114 THREAD_PRIORITY_IDLE, 111 THREAD_PRIORITY_IDLE,
115 THREAD_PRIORITY_LOWEST, 112 THREAD_PRIORITY_LOWEST,
116 THREAD_PRIORITY_BELOW_NORMAL, 113 THREAD_PRIORITY_BELOW_NORMAL,
@@ -125,8 +122,7 @@ static int const gs_prio_remap[] =
125void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) 122void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_)
126{ 123{
127 // prio range [-3,+3] was checked by the caller 124 // prio range [-3,+3] was checked by the caller
128 if (!SetThreadPriority(GetCurrentThread(), gs_prio_remap[prio_ + 3])) 125 if (!SetThreadPriority(GetCurrentThread(), gs_prio_remap[prio_ + 3])) {
129 {
130 FAIL("THREAD_SET_PRIORITY", GetLastError()); 126 FAIL("THREAD_SET_PRIORITY", GetLastError());
131 } 127 }
132} 128}
@@ -136,8 +132,7 @@ void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_)
136void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_, [[maybe_unused]] bool sudo_) 132void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_, [[maybe_unused]] bool sudo_)
137{ 133{
138 // prio range [-3,+3] was checked by the caller 134 // prio range [-3,+3] was checked by the caller
139 if (!SetThreadPriority(thread_.native_handle(), gs_prio_remap[prio_ + 3])) 135 if (!SetThreadPriority(thread_.native_handle(), gs_prio_remap[prio_ + 3])) {
140 {
141 FAIL("JTHREAD_SET_PRIORITY", GetLastError()); 136 FAIL("JTHREAD_SET_PRIORITY", GetLastError());
142 } 137 }
143} 138}
@@ -146,8 +141,7 @@ void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_, [[maybe_unused]] boo
146 141
147void THREAD_SET_AFFINITY(unsigned int aff) 142void THREAD_SET_AFFINITY(unsigned int aff)
148{ 143{
149 if (!SetThreadAffinityMask(GetCurrentThread(), aff)) 144 if (!SetThreadAffinityMask(GetCurrentThread(), aff)) {
150 {
151 FAIL("THREAD_SET_AFFINITY", GetLastError()); 145 FAIL("THREAD_SET_AFFINITY", GetLastError());
152 } 146 }
153} 147}
@@ -155,15 +149,15 @@ void THREAD_SET_AFFINITY(unsigned int aff)
155// ################################################################################################# 149// #################################################################################################
156 150
157#if !defined __GNUC__ 151#if !defined __GNUC__
158//see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx 152// see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
159#define MS_VC_EXCEPTION 0x406D1388 153#define MS_VC_EXCEPTION 0x406D1388
160#pragma pack(push,8) 154#pragma pack(push, 8)
161typedef struct tagTHREADNAME_INFO 155typedef struct tagTHREADNAME_INFO
162{ 156{
163 DWORD dwType; // Must be 0x1000. 157 DWORD dwType; // Must be 0x1000.
164 LPCSTR szName; // Pointer to name (in user addr space). 158 LPCSTR szName; // Pointer to name (in user addr space).
165 DWORD dwThreadID; // Thread ID (-1=caller thread). 159 DWORD dwThreadID; // Thread ID (-1=caller thread).
166 DWORD dwFlags; // Reserved for future use, must be zero. 160 DWORD dwFlags; // Reserved for future use, must be zero.
167} THREADNAME_INFO; 161} THREADNAME_INFO;
168#pragma pack(pop) 162#pragma pack(pop)
169#endif // !__GNUC__ 163#endif // !__GNUC__
@@ -177,12 +171,9 @@ void THREAD_SETNAME(char const* _name)
177 info.dwThreadID = GetCurrentThreadId(); 171 info.dwThreadID = GetCurrentThreadId();
178 info.dwFlags = 0; 172 info.dwFlags = 0;
179 173
180 __try 174 __try {
181 { 175 RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*) &info);
182 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); 176 } __except (EXCEPTION_EXECUTE_HANDLER) {
183 }
184 __except(EXCEPTION_EXECUTE_HANDLER)
185 {
186 } 177 }
187#endif // !__GNUC__ 178#endif // !__GNUC__
188} 179}
@@ -211,8 +202,7 @@ void THREAD_SETNAME(char const* _name)
211#undef pthread_attr_setschedpolicy 202#undef pthread_attr_setschedpolicy
212[[nodiscard]] static int pthread_attr_setschedpolicy(pthread_attr_t* attr, int policy) 203[[nodiscard]] static int pthread_attr_setschedpolicy(pthread_attr_t* attr, int policy)
213{ 204{
214 if (policy != SCHED_OTHER) 205 if (policy != SCHED_OTHER) {
215 {
216 return ENOTSUP; 206 return ENOTSUP;
217 } 207 }
218 return 0; 208 return 0;
@@ -220,131 +210,140 @@ void THREAD_SETNAME(char const* _name)
220#endif // pthread_attr_setschedpolicy() 210#endif // pthread_attr_setschedpolicy()
221#endif // defined(__MINGW32__) || defined(__MINGW64__) 211#endif // defined(__MINGW32__) || defined(__MINGW64__)
222 212
223static void _PT_FAIL( int rc, const char *name, const char *file, int line ) 213static void _PT_FAIL(int rc, const char* name, const char* file, int line)
224{ 214{
225 const char *why= (rc==EINVAL) ? "EINVAL" : 215 const char* why = (rc == EINVAL) ? "EINVAL"
226 (rc==EBUSY) ? "EBUSY" : 216 : (rc == EBUSY) ? "EBUSY"
227 (rc==EPERM) ? "EPERM" : 217 : (rc == EPERM) ? "EPERM"
228 (rc==ENOMEM) ? "ENOMEM" : 218 : (rc == ENOMEM) ? "ENOMEM"
229 (rc==ESRCH) ? "ESRCH" : 219 : (rc == ESRCH) ? "ESRCH"
230 (rc==ENOTSUP) ? "ENOTSUP": 220 : (rc == ENOTSUP) ? "ENOTSUP"
231 //... 221 : "<UNKNOWN>";
232 "<UNKNOWN>"; 222 fprintf(stderr, "%s %d: %s failed, %d %s\n", file, line, name, rc, why);
233 fprintf( stderr, "%s %d: %s failed, %d %s\n", file, line, name, rc, why );
234 abort(); 223 abort();
235} 224}
236#define PT_CALL( call ) { int rc= call; if (rc!=0) _PT_FAIL( rc, #call, __FILE__, __LINE__ ); } 225#define PT_CALL(call) \
226 { \
227 int rc = call; \
228 if (rc != 0) \
229 _PT_FAIL(rc, #call, __FILE__, __LINE__); \
230 }
237 231
238// array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range 232// array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range
239static int const gs_prio_remap[] = 233static int const gs_prio_remap[] = {
240{ 234// NB: PThreads priority handling is about as twisty as one can get it
241 // NB: PThreads priority handling is about as twisty as one can get it 235// (and then some). DON*T TRUST ANYTHING YOU READ ON THE NET!!!
242 // (and then some). DON*T TRUST ANYTHING YOU READ ON THE NET!!! 236
243 237//---
244 //--- 238// "Select the scheduling policy for the thread: one of SCHED_OTHER
245 // "Select the scheduling policy for the thread: one of SCHED_OTHER 239// (regular, non-real-time scheduling), SCHED_RR (real-time,
246 // (regular, non-real-time scheduling), SCHED_RR (real-time, 240// round-robin) or SCHED_FIFO (real-time, first-in first-out)."
247 // round-robin) or SCHED_FIFO (real-time, first-in first-out)." 241//
248 // 242// "Using the RR policy ensures that all threads having the same
249 // "Using the RR policy ensures that all threads having the same 243// priority level will be scheduled equally, regardless of their activity."
250 // priority level will be scheduled equally, regardless of their activity." 244//
251 // 245// "For SCHED_FIFO and SCHED_RR, the only required member of the
252 // "For SCHED_FIFO and SCHED_RR, the only required member of the 246// sched_param structure is the priority sched_priority. For SCHED_OTHER,
253 // sched_param structure is the priority sched_priority. For SCHED_OTHER, 247// the affected scheduling parameters are implementation-defined."
254 // the affected scheduling parameters are implementation-defined." 248//
255 // 249// "The priority of a thread is specified as a delta which is added to
256 // "The priority of a thread is specified as a delta which is added to 250// the priority of the process."
257 // the priority of the process." 251//
258 // 252// ".. priority is an integer value, in the range from 1 to 127.
259 // ".. priority is an integer value, in the range from 1 to 127. 253// 1 is the least-favored priority, 127 is the most-favored."
260 // 1 is the least-favored priority, 127 is the most-favored." 254//
261 // 255// "Priority level 0 cannot be used: it is reserved for the system."
262 // "Priority level 0 cannot be used: it is reserved for the system." 256//
263 // 257// "When you use specify a priority of -99 in a call to
264 // "When you use specify a priority of -99 in a call to 258// pthread_setschedparam(), the priority of the target thread is
265 // pthread_setschedparam(), the priority of the target thread is 259// lowered to the lowest possible value."
266 // lowered to the lowest possible value." 260//
267 // 261// ...
268 // ...
269
270 // ** CONCLUSION **
271 //
272 // PThread priorities are _hugely_ system specific, and we need at
273 // least OS specific settings. Hopefully, Linuxes and OS X versions
274 // are uniform enough, among each other...
275 //
276# if defined PLATFORM_OSX
277 // AK 10-Apr-07 (OS X PowerPC 10.4.9):
278 //
279 // With SCHED_RR, 26 seems to be the "normal" priority, where setting
280 // it does not seem to affect the order of threads processed.
281 //
282 // With SCHED_OTHER, the range 25..32 is normal (maybe the same 26,
283 // but the difference is not so clear with OTHER).
284 //
285 // 'sched_get_priority_min()' and '..max()' give 15, 47 as the
286 // priority limits. This could imply, user mode applications won't
287 // be able to use values outside of that range.
288 //
289# define _PRIO_MODE SCHED_OTHER
290
291 // OS X 10.4.9 (PowerPC) gives ENOTSUP for process scope
292 //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
293
294# define _PRIO_HI 32 // seems to work (_carefully_ picked!)
295# define _PRIO_0 26 // detected
296# define _PRIO_LO 1 // seems to work (tested)
297
298# elif defined PLATFORM_LINUX
299 // (based on Ubuntu Linux 2.6.15 kernel)
300 //
301 // SCHED_OTHER is the default policy, but does not allow for priorities.
302 // SCHED_RR allows priorities, all of which (1..99) are higher than
303 // a thread with SCHED_OTHER policy.
304 //
305 // <http://kerneltrap.org/node/6080>
306 // <http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library>
307 // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html>
308 //
309 // Manuals suggest checking #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING,
310 // but even Ubuntu does not seem to define it.
311 //
312# define _PRIO_MODE SCHED_RR
313
314 // NTLP 2.5: only system scope allowed (being the basic reason why
315 // root privileges are required..)
316 //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
317
318# define _PRIO_HI 99
319# define _PRIO_0 50
320# define _PRIO_LO 1
321
322# elif defined(PLATFORM_BSD)
323 //
324 // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html>
325 //
326 // "When control over the thread scheduling is desired, then FreeBSD
327 // with the libpthread implementation is by far the best choice .."
328 //
329# define _PRIO_MODE SCHED_OTHER
330# define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
331# define _PRIO_HI 31
332# define _PRIO_0 15
333# define _PRIO_LO 1
334
335# elif defined(PLATFORM_CYGWIN)
336 //
337 // TBD: Find right values for Cygwin
338 //
339# else
340# error "Unknown OS: not implemented!"
341# endif
342 262
343#if defined _PRIO_0 263// ** CONCLUSION **
344# define _PRIO_AN (_PRIO_0 + ((_PRIO_HI-_PRIO_0)/2)) 264//
345# define _PRIO_BN (_PRIO_LO + ((_PRIO_0-_PRIO_LO)/2)) 265// PThread priorities are _hugely_ system specific, and we need at
266// least OS specific settings. Hopefully, Linuxes and OS X versions
267// are uniform enough, among each other...
268//
269#if defined PLATFORM_OSX
270// AK 10-Apr-07 (OS X PowerPC 10.4.9):
271//
272// With SCHED_RR, 26 seems to be the "normal" priority, where setting
273// it does not seem to affect the order of threads processed.
274//
275// With SCHED_OTHER, the range 25..32 is normal (maybe the same 26,
276// but the difference is not so clear with OTHER).
277//
278// 'sched_get_priority_min()' and '..max()' give 15, 47 as the
279// priority limits. This could imply, user mode applications won't
280// be able to use values outside of that range.
281//
282#define _PRIO_MODE SCHED_OTHER
283
284// OS X 10.4.9 (PowerPC) gives ENOTSUP for process scope
285// #define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
286
287#define _PRIO_HI 32 // seems to work (_carefully_ picked!)
288#define _PRIO_0 26 // detected
289#define _PRIO_LO 1 // seems to work (tested)
290
291#elif defined PLATFORM_LINUX
292// (based on Ubuntu Linux 2.6.15 kernel)
293//
294// SCHED_OTHER is the default policy, but does not allow for priorities.
295// SCHED_RR allows priorities, all of which (1..99) are higher than
296// a thread with SCHED_OTHER policy.
297//
298// <http://kerneltrap.org/node/6080>
299// <http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library>
300// <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html>
301//
302// Manuals suggest checking #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING,
303// but even Ubuntu does not seem to define it.
304//
305#define _PRIO_MODE SCHED_RR
346 306
347 _PRIO_LO, _PRIO_LO, _PRIO_BN, _PRIO_0, _PRIO_AN, _PRIO_HI, _PRIO_HI 307// NTLP 2.5: only system scope allowed (being the basic reason why
308// root privileges are required..)
309// #define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
310
311#define _PRIO_HI 99
312#define _PRIO_0 50
313#define _PRIO_LO 1
314
315#elif defined(PLATFORM_BSD)
316//
317// <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html>
318//
319// "When control over the thread scheduling is desired, then FreeBSD
320// with the libpthread implementation is by far the best choice .."
321//
322#define _PRIO_MODE SCHED_OTHER
323#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
324#define _PRIO_HI 31
325#define _PRIO_0 15
326#define _PRIO_LO 1
327
328#elif defined(PLATFORM_CYGWIN)
329//
330// TBD: Find right values for Cygwin
331//
332#else
333#error "Unknown OS: not implemented!"
334#endif
335
336#if defined _PRIO_0
337#define _PRIO_AN (_PRIO_0 + ((_PRIO_HI - _PRIO_0) / 2))
338#define _PRIO_BN (_PRIO_LO + ((_PRIO_0 - _PRIO_LO) / 2))
339
340 _PRIO_LO,
341 _PRIO_LO,
342 _PRIO_BN,
343 _PRIO_0,
344 _PRIO_AN,
345 _PRIO_HI,
346 _PRIO_HI
348#endif // _PRIO_0 347#endif // _PRIO_0
349}; 348};
350 349
@@ -398,10 +397,8 @@ void THREAD_SET_AFFINITY(unsigned int aff)
398 cpu_set_t cpuset; 397 cpu_set_t cpuset;
399 CPU_ZERO(&cpuset); 398 CPU_ZERO(&cpuset);
400#endif 399#endif
401 while (aff != 0) 400 while (aff != 0) {
402 { 401 if (aff & 1) {
403 if (aff & 1)
404 {
405 CPU_SET(bit, &cpuset); 402 CPU_SET(bit, &cpuset);
406 } 403 }
407 ++bit; 404 ++bit;
@@ -430,7 +427,7 @@ void THREAD_SETNAME(char const* _name)
430#elif defined PLATFORM_LINUX 427#elif defined PLATFORM_LINUX
431#if LINUX_USE_PTHREAD_SETNAME_NP 428#if LINUX_USE_PTHREAD_SETNAME_NP
432 pthread_setname_np(pthread_self(), _name); 429 pthread_setname_np(pthread_self(), _name);
433#else // LINUX_USE_PTHREAD_SETNAME_NP 430#else // LINUX_USE_PTHREAD_SETNAME_NP
434 prctl(PR_SET_NAME, _name, 0, 0, 0); 431 prctl(PR_SET_NAME, _name, 0, 0, 0);
435#endif // LINUX_USE_PTHREAD_SETNAME_NP 432#endif // LINUX_USE_PTHREAD_SETNAME_NP
436#elif defined PLATFORM_QNX || defined PLATFORM_CYGWIN 433#elif defined PLATFORM_QNX || defined PLATFORM_CYGWIN