diff options
Diffstat (limited to 'src/threading.cpp')
-rw-r--r-- | src/threading.cpp | 300 |
1 files changed, 135 insertions, 165 deletions
diff --git a/src/threading.cpp b/src/threading.cpp index bc1852f..aab2fa7 100644 --- a/src/threading.cpp +++ b/src/threading.cpp | |||
@@ -97,64 +97,28 @@ THE SOFTWARE. | |||
97 | * FAIL is for unexpected API return values - essentially programming | 97 | * FAIL is for unexpected API return values - essentially programming |
98 | * error in _this_ code. | 98 | * error in _this_ code. |
99 | */ | 99 | */ |
100 | #if defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC) | 100 | #if defined(PLATFORM_XBOX) || defined(PLATFORM_WIN32) || defined(PLATFORM_POCKETPC) |
101 | static void FAIL( char const* funcname, int rc) | 101 | static void FAIL(char const* funcname, int rc) |
102 | { | 102 | { |
103 | #if defined( PLATFORM_XBOX) | 103 | #if defined(PLATFORM_XBOX) |
104 | fprintf( stderr, "%s() failed! (%d)\n", funcname, rc ); | 104 | fprintf(stderr, "%s() failed! (%d)\n", funcname, rc); |
105 | #else // PLATFORM_XBOX | 105 | #else // PLATFORM_XBOX |
106 | char buf[256]; | 106 | char buf[256]; |
107 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, nullptr); | 107 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, nullptr); |
108 | fprintf( stderr, "%s() failed! [GetLastError() -> %d] '%s'", funcname, rc, buf); | 108 | fprintf(stderr, "%s() failed! [GetLastError() -> %d] '%s'", funcname, rc, buf); |
109 | #endif // PLATFORM_XBOX | 109 | #endif // PLATFORM_XBOX |
110 | #ifdef _MSC_VER | 110 | #ifdef _MSC_VER |
111 | __debugbreak(); // give a chance to the debugger! | 111 | __debugbreak(); // give a chance to the debugger! |
112 | #endif // _MSC_VER | 112 | #endif // _MSC_VER |
113 | abort(); | 113 | abort(); |
114 | } | 114 | } |
115 | #endif // win32 build | 115 | #endif // win32 build |
116 | 116 | ||
117 | 117 | ||
118 | /*---=== Threading ===---*/ | 118 | /*---=== Threading ===---*/ |
119 | 119 | ||
120 | //--- | 120 | // ################################################################################################## |
121 | // It may be meaningful to explicitly limit the new threads' C stack size. | 121 | // ################################################################################################## |
122 | // We should know how much Lua needs in the C stack, all Lua side allocations | ||
123 | // are done in heap so they don't count. | ||
124 | // | ||
125 | // Consequence of _not_ limiting the stack is running out of virtual memory | ||
126 | // with 1000-5000 threads on 32-bit systems. | ||
127 | // | ||
128 | // Note: using external C modules may be affected by the stack size check. | ||
129 | // if having problems, set back to '0' (default stack size of the system). | ||
130 | // | ||
131 | // Win32: 64K (?) | ||
132 | // Win64: xxx | ||
133 | // | ||
134 | // Linux x86: 2MB Ubuntu 7.04 via 'pthread_getstacksize()' | ||
135 | // Linux x64: xxx | ||
136 | // Linux ARM: xxx | ||
137 | // | ||
138 | // OS X 10.4.9: 512K <http://developer.apple.com/qa/qa2005/qa1419.html> | ||
139 | // valid values N * 4KB | ||
140 | // | ||
141 | #ifndef _THREAD_STACK_SIZE | ||
142 | # if defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC) || defined( PLATFORM_CYGWIN) | ||
143 | # define _THREAD_STACK_SIZE 0 | ||
144 | // Win32: does it work with less? | ||
145 | # elif (defined PLATFORM_OSX) | ||
146 | # define _THREAD_STACK_SIZE (524288/2) // 262144 | ||
147 | // OS X: "make test" works on 65536 and even below | ||
148 | // "make perftest" works on >= 4*65536 == 262144 (not 3*65536) | ||
149 | # elif (defined PLATFORM_LINUX) && (defined __i386) | ||
150 | # define _THREAD_STACK_SIZE (2097152/16) // 131072 | ||
151 | // Linux x86 (Ubuntu 7.04): "make perftest" works on /16 (not on /32) | ||
152 | # elif (defined PLATFORM_BSD) && (defined __i386) | ||
153 | # define _THREAD_STACK_SIZE (1048576/8) // 131072 | ||
154 | // FreeBSD 6.2 SMP i386: ("gmake perftest" works on /8 (not on /16) | ||
155 | # endif | ||
156 | #endif | ||
157 | |||
158 | #if THREADAPI == THREADAPI_WINDOWS | 122 | #if THREADAPI == THREADAPI_WINDOWS |
159 | 123 | ||
160 | static int const gs_prio_remap[] = | 124 | static int const gs_prio_remap[] = |
@@ -170,12 +134,12 @@ static int const gs_prio_remap[] = | |||
170 | 134 | ||
171 | // ############################################################################################### | 135 | // ############################################################################################### |
172 | 136 | ||
173 | void THREAD_SET_PRIORITY( int prio) | 137 | void THREAD_SET_PRIORITY(int prio) |
174 | { | 138 | { |
175 | // prio range [-3,+3] was checked by the caller | 139 | // prio range [-3,+3] was checked by the caller |
176 | if (!SetThreadPriority( GetCurrentThread(), gs_prio_remap[prio + 3])) | 140 | if (!SetThreadPriority(GetCurrentThread(), gs_prio_remap[prio + 3])) |
177 | { | 141 | { |
178 | FAIL( "THREAD_SET_PRIORITY", GetLastError()); | 142 | FAIL("THREAD_SET_PRIORITY", GetLastError()); |
179 | } | 143 | } |
180 | } | 144 | } |
181 | 145 | ||
@@ -194,86 +158,94 @@ void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_) | |||
194 | 158 | ||
195 | void THREAD_SET_AFFINITY(unsigned int aff) | 159 | void THREAD_SET_AFFINITY(unsigned int aff) |
196 | { | 160 | { |
197 | if( !SetThreadAffinityMask( GetCurrentThread(), aff)) | 161 | if (!SetThreadAffinityMask(GetCurrentThread(), aff)) |
198 | { | 162 | { |
199 | FAIL( "THREAD_SET_AFFINITY", GetLastError()); | 163 | FAIL("THREAD_SET_AFFINITY", GetLastError()); |
200 | } | 164 | } |
201 | } | 165 | } |
202 | 166 | ||
167 | // ############################################################################################### | ||
168 | |||
203 | #if !defined __GNUC__ | 169 | #if !defined __GNUC__ |
204 | //see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx | 170 | //see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx |
205 | #define MS_VC_EXCEPTION 0x406D1388 | 171 | #define MS_VC_EXCEPTION 0x406D1388 |
206 | #pragma pack(push,8) | 172 | #pragma pack(push,8) |
207 | typedef struct tagTHREADNAME_INFO | 173 | typedef struct tagTHREADNAME_INFO |
208 | { | 174 | { |
209 | DWORD dwType; // Must be 0x1000. | 175 | DWORD dwType; // Must be 0x1000. |
210 | LPCSTR szName; // Pointer to name (in user addr space). | 176 | LPCSTR szName; // Pointer to name (in user addr space). |
211 | DWORD dwThreadID; // Thread ID (-1=caller thread). | 177 | DWORD dwThreadID; // Thread ID (-1=caller thread). |
212 | DWORD dwFlags; // Reserved for future use, must be zero. | 178 | DWORD dwFlags; // Reserved for future use, must be zero. |
213 | } THREADNAME_INFO; | 179 | } THREADNAME_INFO; |
214 | #pragma pack(pop) | 180 | #pragma pack(pop) |
215 | #endif // !__GNUC__ | 181 | #endif // !__GNUC__ |
216 | 182 | ||
217 | void THREAD_SETNAME( char const* _name) | 183 | void THREAD_SETNAME(char const* _name) |
218 | { | 184 | { |
219 | #if !defined __GNUC__ | 185 | #if !defined __GNUC__ |
220 | THREADNAME_INFO info; | 186 | THREADNAME_INFO info; |
221 | info.dwType = 0x1000; | 187 | info.dwType = 0x1000; |
222 | info.szName = _name; | 188 | info.szName = _name; |
223 | info.dwThreadID = GetCurrentThreadId(); | 189 | info.dwThreadID = GetCurrentThreadId(); |
224 | info.dwFlags = 0; | 190 | info.dwFlags = 0; |
225 | 191 | ||
226 | __try | 192 | __try |
227 | { | 193 | { |
228 | RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); | 194 | RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); |
229 | } | ||
230 | __except(EXCEPTION_EXECUTE_HANDLER) | ||
231 | { | ||
232 | } | ||
233 | #endif // !__GNUC__ | ||
234 | } | 195 | } |
196 | __except(EXCEPTION_EXECUTE_HANDLER) | ||
197 | { | ||
198 | } | ||
199 | #endif // !__GNUC__ | ||
200 | } | ||
235 | 201 | ||
202 | // ################################################################################################## | ||
203 | // ################################################################################################## | ||
236 | #else // THREADAPI == THREADAPI_PTHREAD | 204 | #else // THREADAPI == THREADAPI_PTHREAD |
237 | // PThread (Linux, OS X, ...) | 205 | // ################################################################################################## |
238 | // | 206 | // ################################################################################################## |
239 | // On OS X, user processes seem to be able to change priorities. | 207 | |
240 | // On Linux, SCHED_RR and su privileges are required.. !-( | 208 | // PThread (Linux, OS X, ...) |
241 | // | 209 | // |
242 | #include <errno.h> | 210 | // On OS X, user processes seem to be able to change priorities. |
243 | #include <sched.h> | 211 | // On Linux, SCHED_RR and su privileges are required.. !-( |
244 | 212 | // | |
245 | # if (defined(__MINGW32__) || defined(__MINGW64__)) && defined pthread_attr_setschedpolicy | 213 | #include <errno.h> |
246 | # if pthread_attr_setschedpolicy( A, S) == ENOTSUP | 214 | #include <sched.h> |
247 | // from the mingw-w64 team: | 215 | |
248 | // Well, we support pthread_setschedparam by which you can specify | 216 | #if (defined(__MINGW32__) || defined(__MINGW64__)) && defined pthread_attr_setschedpolicy |
249 | // threading-policy. Nevertheless, yes we lack this function. In | 217 | #if pthread_attr_setschedpolicy(A, S) == ENOTSUP |
250 | // general its implementation is pretty much trivial, as on Win32 target | 218 | // from the mingw-w64 team: |
251 | // just SCHED_OTHER can be supported. | 219 | // Well, we support pthread_setschedparam by which you can specify |
252 | #undef pthread_attr_setschedpolicy | 220 | // threading-policy. Nevertheless, yes we lack this function. In |
253 | static int pthread_attr_setschedpolicy( pthread_attr_t* attr, int policy) | 221 | // general its implementation is pretty much trivial, as on Win32 target |
222 | // just SCHED_OTHER can be supported. | ||
223 | #undef pthread_attr_setschedpolicy | ||
224 | static int pthread_attr_setschedpolicy(pthread_attr_t* attr, int policy) | ||
225 | { | ||
226 | if (policy != SCHED_OTHER) | ||
254 | { | 227 | { |
255 | if( policy != SCHED_OTHER) | 228 | return ENOTSUP; |
256 | { | ||
257 | return ENOTSUP; | ||
258 | } | ||
259 | return 0; | ||
260 | } | 229 | } |
261 | # endif // pthread_attr_setschedpolicy() | 230 | return 0; |
262 | # endif // defined(__MINGW32__) || defined(__MINGW64__) | 231 | } |
232 | #endif // pthread_attr_setschedpolicy() | ||
233 | #endif // defined(__MINGW32__) || defined(__MINGW64__) | ||
263 | 234 | ||
264 | static void _PT_FAIL( int rc, const char *name, const char *file, int line ) { | 235 | static void _PT_FAIL( int rc, const char *name, const char *file, int line ) |
236 | { | ||
265 | const char *why= (rc==EINVAL) ? "EINVAL" : | 237 | const char *why= (rc==EINVAL) ? "EINVAL" : |
266 | (rc==EBUSY) ? "EBUSY" : | 238 | (rc==EBUSY) ? "EBUSY" : |
267 | (rc==EPERM) ? "EPERM" : | 239 | (rc==EPERM) ? "EPERM" : |
268 | (rc==ENOMEM) ? "ENOMEM" : | 240 | (rc==ENOMEM) ? "ENOMEM" : |
269 | (rc==ESRCH) ? "ESRCH" : | 241 | (rc==ESRCH) ? "ESRCH" : |
270 | (rc==ENOTSUP) ? "ENOTSUP": | 242 | (rc==ENOTSUP) ? "ENOTSUP": |
271 | //... | 243 | //... |
272 | "<UNKNOWN>"; | 244 | "<UNKNOWN>"; |
273 | fprintf( stderr, "%s %d: %s failed, %d %s\n", file, line, name, rc, why ); | 245 | fprintf( stderr, "%s %d: %s failed, %d %s\n", file, line, name, rc, why ); |
274 | abort(); | 246 | abort(); |
275 | } | 247 | } |
276 | #define PT_CALL( call ) { int rc= call; if (rc!=0) _PT_FAIL( rc, #call, __FILE__, __LINE__ ); } | 248 | #define PT_CALL( call ) { int rc= call; if (rc!=0) _PT_FAIL( rc, #call, __FILE__, __LINE__ ); } |
277 | 249 | ||
278 | // array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range | 250 | // array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range |
279 | static int const gs_prio_remap[] = | 251 | static int const gs_prio_remap[] = |
@@ -376,14 +348,6 @@ static int const gs_prio_remap[] = | |||
376 | // | 348 | // |
377 | // TBD: Find right values for Cygwin | 349 | // TBD: Find right values for Cygwin |
378 | // | 350 | // |
379 | # elif defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC) | ||
380 | // any other value not supported by win32-pthread as of version 2.9.1 | ||
381 | # define _PRIO_MODE SCHED_OTHER | ||
382 | |||
383 | // PTHREAD_SCOPE_PROCESS not supported by win32-pthread as of version 2.9.1 | ||
384 | //#define _PRIO_SCOPE PTHREAD_SCOPE_SYSTEM // but do we need this at all to start with? | ||
385 | THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST, THREAD_PRIORITY_TIME_CRITICAL | ||
386 | |||
387 | # else | 351 | # else |
388 | # error "Unknown OS: not implemented!" | 352 | # error "Unknown OS: not implemented!" |
389 | # endif | 353 | # endif |
@@ -404,17 +368,17 @@ static int select_prio(int prio /* -3..+3 */) | |||
404 | return gs_prio_remap[prio + 3]; | 368 | return gs_prio_remap[prio + 3]; |
405 | } | 369 | } |
406 | 370 | ||
407 | void THREAD_SET_PRIORITY( int prio) | 371 | void THREAD_SET_PRIORITY(int prio) |
408 | { | 372 | { |
409 | #ifdef PLATFORM_LINUX | 373 | #ifdef PLATFORM_LINUX |
410 | if( sudo) // only root-privileged process can change priorities | 374 | if (!sudo) // only root-privileged process can change priorities |
375 | return; | ||
411 | #endif // PLATFORM_LINUX | 376 | #endif // PLATFORM_LINUX |
412 | { | 377 | |
413 | struct sched_param sp; | 378 | struct sched_param sp; |
414 | // prio range [-3,+3] was checked by the caller | 379 | // prio range [-3,+3] was checked by the caller |
415 | sp.sched_priority = gs_prio_remap[ prio + 3]; | 380 | sp.sched_priority = gs_prio_remap[prio + 3]; |
416 | PT_CALL( pthread_setschedparam( pthread_self(), _PRIO_MODE, &sp)); | 381 | PT_CALL(pthread_setschedparam(pthread_self(), _PRIO_MODE, &sp)); |
417 | } | ||
418 | } | 382 | } |
419 | 383 | ||
420 | // ################################################################################################# | 384 | // ################################################################################################# |
@@ -422,69 +386,75 @@ void THREAD_SET_PRIORITY( int prio) | |||
422 | void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_) | 386 | void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_) |
423 | { | 387 | { |
424 | #ifdef PLATFORM_LINUX | 388 | #ifdef PLATFORM_LINUX |
425 | if (sudo) // only root-privileged process can change priorities | 389 | if (!sudo) // only root-privileged process can change priorities |
390 | return; | ||
426 | #endif // PLATFORM_LINUX | 391 | #endif // PLATFORM_LINUX |
427 | { | 392 | |
428 | struct sched_param sp; | 393 | struct sched_param sp; |
429 | // prio range [-3,+3] was checked by the caller | 394 | // prio range [-3,+3] was checked by the caller |
430 | sp.sched_priority = gs_prio_remap[prio_ + 3]; | 395 | sp.sched_priority = gs_prio_remap[prio_ + 3]; |
431 | PT_CALL(pthread_setschedparam(static_cast<pthread_t>(thread_.native_handle()), _PRIO_MODE, &sp)); | 396 | PT_CALL(pthread_setschedparam(static_cast<pthread_t>(thread_.native_handle()), _PRIO_MODE, &sp)); |
432 | } | ||
433 | } | 397 | } |
434 | 398 | ||
435 | // ################################################################################################# | 399 | // ################################################################################################# |
436 | 400 | ||
437 | void THREAD_SET_AFFINITY( unsigned int aff) | 401 | void THREAD_SET_AFFINITY(unsigned int aff) |
438 | { | 402 | { |
439 | int bit = 0; | 403 | int bit = 0; |
440 | #ifdef __NetBSD__ | 404 | #ifdef __NetBSD__ |
441 | cpuset_t *cpuset = cpuset_create(); | 405 | cpuset_t* cpuset = cpuset_create(); |
442 | if (cpuset == nullptr) | 406 | if (cpuset == nullptr) |
443 | _PT_FAIL( errno, "cpuset_create", __FILE__, __LINE__-2 ); | 407 | _PT_FAIL(errno, "cpuset_create", __FILE__, __LINE__ - 2); |
444 | #define CPU_SET(b, s) cpuset_set(b, *(s)) | 408 | #define CPU_SET(b, s) cpuset_set(b, *(s)) |
445 | #else | 409 | #else |
446 | cpu_set_t cpuset; | 410 | cpu_set_t cpuset; |
447 | CPU_ZERO( &cpuset); | 411 | CPU_ZERO(&cpuset); |
448 | #endif | 412 | #endif |
449 | while( aff != 0) | 413 | while (aff != 0) |
450 | { | 414 | { |
451 | if( aff & 1) | 415 | if (aff & 1) |
452 | { | 416 | { |
453 | CPU_SET( bit, &cpuset); | 417 | CPU_SET(bit, &cpuset); |
454 | } | 418 | } |
455 | ++ bit; | 419 | ++bit; |
456 | aff >>= 1; | 420 | aff >>= 1; |
457 | } | 421 | } |
458 | #ifdef __ANDROID__ | 422 | #ifdef __ANDROID__ |
459 | PT_CALL( sched_setaffinity( pthread_self(), sizeof(cpu_set_t), &cpuset)); | 423 | PT_CALL(sched_setaffinity(pthread_self(), sizeof(cpu_set_t), &cpuset)); |
460 | #elif defined(__NetBSD__) | 424 | #elif defined(__NetBSD__) |
461 | PT_CALL( pthread_setaffinity_np( pthread_self(), cpuset_size(cpuset), cpuset)); | 425 | PT_CALL(pthread_setaffinity_np(pthread_self(), cpuset_size(cpuset), cpuset)); |
462 | cpuset_destroy( cpuset); | 426 | cpuset_destroy(cpuset); |
463 | #else | 427 | #else |
464 | PT_CALL( pthread_setaffinity_np( pthread_self(), sizeof(cpu_set_t), &cpuset)); | 428 | PT_CALL(pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset)); |
465 | #endif | 429 | #endif |
466 | } | 430 | } |
467 | 431 | ||
468 | void THREAD_SETNAME( char const* _name) | 432 | // ################################################################################################# |
469 | { | 433 | |
470 | // exact API to set the thread name is platform-dependant | 434 | void THREAD_SETNAME(char const* _name) |
471 | // if you need to fix the build, or if you know how to fill a hole, tell me (bnt.germain@gmail.com) so that I can submit the fix in github. | 435 | { |
436 | // exact API to set the thread name is platform-dependant | ||
437 | // if you need to fix the build, or if you know how to fill a hole, tell me (bnt.germain@gmail.com) so that I can submit the fix in github. | ||
472 | #if defined PLATFORM_BSD && !defined __NetBSD__ | 438 | #if defined PLATFORM_BSD && !defined __NetBSD__ |
473 | pthread_set_name_np( pthread_self(), _name); | 439 | pthread_set_name_np(pthread_self(), _name); |
474 | #elif defined PLATFORM_BSD && defined __NetBSD__ | 440 | #elif defined PLATFORM_BSD && defined __NetBSD__ |
475 | pthread_setname_np( pthread_self(), "%s", (void *)_name); | 441 | pthread_setname_np(pthread_self(), "%s", (void*) _name); |
476 | #elif defined PLATFORM_LINUX | 442 | #elif defined PLATFORM_LINUX |
477 | #if LINUX_USE_PTHREAD_SETNAME_NP | 443 | #if LINUX_USE_PTHREAD_SETNAME_NP |
478 | pthread_setname_np( pthread_self(), _name); | 444 | pthread_setname_np(pthread_self(), _name); |
479 | #else // LINUX_USE_PTHREAD_SETNAME_NP | 445 | #else // LINUX_USE_PTHREAD_SETNAME_NP |
480 | prctl(PR_SET_NAME, _name, 0, 0, 0); | 446 | prctl(PR_SET_NAME, _name, 0, 0, 0); |
481 | #endif // LINUX_USE_PTHREAD_SETNAME_NP | 447 | #endif // LINUX_USE_PTHREAD_SETNAME_NP |
482 | #elif defined PLATFORM_QNX || defined PLATFORM_CYGWIN | 448 | #elif defined PLATFORM_QNX || defined PLATFORM_CYGWIN |
483 | pthread_setname_np( pthread_self(), _name); | 449 | pthread_setname_np(pthread_self(), _name); |
484 | #elif defined PLATFORM_OSX | 450 | #elif defined PLATFORM_OSX |
485 | pthread_setname_np(_name); | 451 | pthread_setname_np(_name); |
486 | #elif defined PLATFORM_WIN32 || defined PLATFORM_POCKETPC | 452 | #else |
487 | PT_CALL( pthread_setname_np( pthread_self(), _name)); | 453 | fprintf(stderr, "THREAD_SETNAME: unsupported platform\n"); |
454 | abort(); | ||
488 | #endif | 455 | #endif |
489 | } | 456 | } |
457 | |||
490 | #endif // THREADAPI == THREADAPI_PTHREAD | 458 | #endif // THREADAPI == THREADAPI_PTHREAD |
459 | // ################################################################################################# | ||
460 | // ################################################################################################# | ||