diff options
Diffstat (limited to 'src/threading.cpp')
-rw-r--r-- | src/threading.cpp | 1041 |
1 files changed, 1041 insertions, 0 deletions
diff --git a/src/threading.cpp b/src/threading.cpp new file mode 100644 index 0000000..2464d03 --- /dev/null +++ b/src/threading.cpp | |||
@@ -0,0 +1,1041 @@ | |||
1 | /* | ||
2 | * THREADING.C Copyright (c) 2007-08, Asko Kauppi | ||
3 | * Copyright (C) 2009-19, Benoit Germain | ||
4 | * | ||
5 | * Lua Lanes OS threading specific code. | ||
6 | * | ||
7 | * References: | ||
8 | * <http://www.cse.wustl.edu/~schmidt/win32-cv-1.html> | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | =============================================================================== | ||
13 | |||
14 | Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> | ||
15 | Copyright (C) 2009-14, Benoit Germain <bnt.germain@gmail.com> | ||
16 | |||
17 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
18 | of this software and associated documentation files (the "Software"), to deal | ||
19 | in the Software without restriction, including without limitation the rights | ||
20 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
21 | copies of the Software, and to permit persons to whom the Software is | ||
22 | furnished to do so, subject to the following conditions: | ||
23 | |||
24 | The above copyright notice and this permission notice shall be included in | ||
25 | all copies or substantial portions of the Software. | ||
26 | |||
27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
30 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
31 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
33 | THE SOFTWARE. | ||
34 | |||
35 | =============================================================================== | ||
36 | */ | ||
37 | #if defined(__linux__) | ||
38 | |||
39 | # ifndef _GNU_SOURCE // definition by the makefile can cause a redefinition error | ||
40 | # define _GNU_SOURCE // must be defined before any include | ||
41 | # endif // _GNU_SOURCE | ||
42 | |||
43 | # ifdef __ANDROID__ | ||
44 | # include <android/log.h> | ||
45 | # define LOG_TAG "LuaLanes" | ||
46 | # endif // __ANDROID__ | ||
47 | |||
48 | #endif // __linux__ | ||
49 | |||
50 | #include <stdio.h> | ||
51 | #include <stdlib.h> | ||
52 | #include <assert.h> | ||
53 | #include <errno.h> | ||
54 | #include <math.h> | ||
55 | |||
56 | #include "threading.h" | ||
57 | |||
58 | #if !defined( PLATFORM_XBOX) && !defined( PLATFORM_WIN32) && !defined( PLATFORM_POCKETPC) | ||
59 | # include <sys/time.h> | ||
60 | #endif // non-WIN32 timing | ||
61 | |||
62 | |||
63 | #if defined(PLATFORM_LINUX) || defined(PLATFORM_CYGWIN) | ||
64 | # include <sys/types.h> | ||
65 | # include <unistd.h> | ||
66 | #endif | ||
67 | |||
68 | /* Linux needs to check, whether it's been run as root | ||
69 | */ | ||
70 | #ifdef PLATFORM_LINUX | ||
71 | volatile bool_t sudo; | ||
72 | #endif | ||
73 | |||
74 | #ifdef PLATFORM_OSX | ||
75 | # include "threading_osx.h" | ||
76 | #endif | ||
77 | |||
78 | /* Linux with older glibc (such as Debian) don't have pthread_setname_np, but have prctl | ||
79 | */ | ||
80 | #if defined PLATFORM_LINUX | ||
81 | #if defined __GNU_LIBRARY__ && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 12 | ||
82 | #define LINUX_USE_PTHREAD_SETNAME_NP 1 | ||
83 | #else // glibc without pthread_setname_np | ||
84 | #include <sys/prctl.h> | ||
85 | #define LINUX_USE_PTHREAD_SETNAME_NP 0 | ||
86 | #endif // glibc without pthread_setname_np | ||
87 | #endif // PLATFORM_LINUX | ||
88 | |||
89 | #ifdef _MSC_VER | ||
90 | // ".. selected for automatic inline expansion" (/O2 option) | ||
91 | # pragma warning( disable : 4711 ) | ||
92 | // ".. type cast from function pointer ... to data pointer" | ||
93 | # pragma warning( disable : 4054 ) | ||
94 | #endif | ||
95 | |||
96 | //#define THREAD_CREATE_RETRIES_MAX 20 | ||
97 | // loops (maybe retry forever?) | ||
98 | |||
99 | /* | ||
100 | * FAIL is for unexpected API return values - essentially programming | ||
101 | * error in _this_ code. | ||
102 | */ | ||
103 | #if defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC) | ||
104 | static void FAIL( char const* funcname, int rc) | ||
105 | { | ||
106 | #if defined( PLATFORM_XBOX) | ||
107 | fprintf( stderr, "%s() failed! (%d)\n", funcname, rc ); | ||
108 | #else // PLATFORM_XBOX | ||
109 | char buf[256]; | ||
110 | FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM, NULL, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, NULL); | ||
111 | fprintf( stderr, "%s() failed! [GetLastError() -> %d] '%s'", funcname, rc, buf); | ||
112 | #endif // PLATFORM_XBOX | ||
113 | #ifdef _MSC_VER | ||
114 | __debugbreak(); // give a chance to the debugger! | ||
115 | #endif // _MSC_VER | ||
116 | abort(); | ||
117 | } | ||
118 | #endif // win32 build | ||
119 | |||
120 | |||
121 | /* | ||
122 | * Returns millisecond timing (in seconds) for the current time. | ||
123 | * | ||
124 | * Note: This function should be called once in single-threaded mode in Win32, | ||
125 | * to get it initialized. | ||
126 | */ | ||
127 | time_d now_secs(void) { | ||
128 | |||
129 | #if defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC) | ||
130 | /* | ||
131 | * Windows FILETIME values are "100-nanosecond intervals since | ||
132 | * January 1, 1601 (UTC)" (MSDN). Well, we'd want Unix Epoch as | ||
133 | * the offset and it seems, so would they: | ||
134 | * | ||
135 | * <http://msdn.microsoft.com/en-us/library/ms724928(VS.85).aspx> | ||
136 | */ | ||
137 | SYSTEMTIME st; | ||
138 | FILETIME ft; | ||
139 | ULARGE_INTEGER uli; | ||
140 | static ULARGE_INTEGER uli_epoch; // Jan 1st 1970 0:0:0 | ||
141 | |||
142 | if (uli_epoch.HighPart==0) { | ||
143 | st.wYear= 1970; | ||
144 | st.wMonth= 1; // Jan | ||
145 | st.wDay= 1; | ||
146 | st.wHour= st.wMinute= st.wSecond= st.wMilliseconds= 0; | ||
147 | |||
148 | if (!SystemTimeToFileTime( &st, &ft )) | ||
149 | FAIL( "SystemTimeToFileTime", GetLastError() ); | ||
150 | |||
151 | uli_epoch.LowPart= ft.dwLowDateTime; | ||
152 | uli_epoch.HighPart= ft.dwHighDateTime; | ||
153 | } | ||
154 | |||
155 | GetSystemTime( &st ); // current system date/time in UTC | ||
156 | if (!SystemTimeToFileTime( &st, &ft )) | ||
157 | FAIL( "SystemTimeToFileTime", GetLastError() ); | ||
158 | |||
159 | uli.LowPart= ft.dwLowDateTime; | ||
160 | uli.HighPart= ft.dwHighDateTime; | ||
161 | |||
162 | /* 'double' has less accuracy than 64-bit int, but if it were to degrade, | ||
163 | * it would do so gracefully. In practice, the integer accuracy is not | ||
164 | * of the 100ns class but just 1ms (Windows XP). | ||
165 | */ | ||
166 | # if 1 | ||
167 | // >= 2.0.3 code | ||
168 | return (double) ((uli.QuadPart - uli_epoch.QuadPart)/10000) / 1000.0; | ||
169 | # elif 0 | ||
170 | // fix from Kriss Daniels, see: | ||
171 | // <http://luaforge.net/forum/forum.php?thread_id=22704&forum_id=1781> | ||
172 | // | ||
173 | // "seem to be getting negative numbers from the old version, probably number | ||
174 | // conversion clipping, this fixes it and maintains ms resolution" | ||
175 | // | ||
176 | // This was a bad fix, and caused timer test 5 sec timers to disappear. | ||
177 | // --AKa 25-Jan-2009 | ||
178 | // | ||
179 | return ((double)((signed)((uli.QuadPart/10000) - (uli_epoch.QuadPart/10000)))) / 1000.0; | ||
180 | # else | ||
181 | // <= 2.0.2 code | ||
182 | return (double)(uli.QuadPart - uli_epoch.QuadPart) / 10000000.0; | ||
183 | # endif | ||
184 | #else // !(defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC)) | ||
185 | struct timeval tv; | ||
186 | // { | ||
187 | // time_t tv_sec; /* seconds since Jan. 1, 1970 */ | ||
188 | // suseconds_t tv_usec; /* and microseconds */ | ||
189 | // }; | ||
190 | |||
191 | int rc= gettimeofday( &tv, NULL /*time zone not used any more (in Linux)*/ ); | ||
192 | assert( rc==0 ); | ||
193 | |||
194 | return ((double)tv.tv_sec) + ((tv.tv_usec)/1000) / 1000.0; | ||
195 | #endif // !(defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC)) | ||
196 | } | ||
197 | |||
198 | |||
199 | /* | ||
200 | */ | ||
201 | time_d SIGNAL_TIMEOUT_PREPARE( double secs ) { | ||
202 | if (secs<=0.0) return secs; | ||
203 | else return now_secs() + secs; | ||
204 | } | ||
205 | |||
206 | |||
207 | #if THREADAPI == THREADAPI_PTHREAD | ||
208 | /* | ||
209 | * Prepare 'abs_secs' kind of timeout to 'timespec' format | ||
210 | */ | ||
211 | static void prepare_timeout( struct timespec *ts, time_d abs_secs ) { | ||
212 | assert(ts); | ||
213 | assert( abs_secs >= 0.0 ); | ||
214 | |||
215 | if (abs_secs==0.0) | ||
216 | abs_secs= now_secs(); | ||
217 | |||
218 | ts->tv_sec= (time_t) floor( abs_secs ); | ||
219 | ts->tv_nsec= ((long)((abs_secs - ts->tv_sec) * 1000.0 +0.5)) * 1000000UL; // 1ms = 1000000ns | ||
220 | if (ts->tv_nsec == 1000000000UL) | ||
221 | { | ||
222 | ts->tv_nsec = 0; | ||
223 | ts->tv_sec = ts->tv_sec + 1; | ||
224 | } | ||
225 | } | ||
226 | #endif // THREADAPI == THREADAPI_PTHREAD | ||
227 | |||
228 | |||
229 | /*---=== Threading ===---*/ | ||
230 | |||
231 | //--- | ||
232 | // It may be meaningful to explicitly limit the new threads' C stack size. | ||
233 | // We should know how much Lua needs in the C stack, all Lua side allocations | ||
234 | // are done in heap so they don't count. | ||
235 | // | ||
236 | // Consequence of _not_ limiting the stack is running out of virtual memory | ||
237 | // with 1000-5000 threads on 32-bit systems. | ||
238 | // | ||
239 | // Note: using external C modules may be affected by the stack size check. | ||
240 | // if having problems, set back to '0' (default stack size of the system). | ||
241 | // | ||
242 | // Win32: 64K (?) | ||
243 | // Win64: xxx | ||
244 | // | ||
245 | // Linux x86: 2MB Ubuntu 7.04 via 'pthread_getstacksize()' | ||
246 | // Linux x64: xxx | ||
247 | // Linux ARM: xxx | ||
248 | // | ||
249 | // OS X 10.4.9: 512K <http://developer.apple.com/qa/qa2005/qa1419.html> | ||
250 | // valid values N * 4KB | ||
251 | // | ||
252 | #ifndef _THREAD_STACK_SIZE | ||
253 | # if defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC) || defined( PLATFORM_CYGWIN) | ||
254 | # define _THREAD_STACK_SIZE 0 | ||
255 | // Win32: does it work with less? | ||
256 | # elif (defined PLATFORM_OSX) | ||
257 | # define _THREAD_STACK_SIZE (524288/2) // 262144 | ||
258 | // OS X: "make test" works on 65536 and even below | ||
259 | // "make perftest" works on >= 4*65536 == 262144 (not 3*65536) | ||
260 | # elif (defined PLATFORM_LINUX) && (defined __i386) | ||
261 | # define _THREAD_STACK_SIZE (2097152/16) // 131072 | ||
262 | // Linux x86 (Ubuntu 7.04): "make perftest" works on /16 (not on /32) | ||
263 | # elif (defined PLATFORM_BSD) && (defined __i386) | ||
264 | # define _THREAD_STACK_SIZE (1048576/8) // 131072 | ||
265 | // FreeBSD 6.2 SMP i386: ("gmake perftest" works on /8 (not on /16) | ||
266 | # endif | ||
267 | #endif | ||
268 | |||
269 | #if THREADAPI == THREADAPI_WINDOWS | ||
270 | |||
271 | #if _WIN32_WINNT < 0x0600 // CONDITION_VARIABLE aren't available | ||
272 | // | ||
273 | void MUTEX_INIT( MUTEX_T *ref ) { | ||
274 | *ref= CreateMutex( NULL /*security attr*/, FALSE /*not locked*/, NULL ); | ||
275 | if (!ref) FAIL( "CreateMutex", GetLastError() ); | ||
276 | } | ||
277 | void MUTEX_FREE( MUTEX_T *ref ) { | ||
278 | if (!CloseHandle(*ref)) FAIL( "CloseHandle (mutex)", GetLastError() ); | ||
279 | *ref= NULL; | ||
280 | } | ||
281 | void MUTEX_LOCK( MUTEX_T *ref ) | ||
282 | { | ||
283 | DWORD rc = WaitForSingleObject( *ref, INFINITE); | ||
284 | // ERROR_WAIT_NO_CHILDREN means a thread was killed (lane terminated because of error raised during a linda transfer for example) while having grabbed this mutex | ||
285 | // this is not a big problem as we will grab it just the same, so ignore this particular error | ||
286 | if( rc != 0 && rc != ERROR_WAIT_NO_CHILDREN) | ||
287 | FAIL( "WaitForSingleObject", (rc == WAIT_FAILED) ? GetLastError() : rc); | ||
288 | } | ||
289 | void MUTEX_UNLOCK( MUTEX_T *ref ) { | ||
290 | if (!ReleaseMutex(*ref)) | ||
291 | FAIL( "ReleaseMutex", GetLastError() ); | ||
292 | } | ||
293 | #endif // CONDITION_VARIABLE aren't available | ||
294 | |||
295 | static int const gs_prio_remap[] = | ||
296 | { | ||
297 | THREAD_PRIORITY_IDLE, | ||
298 | THREAD_PRIORITY_LOWEST, | ||
299 | THREAD_PRIORITY_BELOW_NORMAL, | ||
300 | THREAD_PRIORITY_NORMAL, | ||
301 | THREAD_PRIORITY_ABOVE_NORMAL, | ||
302 | THREAD_PRIORITY_HIGHEST, | ||
303 | THREAD_PRIORITY_TIME_CRITICAL | ||
304 | }; | ||
305 | |||
306 | /* MSDN: "If you would like to use the CRT in ThreadProc, use the | ||
307 | _beginthreadex function instead (of CreateThread)." | ||
308 | MSDN: "you can create at most 2028 threads" | ||
309 | */ | ||
310 | // Note: Visual C++ requires '__stdcall' where it is | ||
311 | void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (__stdcall *func)( void*), void* data, int prio /* -3..+3 */) | ||
312 | { | ||
313 | HANDLE h = (HANDLE) _beginthreadex( NULL, // security | ||
314 | _THREAD_STACK_SIZE, | ||
315 | func, | ||
316 | data, | ||
317 | 0, // flags (0/CREATE_SUSPENDED) | ||
318 | NULL // thread id (not used) | ||
319 | ); | ||
320 | |||
321 | if( h == NULL) // _beginthreadex returns 0L on failure instead of -1L (like _beginthread) | ||
322 | { | ||
323 | FAIL( "CreateThread", GetLastError()); | ||
324 | } | ||
325 | |||
326 | if (prio != THREAD_PRIO_DEFAULT) | ||
327 | { | ||
328 | if (!SetThreadPriority( h, gs_prio_remap[prio + 3])) | ||
329 | { | ||
330 | FAIL( "SetThreadPriority", GetLastError()); | ||
331 | } | ||
332 | } | ||
333 | |||
334 | *ref = h; | ||
335 | } | ||
336 | |||
337 | |||
338 | void THREAD_SET_PRIORITY( int prio) | ||
339 | { | ||
340 | // prio range [-3,+3] was checked by the caller | ||
341 | if (!SetThreadPriority( GetCurrentThread(), gs_prio_remap[prio + 3])) | ||
342 | { | ||
343 | FAIL( "THREAD_SET_PRIORITY", GetLastError()); | ||
344 | } | ||
345 | } | ||
346 | |||
347 | void THREAD_SET_AFFINITY( unsigned int aff) | ||
348 | { | ||
349 | if( !SetThreadAffinityMask( GetCurrentThread(), aff)) | ||
350 | { | ||
351 | FAIL( "THREAD_SET_AFFINITY", GetLastError()); | ||
352 | } | ||
353 | } | ||
354 | |||
355 | bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs) | ||
356 | { | ||
357 | DWORD ms = (secs<0.0) ? INFINITE : (DWORD)((secs*1000.0)+0.5); | ||
358 | |||
359 | DWORD rc= WaitForSingleObject( *ref, ms /*timeout*/ ); | ||
360 | // | ||
361 | // (WAIT_ABANDONED) | ||
362 | // WAIT_OBJECT_0 success (0) | ||
363 | // WAIT_TIMEOUT | ||
364 | // WAIT_FAILED more info via GetLastError() | ||
365 | |||
366 | if (rc == WAIT_TIMEOUT) return FALSE; | ||
367 | if( rc !=0) FAIL( "WaitForSingleObject", rc==WAIT_FAILED ? GetLastError() : rc); | ||
368 | *ref= NULL; // thread no longer usable | ||
369 | return TRUE; | ||
370 | } | ||
371 | // | ||
372 | void THREAD_KILL( THREAD_T *ref ) | ||
373 | { | ||
374 | // nonexistent on Xbox360, simply disable until a better solution is found | ||
375 | #if !defined( PLATFORM_XBOX) | ||
376 | // in theory no-one should call this as it is very dangerous (memory and mutex leaks, no notification of DLLs, etc.) | ||
377 | if (!TerminateThread( *ref, 0 )) FAIL("TerminateThread", GetLastError()); | ||
378 | #endif // PLATFORM_XBOX | ||
379 | *ref= NULL; | ||
380 | } | ||
381 | |||
382 | void THREAD_MAKE_ASYNCH_CANCELLABLE() {} // nothing to do for windows threads, we can cancel them anytime we want | ||
383 | |||
384 | #if !defined __GNUC__ | ||
385 | //see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx | ||
386 | #define MS_VC_EXCEPTION 0x406D1388 | ||
387 | #pragma pack(push,8) | ||
388 | typedef struct tagTHREADNAME_INFO | ||
389 | { | ||
390 | DWORD dwType; // Must be 0x1000. | ||
391 | LPCSTR szName; // Pointer to name (in user addr space). | ||
392 | DWORD dwThreadID; // Thread ID (-1=caller thread). | ||
393 | DWORD dwFlags; // Reserved for future use, must be zero. | ||
394 | } THREADNAME_INFO; | ||
395 | #pragma pack(pop) | ||
396 | #endif // !__GNUC__ | ||
397 | |||
398 | void THREAD_SETNAME( char const* _name) | ||
399 | { | ||
400 | #if !defined __GNUC__ | ||
401 | THREADNAME_INFO info; | ||
402 | info.dwType = 0x1000; | ||
403 | info.szName = _name; | ||
404 | info.dwThreadID = GetCurrentThreadId(); | ||
405 | info.dwFlags = 0; | ||
406 | |||
407 | __try | ||
408 | { | ||
409 | RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); | ||
410 | } | ||
411 | __except(EXCEPTION_EXECUTE_HANDLER) | ||
412 | { | ||
413 | } | ||
414 | #endif // !__GNUC__ | ||
415 | } | ||
416 | |||
417 | #if _WIN32_WINNT < 0x0600 // CONDITION_VARIABLE aren't available | ||
418 | |||
419 | void SIGNAL_INIT( SIGNAL_T* ref) | ||
420 | { | ||
421 | InitializeCriticalSection( &ref->signalCS); | ||
422 | InitializeCriticalSection( &ref->countCS); | ||
423 | if( 0 == (ref->waitEvent = CreateEvent( 0, TRUE, FALSE, 0))) // manual-reset | ||
424 | FAIL( "CreateEvent", GetLastError()); | ||
425 | if( 0 == (ref->waitDoneEvent = CreateEvent( 0, FALSE, FALSE, 0))) // auto-reset | ||
426 | FAIL( "CreateEvent", GetLastError()); | ||
427 | ref->waitersCount = 0; | ||
428 | } | ||
429 | |||
430 | void SIGNAL_FREE( SIGNAL_T* ref) | ||
431 | { | ||
432 | CloseHandle( ref->waitDoneEvent); | ||
433 | CloseHandle( ref->waitEvent); | ||
434 | DeleteCriticalSection( &ref->countCS); | ||
435 | DeleteCriticalSection( &ref->signalCS); | ||
436 | } | ||
437 | |||
438 | bool_t SIGNAL_WAIT( SIGNAL_T* ref, MUTEX_T* mu_ref, time_d abs_secs) | ||
439 | { | ||
440 | DWORD errc; | ||
441 | DWORD ms; | ||
442 | |||
443 | if( abs_secs < 0.0) | ||
444 | ms = INFINITE; | ||
445 | else if( abs_secs == 0.0) | ||
446 | ms = 0; | ||
447 | else | ||
448 | { | ||
449 | time_d msd = (abs_secs - now_secs()) * 1000.0 + 0.5; | ||
450 | // If the time already passed, still try once (ms==0). A short timeout | ||
451 | // may have turned negative or 0 because of the two time samples done. | ||
452 | ms = msd <= 0.0 ? 0 : (DWORD)msd; | ||
453 | } | ||
454 | |||
455 | EnterCriticalSection( &ref->signalCS); | ||
456 | EnterCriticalSection( &ref->countCS); | ||
457 | ++ ref->waitersCount; | ||
458 | LeaveCriticalSection( &ref->countCS); | ||
459 | LeaveCriticalSection( &ref->signalCS); | ||
460 | |||
461 | errc = SignalObjectAndWait( *mu_ref, ref->waitEvent, ms, FALSE); | ||
462 | |||
463 | EnterCriticalSection( &ref->countCS); | ||
464 | if( 0 == -- ref->waitersCount) | ||
465 | { | ||
466 | // we're the last one leaving... | ||
467 | ResetEvent( ref->waitEvent); | ||
468 | SetEvent( ref->waitDoneEvent); | ||
469 | } | ||
470 | LeaveCriticalSection( &ref->countCS); | ||
471 | MUTEX_LOCK( mu_ref); | ||
472 | |||
473 | switch( errc) | ||
474 | { | ||
475 | case WAIT_TIMEOUT: | ||
476 | return FALSE; | ||
477 | case WAIT_OBJECT_0: | ||
478 | return TRUE; | ||
479 | } | ||
480 | |||
481 | FAIL( "SignalObjectAndWait", GetLastError()); | ||
482 | return FALSE; | ||
483 | } | ||
484 | |||
485 | void SIGNAL_ALL( SIGNAL_T* ref) | ||
486 | { | ||
487 | DWORD errc = WAIT_OBJECT_0; | ||
488 | |||
489 | EnterCriticalSection( &ref->signalCS); | ||
490 | EnterCriticalSection( &ref->countCS); | ||
491 | |||
492 | if( ref->waitersCount > 0) | ||
493 | { | ||
494 | ResetEvent( ref->waitDoneEvent); | ||
495 | SetEvent( ref->waitEvent); | ||
496 | LeaveCriticalSection( &ref->countCS); | ||
497 | errc = WaitForSingleObject( ref->waitDoneEvent, INFINITE); | ||
498 | } | ||
499 | else | ||
500 | { | ||
501 | LeaveCriticalSection( &ref->countCS); | ||
502 | } | ||
503 | |||
504 | LeaveCriticalSection( &ref->signalCS); | ||
505 | |||
506 | if( WAIT_OBJECT_0 != errc) | ||
507 | FAIL( "WaitForSingleObject", GetLastError()); | ||
508 | } | ||
509 | |||
510 | #else // CONDITION_VARIABLE are available, use them | ||
511 | |||
512 | // | ||
513 | void SIGNAL_INIT( SIGNAL_T *ref ) | ||
514 | { | ||
515 | InitializeConditionVariable( ref); | ||
516 | } | ||
517 | |||
518 | void SIGNAL_FREE( SIGNAL_T *ref ) | ||
519 | { | ||
520 | // nothing to do | ||
521 | (void)ref; | ||
522 | } | ||
523 | |||
524 | bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu_ref, time_d abs_secs) | ||
525 | { | ||
526 | long ms; | ||
527 | |||
528 | if( abs_secs < 0.0) | ||
529 | ms = INFINITE; | ||
530 | else if( abs_secs == 0.0) | ||
531 | ms = 0; | ||
532 | else | ||
533 | { | ||
534 | ms = (long) ((abs_secs - now_secs())*1000.0 + 0.5); | ||
535 | |||
536 | // If the time already passed, still try once (ms==0). A short timeout | ||
537 | // may have turned negative or 0 because of the two time samples done. | ||
538 | // | ||
539 | if( ms < 0) | ||
540 | ms = 0; | ||
541 | } | ||
542 | |||
543 | if( !SleepConditionVariableCS( ref, mu_ref, ms)) | ||
544 | { | ||
545 | if( GetLastError() == ERROR_TIMEOUT) | ||
546 | { | ||
547 | return FALSE; | ||
548 | } | ||
549 | else | ||
550 | { | ||
551 | FAIL( "SleepConditionVariableCS", GetLastError()); | ||
552 | } | ||
553 | } | ||
554 | return TRUE; | ||
555 | } | ||
556 | |||
557 | void SIGNAL_ONE( SIGNAL_T *ref ) | ||
558 | { | ||
559 | WakeConditionVariable( ref); | ||
560 | } | ||
561 | |||
562 | void SIGNAL_ALL( SIGNAL_T *ref ) | ||
563 | { | ||
564 | WakeAllConditionVariable( ref); | ||
565 | } | ||
566 | |||
567 | #endif // CONDITION_VARIABLE are available | ||
568 | |||
569 | #else // THREADAPI == THREADAPI_PTHREAD | ||
570 | // PThread (Linux, OS X, ...) | ||
571 | // | ||
572 | // On OS X, user processes seem to be able to change priorities. | ||
573 | // On Linux, SCHED_RR and su privileges are required.. !-( | ||
574 | // | ||
575 | #include <errno.h> | ||
576 | #include <sched.h> | ||
577 | |||
578 | # if (defined(__MINGW32__) || defined(__MINGW64__)) && defined pthread_attr_setschedpolicy | ||
579 | # if pthread_attr_setschedpolicy( A, S) == ENOTSUP | ||
580 | // from the mingw-w64 team: | ||
581 | // Well, we support pthread_setschedparam by which you can specify | ||
582 | // threading-policy. Nevertheless, yes we lack this function. In | ||
583 | // general its implementation is pretty much trivial, as on Win32 target | ||
584 | // just SCHED_OTHER can be supported. | ||
585 | #undef pthread_attr_setschedpolicy | ||
586 | static int pthread_attr_setschedpolicy( pthread_attr_t* attr, int policy) | ||
587 | { | ||
588 | if( policy != SCHED_OTHER) | ||
589 | { | ||
590 | return ENOTSUP; | ||
591 | } | ||
592 | return 0; | ||
593 | } | ||
594 | # endif // pthread_attr_setschedpolicy() | ||
595 | # endif // defined(__MINGW32__) || defined(__MINGW64__) | ||
596 | |||
597 | static void _PT_FAIL( int rc, const char *name, const char *file, uint_t line ) { | ||
598 | const char *why= (rc==EINVAL) ? "EINVAL" : | ||
599 | (rc==EBUSY) ? "EBUSY" : | ||
600 | (rc==EPERM) ? "EPERM" : | ||
601 | (rc==ENOMEM) ? "ENOMEM" : | ||
602 | (rc==ESRCH) ? "ESRCH" : | ||
603 | (rc==ENOTSUP) ? "ENOTSUP": | ||
604 | //... | ||
605 | "<UNKNOWN>"; | ||
606 | fprintf( stderr, "%s %d: %s failed, %d %s\n", file, line, name, rc, why ); | ||
607 | abort(); | ||
608 | } | ||
609 | #define PT_CALL( call ) { int rc= call; if (rc!=0) _PT_FAIL( rc, #call, __FILE__, __LINE__ ); } | ||
610 | // | ||
611 | void SIGNAL_INIT( SIGNAL_T *ref ) { | ||
612 | PT_CALL( pthread_cond_init(ref,NULL /*attr*/) ); | ||
613 | } | ||
614 | void SIGNAL_FREE( SIGNAL_T *ref ) { | ||
615 | PT_CALL( pthread_cond_destroy(ref) ); | ||
616 | } | ||
617 | // | ||
618 | /* | ||
619 | * Timeout is given as absolute since we may have fake wakeups during | ||
620 | * a timed out sleep. A Linda with some other key read, or just because | ||
621 | * PThread cond vars can wake up unwantedly. | ||
622 | */ | ||
623 | bool_t SIGNAL_WAIT( SIGNAL_T *ref, pthread_mutex_t *mu, time_d abs_secs ) { | ||
624 | if (abs_secs<0.0) { | ||
625 | PT_CALL( pthread_cond_wait( ref, mu ) ); // infinite | ||
626 | } else { | ||
627 | int rc; | ||
628 | struct timespec ts; | ||
629 | |||
630 | assert( abs_secs != 0.0 ); | ||
631 | prepare_timeout( &ts, abs_secs ); | ||
632 | |||
633 | rc= pthread_cond_timedwait( ref, mu, &ts ); | ||
634 | |||
635 | if (rc==ETIMEDOUT) return FALSE; | ||
636 | if (rc) { _PT_FAIL( rc, "pthread_cond_timedwait()", __FILE__, __LINE__ ); } | ||
637 | } | ||
638 | return TRUE; | ||
639 | } | ||
640 | // | ||
641 | void SIGNAL_ONE( SIGNAL_T *ref ) { | ||
642 | PT_CALL( pthread_cond_signal(ref) ); // wake up ONE (or no) waiting thread | ||
643 | } | ||
644 | // | ||
645 | void SIGNAL_ALL( SIGNAL_T *ref ) { | ||
646 | PT_CALL( pthread_cond_broadcast(ref) ); // wake up ALL waiting threads | ||
647 | } | ||
648 | |||
649 | // array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range | ||
650 | static int const gs_prio_remap[] = | ||
651 | { | ||
652 | // NB: PThreads priority handling is about as twisty as one can get it | ||
653 | // (and then some). DON*T TRUST ANYTHING YOU READ ON THE NET!!! | ||
654 | |||
655 | //--- | ||
656 | // "Select the scheduling policy for the thread: one of SCHED_OTHER | ||
657 | // (regular, non-real-time scheduling), SCHED_RR (real-time, | ||
658 | // round-robin) or SCHED_FIFO (real-time, first-in first-out)." | ||
659 | // | ||
660 | // "Using the RR policy ensures that all threads having the same | ||
661 | // priority level will be scheduled equally, regardless of their activity." | ||
662 | // | ||
663 | // "For SCHED_FIFO and SCHED_RR, the only required member of the | ||
664 | // sched_param structure is the priority sched_priority. For SCHED_OTHER, | ||
665 | // the affected scheduling parameters are implementation-defined." | ||
666 | // | ||
667 | // "The priority of a thread is specified as a delta which is added to | ||
668 | // the priority of the process." | ||
669 | // | ||
670 | // ".. priority is an integer value, in the range from 1 to 127. | ||
671 | // 1 is the least-favored priority, 127 is the most-favored." | ||
672 | // | ||
673 | // "Priority level 0 cannot be used: it is reserved for the system." | ||
674 | // | ||
675 | // "When you use specify a priority of -99 in a call to | ||
676 | // pthread_setschedparam(), the priority of the target thread is | ||
677 | // lowered to the lowest possible value." | ||
678 | // | ||
679 | // ... | ||
680 | |||
681 | // ** CONCLUSION ** | ||
682 | // | ||
683 | // PThread priorities are _hugely_ system specific, and we need at | ||
684 | // least OS specific settings. Hopefully, Linuxes and OS X versions | ||
685 | // are uniform enough, among each other... | ||
686 | // | ||
687 | # if defined PLATFORM_OSX | ||
688 | // AK 10-Apr-07 (OS X PowerPC 10.4.9): | ||
689 | // | ||
690 | // With SCHED_RR, 26 seems to be the "normal" priority, where setting | ||
691 | // it does not seem to affect the order of threads processed. | ||
692 | // | ||
693 | // With SCHED_OTHER, the range 25..32 is normal (maybe the same 26, | ||
694 | // but the difference is not so clear with OTHER). | ||
695 | // | ||
696 | // 'sched_get_priority_min()' and '..max()' give 15, 47 as the | ||
697 | // priority limits. This could imply, user mode applications won't | ||
698 | // be able to use values outside of that range. | ||
699 | // | ||
700 | # define _PRIO_MODE SCHED_OTHER | ||
701 | |||
702 | // OS X 10.4.9 (PowerPC) gives ENOTSUP for process scope | ||
703 | //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS | ||
704 | |||
705 | # define _PRIO_HI 32 // seems to work (_carefully_ picked!) | ||
706 | # define _PRIO_0 26 // detected | ||
707 | # define _PRIO_LO 1 // seems to work (tested) | ||
708 | |||
709 | # elif defined PLATFORM_LINUX | ||
710 | // (based on Ubuntu Linux 2.6.15 kernel) | ||
711 | // | ||
712 | // SCHED_OTHER is the default policy, but does not allow for priorities. | ||
713 | // SCHED_RR allows priorities, all of which (1..99) are higher than | ||
714 | // a thread with SCHED_OTHER policy. | ||
715 | // | ||
716 | // <http://kerneltrap.org/node/6080> | ||
717 | // <http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library> | ||
718 | // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html> | ||
719 | // | ||
720 | // Manuals suggest checking #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING, | ||
721 | // but even Ubuntu does not seem to define it. | ||
722 | // | ||
723 | # define _PRIO_MODE SCHED_RR | ||
724 | |||
725 | // NTLP 2.5: only system scope allowed (being the basic reason why | ||
726 | // root privileges are required..) | ||
727 | //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS | ||
728 | |||
729 | # define _PRIO_HI 99 | ||
730 | # define _PRIO_0 50 | ||
731 | # define _PRIO_LO 1 | ||
732 | |||
733 | # elif defined(PLATFORM_BSD) | ||
734 | // | ||
735 | // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html> | ||
736 | // | ||
737 | // "When control over the thread scheduling is desired, then FreeBSD | ||
738 | // with the libpthread implementation is by far the best choice .." | ||
739 | // | ||
740 | # define _PRIO_MODE SCHED_OTHER | ||
741 | # define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS | ||
742 | # define _PRIO_HI 31 | ||
743 | # define _PRIO_0 15 | ||
744 | # define _PRIO_LO 1 | ||
745 | |||
746 | # elif defined(PLATFORM_CYGWIN) | ||
747 | // | ||
748 | // TBD: Find right values for Cygwin | ||
749 | // | ||
750 | # elif defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC) | ||
751 | // any other value not supported by win32-pthread as of version 2.9.1 | ||
752 | # define _PRIO_MODE SCHED_OTHER | ||
753 | |||
754 | // PTHREAD_SCOPE_PROCESS not supported by win32-pthread as of version 2.9.1 | ||
755 | //#define _PRIO_SCOPE PTHREAD_SCOPE_SYSTEM // but do we need this at all to start with? | ||
756 | THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST, THREAD_PRIORITY_TIME_CRITICAL | ||
757 | |||
758 | # else | ||
759 | # error "Unknown OS: not implemented!" | ||
760 | # endif | ||
761 | |||
762 | #if defined _PRIO_0 | ||
763 | # define _PRIO_AN (_PRIO_0 + ((_PRIO_HI-_PRIO_0)/2)) | ||
764 | # define _PRIO_BN (_PRIO_LO + ((_PRIO_0-_PRIO_LO)/2)) | ||
765 | |||
766 | _PRIO_LO, _PRIO_LO, _PRIO_BN, _PRIO_0, _PRIO_AN, _PRIO_HI, _PRIO_HI | ||
767 | #endif // _PRIO_0 | ||
768 | }; | ||
769 | |||
770 | static int select_prio(int prio /* -3..+3 */) | ||
771 | { | ||
772 | if (prio == THREAD_PRIO_DEFAULT) | ||
773 | prio = 0; | ||
774 | // prio range [-3,+3] was checked by the caller | ||
775 | return gs_prio_remap[prio + 3]; | ||
776 | } | ||
777 | |||
778 | void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void*), void* data, int prio /* -3..+3 */) | ||
779 | { | ||
780 | pthread_attr_t a; | ||
781 | bool_t const change_priority = | ||
782 | #ifdef PLATFORM_LINUX | ||
783 | sudo && // only root-privileged process can change priorities | ||
784 | #endif | ||
785 | (prio != THREAD_PRIO_DEFAULT); | ||
786 | |||
787 | PT_CALL( pthread_attr_init( &a)); | ||
788 | |||
789 | #ifndef PTHREAD_TIMEDJOIN | ||
790 | // We create a NON-JOINABLE thread. This is mainly due to the lack of | ||
791 | // 'pthread_timedjoin()', but does offer other benefits (s.a. earlier | ||
792 | // freeing of the thread's resources). | ||
793 | // | ||
794 | PT_CALL( pthread_attr_setdetachstate( &a, PTHREAD_CREATE_DETACHED)); | ||
795 | #endif // PTHREAD_TIMEDJOIN | ||
796 | |||
797 | // Use this to find a system's default stack size (DEBUG) | ||
798 | #if 0 | ||
799 | { | ||
800 | size_t n; | ||
801 | pthread_attr_getstacksize( &a, &n); | ||
802 | fprintf( stderr, "Getstack: %u\n", (unsigned int)n); | ||
803 | } | ||
804 | // 524288 on OS X | ||
805 | // 2097152 on Linux x86 (Ubuntu 7.04) | ||
806 | // 1048576 on FreeBSD 6.2 SMP i386 | ||
807 | #endif // 0 | ||
808 | |||
809 | #if defined _THREAD_STACK_SIZE && _THREAD_STACK_SIZE > 0 | ||
810 | PT_CALL( pthread_attr_setstacksize( &a, _THREAD_STACK_SIZE)); | ||
811 | #endif | ||
812 | |||
813 | if (change_priority) | ||
814 | { | ||
815 | struct sched_param sp; | ||
816 | // "The specified scheduling parameters are only used if the scheduling | ||
817 | // parameter inheritance attribute is PTHREAD_EXPLICIT_SCHED." | ||
818 | // | ||
819 | #if !defined __ANDROID__ || ( defined __ANDROID__ && __ANDROID_API__ >= 28 ) | ||
820 | PT_CALL( pthread_attr_setinheritsched( &a, PTHREAD_EXPLICIT_SCHED)); | ||
821 | #endif | ||
822 | |||
823 | #ifdef _PRIO_SCOPE | ||
824 | PT_CALL( pthread_attr_setscope( &a, _PRIO_SCOPE)); | ||
825 | #endif // _PRIO_SCOPE | ||
826 | |||
827 | PT_CALL( pthread_attr_setschedpolicy( &a, _PRIO_MODE)); | ||
828 | |||
829 | sp.sched_priority = select_prio(prio); | ||
830 | PT_CALL( pthread_attr_setschedparam( &a, &sp)); | ||
831 | } | ||
832 | |||
833 | //--- | ||
834 | // Seems on OS X, _POSIX_THREAD_THREADS_MAX is some kind of system | ||
835 | // thread limit (not userland thread). Actual limit for us is way higher. | ||
836 | // PTHREAD_THREADS_MAX is not defined (even though man page refers to it!) | ||
837 | // | ||
838 | # ifndef THREAD_CREATE_RETRIES_MAX | ||
839 | // Don't bother with retries; a failure is a failure | ||
840 | // | ||
841 | { | ||
842 | int rc = pthread_create( ref, &a, func, data); | ||
843 | if( rc) _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__ - 1); | ||
844 | } | ||
845 | # else | ||
846 | # error "This code deprecated" | ||
847 | /* | ||
848 | // Wait slightly if thread creation has exchausted the system | ||
849 | // | ||
850 | { uint_t retries; | ||
851 | for( retries=0; retries<THREAD_CREATE_RETRIES_MAX; retries++ ) { | ||
852 | |||
853 | int rc= pthread_create( ref, &a, func, data ); | ||
854 | // | ||
855 | // OS X / Linux: | ||
856 | // EAGAIN: ".. lacked the necessary resources to create | ||
857 | // another thread, or the system-imposed limit on the | ||
858 | // total number of threads in a process | ||
859 | // [PTHREAD_THREADS_MAX] would be exceeded." | ||
860 | // EINVAL: attr is invalid | ||
861 | // Linux: | ||
862 | // EPERM: no rights for given parameters or scheduling (no sudo) | ||
863 | // ENOMEM: (known to fail with this code, too - not listed in man) | ||
864 | |||
865 | if (rc==0) break; // ok! | ||
866 | |||
867 | // In practise, exhaustion seems to be coming from memory, not a | ||
868 | // maximum number of threads. Keep tuning... ;) | ||
869 | // | ||
870 | if (rc==EAGAIN) { | ||
871 | //fprintf( stderr, "Looping (retries=%d) ", retries ); // DEBUG | ||
872 | |||
873 | // Try again, later. | ||
874 | |||
875 | Yield(); | ||
876 | } else { | ||
877 | _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__ ); | ||
878 | } | ||
879 | } | ||
880 | } | ||
881 | */ | ||
882 | # endif | ||
883 | |||
884 | PT_CALL( pthread_attr_destroy( &a)); | ||
885 | } | ||
886 | |||
887 | |||
888 | void THREAD_SET_PRIORITY( int prio) | ||
889 | { | ||
890 | #ifdef PLATFORM_LINUX | ||
891 | if( sudo) // only root-privileged process can change priorities | ||
892 | #endif // PLATFORM_LINUX | ||
893 | { | ||
894 | struct sched_param sp; | ||
895 | // prio range [-3,+3] was checked by the caller | ||
896 | sp.sched_priority = gs_prio_remap[ prio + 3]; | ||
897 | PT_CALL( pthread_setschedparam( pthread_self(), _PRIO_MODE, &sp)); | ||
898 | } | ||
899 | } | ||
900 | |||
901 | void THREAD_SET_AFFINITY( unsigned int aff) | ||
902 | { | ||
903 | int bit = 0; | ||
904 | #ifdef __NetBSD__ | ||
905 | cpuset_t *cpuset = cpuset_create(); | ||
906 | if( cpuset == NULL) | ||
907 | _PT_FAIL( errno, "cpuset_create", __FILE__, __LINE__-2 ); | ||
908 | #define CPU_SET(b, s) cpuset_set(b, *(s)) | ||
909 | #else | ||
910 | cpu_set_t cpuset; | ||
911 | CPU_ZERO( &cpuset); | ||
912 | #endif | ||
913 | while( aff != 0) | ||
914 | { | ||
915 | if( aff & 1) | ||
916 | { | ||
917 | CPU_SET( bit, &cpuset); | ||
918 | } | ||
919 | ++ bit; | ||
920 | aff >>= 1; | ||
921 | } | ||
922 | #ifdef __ANDROID__ | ||
923 | PT_CALL( sched_setaffinity( pthread_self(), sizeof(cpu_set_t), &cpuset)); | ||
924 | #elif defined(__NetBSD__) | ||
925 | PT_CALL( pthread_setaffinity_np( pthread_self(), cpuset_size(cpuset), cpuset)); | ||
926 | cpuset_destroy( cpuset); | ||
927 | #else | ||
928 | PT_CALL( pthread_setaffinity_np( pthread_self(), sizeof(cpu_set_t), &cpuset)); | ||
929 | #endif | ||
930 | } | ||
931 | |||
932 | /* | ||
933 | * Wait for a thread to finish. | ||
934 | * | ||
935 | * 'mu_ref' is a lock we should use for the waiting; initially unlocked. | ||
936 | * Same lock as passed to THREAD_EXIT. | ||
937 | * | ||
938 | * Returns TRUE for successful wait, FALSE for timed out | ||
939 | */ | ||
940 | bool_t THREAD_WAIT( THREAD_T *ref, double secs , SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref) | ||
941 | { | ||
942 | struct timespec ts_store; | ||
943 | const struct timespec *timeout= NULL; | ||
944 | bool_t done; | ||
945 | |||
946 | // Do timeout counting before the locks | ||
947 | // | ||
948 | #if THREADWAIT_METHOD == THREADWAIT_TIMEOUT | ||
949 | if (secs>=0.0) | ||
950 | #else // THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
951 | if (secs>0.0) | ||
952 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
953 | { | ||
954 | prepare_timeout( &ts_store, now_secs()+secs ); | ||
955 | timeout= &ts_store; | ||
956 | } | ||
957 | |||
958 | #if THREADWAIT_METHOD == THREADWAIT_TIMEOUT | ||
959 | /* Thread is joinable | ||
960 | */ | ||
961 | if (!timeout) { | ||
962 | PT_CALL( pthread_join( *ref, NULL /*ignore exit value*/ )); | ||
963 | done= TRUE; | ||
964 | } else { | ||
965 | int rc= PTHREAD_TIMEDJOIN( *ref, NULL, timeout ); | ||
966 | if ((rc!=0) && (rc!=ETIMEDOUT)) { | ||
967 | _PT_FAIL( rc, "PTHREAD_TIMEDJOIN", __FILE__, __LINE__-2 ); | ||
968 | } | ||
969 | done= rc==0; | ||
970 | } | ||
971 | #else // THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
972 | /* Since we've set the thread up as PTHREAD_CREATE_DETACHED, we cannot | ||
973 | * join with it. Use the cond.var. | ||
974 | */ | ||
975 | (void) ref; // unused | ||
976 | MUTEX_LOCK( mu_ref ); | ||
977 | |||
978 | // 'secs'==0.0 does not need to wait, just take the current status | ||
979 | // within the 'mu_ref' locks | ||
980 | // | ||
981 | if (secs != 0.0) { | ||
982 | while( *st_ref < DONE ) { | ||
983 | if (!timeout) { | ||
984 | PT_CALL( pthread_cond_wait( signal_ref, mu_ref )); | ||
985 | } else { | ||
986 | int rc= pthread_cond_timedwait( signal_ref, mu_ref, timeout ); | ||
987 | if (rc==ETIMEDOUT) break; | ||
988 | if (rc!=0) _PT_FAIL( rc, "pthread_cond_timedwait", __FILE__, __LINE__-2 ); | ||
989 | } | ||
990 | } | ||
991 | } | ||
992 | done= *st_ref >= DONE; // DONE|ERROR_ST|CANCELLED | ||
993 | |||
994 | MUTEX_UNLOCK( mu_ref ); | ||
995 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
996 | return done; | ||
997 | } | ||
998 | // | ||
999 | void THREAD_KILL( THREAD_T *ref ) { | ||
1000 | #ifdef __ANDROID__ | ||
1001 | __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Cannot kill thread!"); | ||
1002 | #else | ||
1003 | pthread_cancel( *ref ); | ||
1004 | #endif | ||
1005 | } | ||
1006 | |||
1007 | void THREAD_MAKE_ASYNCH_CANCELLABLE() | ||
1008 | { | ||
1009 | #ifdef __ANDROID__ | ||
1010 | __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Cannot make thread async cancellable!"); | ||
1011 | #else | ||
1012 | // that's the default, but just in case... | ||
1013 | pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); | ||
1014 | // we want cancellation to take effect immediately if possible, instead of waiting for a cancellation point (which is the default) | ||
1015 | pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL); | ||
1016 | #endif | ||
1017 | } | ||
1018 | |||
1019 | void THREAD_SETNAME( char const* _name) | ||
1020 | { | ||
1021 | // exact API to set the thread name is platform-dependant | ||
1022 | // 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. | ||
1023 | #if defined PLATFORM_BSD && !defined __NetBSD__ | ||
1024 | pthread_set_name_np( pthread_self(), _name); | ||
1025 | #elif defined PLATFORM_BSD && defined __NetBSD__ | ||
1026 | pthread_setname_np( pthread_self(), "%s", (void *)_name); | ||
1027 | #elif defined PLATFORM_LINUX | ||
1028 | #if LINUX_USE_PTHREAD_SETNAME_NP | ||
1029 | pthread_setname_np( pthread_self(), _name); | ||
1030 | #else // LINUX_USE_PTHREAD_SETNAME_NP | ||
1031 | prctl(PR_SET_NAME, _name, 0, 0, 0); | ||
1032 | #endif // LINUX_USE_PTHREAD_SETNAME_NP | ||
1033 | #elif defined PLATFORM_QNX || defined PLATFORM_CYGWIN | ||
1034 | pthread_setname_np( pthread_self(), _name); | ||
1035 | #elif defined PLATFORM_OSX | ||
1036 | pthread_setname_np(_name); | ||
1037 | #elif defined PLATFORM_WIN32 || defined PLATFORM_POCKETPC | ||
1038 | PT_CALL( pthread_setname_np( pthread_self(), _name)); | ||
1039 | #endif | ||
1040 | } | ||
1041 | #endif // THREADAPI == THREADAPI_PTHREAD | ||