diff options
Diffstat (limited to 'src/threading.cpp')
-rw-r--r-- | src/threading.cpp | 887 |
1 files changed, 147 insertions, 740 deletions
diff --git a/src/threading.cpp b/src/threading.cpp index afeb184..259693a 100644 --- a/src/threading.cpp +++ b/src/threading.cpp | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * THREADING.C Copyright (c) 2007-08, Asko Kauppi | 2 | * THREADING.CPP Copyright (c) 2007-08, Asko Kauppi |
3 | * Copyright (C) 2009-19, Benoit Germain | 3 | * Copyright (C) 2009-24, Benoit Germain |
4 | * | 4 | * |
5 | * Lua Lanes OS threading specific code. | 5 | * Lua Lanes OS threading specific code. |
6 | * | 6 | * |
@@ -12,7 +12,7 @@ | |||
12 | =============================================================================== | 12 | =============================================================================== |
13 | 13 | ||
14 | Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> | 14 | Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> |
15 | Copyright (C) 2009-14, Benoit Germain <bnt.germain@gmail.com> | 15 | Copyright (C) 2009-24, Benoit Germain <bnt.germain@gmail.com> |
16 | 16 | ||
17 | Permission is hereby granted, free of charge, to any person obtaining a copy | 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 | 18 | of this software and associated documentation files (the "Software"), to deal |
@@ -47,12 +47,6 @@ THE SOFTWARE. | |||
47 | 47 | ||
48 | #endif // __linux__ | 48 | #endif // __linux__ |
49 | 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" | 50 | #include "threading.h" |
57 | 51 | ||
58 | #if !defined( PLATFORM_XBOX) && !defined( PLATFORM_WIN32) && !defined( PLATFORM_POCKETPC) | 52 | #if !defined( PLATFORM_XBOX) && !defined( PLATFORM_WIN32) && !defined( PLATFORM_POCKETPC) |
@@ -65,12 +59,6 @@ THE SOFTWARE. | |||
65 | # include <unistd.h> | 59 | # include <unistd.h> |
66 | #endif | 60 | #endif |
67 | 61 | ||
68 | /* Linux needs to check, whether it's been run as root | ||
69 | */ | ||
70 | #ifdef PLATFORM_LINUX | ||
71 | volatile bool sudo; | ||
72 | #endif | ||
73 | |||
74 | #ifdef PLATFORM_OSX | 62 | #ifdef PLATFORM_OSX |
75 | # include "threading_osx.h" | 63 | # include "threading_osx.h" |
76 | #endif | 64 | #endif |
@@ -93,205 +81,34 @@ THE SOFTWARE. | |||
93 | # pragma warning( disable : 4054 ) | 81 | # pragma warning( disable : 4054 ) |
94 | #endif | 82 | #endif |
95 | 83 | ||
96 | //#define THREAD_CREATE_RETRIES_MAX 20 | ||
97 | // loops (maybe retry forever?) | ||
98 | |||
99 | /* | 84 | /* |
100 | * FAIL is for unexpected API return values - essentially programming | 85 | * FAIL is for unexpected API return values - essentially programming |
101 | * error in _this_ code. | 86 | * error in _this_ code. |
102 | */ | 87 | */ |
103 | #if defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC) | 88 | #if defined(PLATFORM_XBOX) || defined(PLATFORM_WIN32) || defined(PLATFORM_POCKETPC) |
104 | static void FAIL( char const* funcname, int rc) | 89 | static void FAIL(char const* funcname, int rc) |
105 | { | 90 | { |
106 | #if defined( PLATFORM_XBOX) | 91 | #if defined(PLATFORM_XBOX) |
107 | fprintf( stderr, "%s() failed! (%d)\n", funcname, rc ); | 92 | fprintf(stderr, "%s() failed! (%d)\n", funcname, rc); |
108 | #else // PLATFORM_XBOX | 93 | #else // PLATFORM_XBOX |
109 | char buf[256]; | 94 | char buf[256]; |
110 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, nullptr); | 95 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, nullptr); |
111 | fprintf( stderr, "%s() failed! [GetLastError() -> %d] '%s'", funcname, rc, buf); | 96 | fprintf(stderr, "%s() failed! [GetLastError() -> %d] '%s'", funcname, rc, buf); |
112 | #endif // PLATFORM_XBOX | 97 | #endif // PLATFORM_XBOX |
113 | #ifdef _MSC_VER | 98 | #ifdef _MSC_VER |
114 | __debugbreak(); // give a chance to the debugger! | 99 | __debugbreak(); // give a chance to the debugger! |
115 | #endif // _MSC_VER | 100 | #endif // _MSC_VER |
116 | abort(); | 101 | abort(); |
117 | } | 102 | } |
118 | #endif // win32 build | 103 | #endif // win32 build |
119 | 104 | ||
120 | 105 | ||
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, nullptr /*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 ===---*/ | 106 | /*---=== Threading ===---*/ |
230 | 107 | ||
231 | //--- | 108 | // ################################################################################################## |
232 | // It may be meaningful to explicitly limit the new threads' C stack size. | 109 | // ################################################################################################## |
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 | 110 | #if THREADAPI == THREADAPI_WINDOWS |
270 | 111 | ||
271 | #if _WIN32_WINNT < 0x0600 // CONDITION_VARIABLE aren't available | ||
272 | // | ||
273 | void MUTEX_INIT( MUTEX_T *ref ) { | ||
274 | *ref= CreateMutex( nullptr /*security attr*/, false /*not locked*/, nullptr ); | ||
275 | if (!ref) FAIL( "CreateMutex", GetLastError() ); | ||
276 | } | ||
277 | void MUTEX_FREE( MUTEX_T *ref ) { | ||
278 | if (!CloseHandle(*ref)) FAIL( "CloseHandle (mutex)", GetLastError() ); | ||
279 | *ref= nullptr; | ||
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[] = | 112 | static int const gs_prio_remap[] = |
296 | { | 113 | { |
297 | THREAD_PRIORITY_IDLE, | 114 | THREAD_PRIORITY_IDLE, |
@@ -303,348 +120,120 @@ static int const gs_prio_remap[] = | |||
303 | THREAD_PRIORITY_TIME_CRITICAL | 120 | THREAD_PRIORITY_TIME_CRITICAL |
304 | }; | 121 | }; |
305 | 122 | ||
306 | /* MSDN: "If you would like to use the CRT in ThreadProc, use the | 123 | // ############################################################################################### |
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(nullptr, // security | ||
314 | _THREAD_STACK_SIZE, | ||
315 | func, | ||
316 | data, | ||
317 | 0, // flags (0/CREATE_SUSPENDED) | ||
318 | nullptr // thread id (not used) | ||
319 | ); | ||
320 | 124 | ||
321 | if (h == nullptr) // _beginthreadex returns 0L on failure instead of -1L (like _beginthread) | 125 | void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) |
322 | { | 126 | { |
323 | FAIL( "CreateThread", GetLastError()); | 127 | // prio range [-3,+3] was checked by the caller |
324 | } | 128 | if (!SetThreadPriority(GetCurrentThread(), gs_prio_remap[prio_ + 3])) |
325 | |||
326 | if (prio != THREAD_PRIO_DEFAULT) | ||
327 | { | 129 | { |
328 | if (!SetThreadPriority( h, gs_prio_remap[prio + 3])) | 130 | FAIL("THREAD_SET_PRIORITY", GetLastError()); |
329 | { | ||
330 | FAIL( "SetThreadPriority", GetLastError()); | ||
331 | } | ||
332 | } | 131 | } |
333 | |||
334 | *ref = h; | ||
335 | } | 132 | } |
336 | 133 | ||
134 | // ############################################################################################### | ||
337 | 135 | ||
338 | void THREAD_SET_PRIORITY( int prio) | 136 | void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_, [[maybe_unused]] bool sudo_) |
339 | { | 137 | { |
340 | // prio range [-3,+3] was checked by the caller | 138 | // prio range [-3,+3] was checked by the caller |
341 | if (!SetThreadPriority( GetCurrentThread(), gs_prio_remap[prio + 3])) | 139 | if (!SetThreadPriority(thread_.native_handle(), gs_prio_remap[prio_ + 3])) |
342 | { | 140 | { |
343 | FAIL( "THREAD_SET_PRIORITY", GetLastError()); | 141 | FAIL("JTHREAD_SET_PRIORITY", GetLastError()); |
344 | } | 142 | } |
345 | } | 143 | } |
346 | 144 | ||
347 | void THREAD_SET_AFFINITY( unsigned int aff) | 145 | // ############################################################################################### |
348 | { | ||
349 | if( !SetThreadAffinityMask( GetCurrentThread(), aff)) | ||
350 | { | ||
351 | FAIL( "THREAD_SET_AFFINITY", GetLastError()); | ||
352 | } | ||
353 | } | ||
354 | 146 | ||
355 | bool THREAD_WAIT_IMPL( THREAD_T *ref, double secs) | 147 | void THREAD_SET_AFFINITY(unsigned int aff) |
356 | { | 148 | { |
357 | DWORD ms = (secs<0.0) ? INFINITE : (DWORD)((secs*1000.0)+0.5); | 149 | if (!SetThreadAffinityMask(GetCurrentThread(), aff)) |
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 = nullptr; // thread no longer usable | ||
369 | return true; | ||
370 | } | ||
371 | // | ||
372 | void THREAD_KILL( THREAD_T *ref ) | ||
373 | { | 150 | { |
374 | // nonexistent on Xbox360, simply disable until a better solution is found | 151 | FAIL("THREAD_SET_AFFINITY", GetLastError()); |
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 = nullptr; | ||
380 | } | 152 | } |
153 | } | ||
381 | 154 | ||
382 | void THREAD_MAKE_ASYNCH_CANCELLABLE() {} // nothing to do for windows threads, we can cancel them anytime we want | 155 | // ############################################################################################### |
383 | 156 | ||
384 | #if !defined __GNUC__ | 157 | #if !defined __GNUC__ |
385 | //see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx | 158 | //see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx |
386 | #define MS_VC_EXCEPTION 0x406D1388 | 159 | #define MS_VC_EXCEPTION 0x406D1388 |
387 | #pragma pack(push,8) | 160 | #pragma pack(push,8) |
388 | typedef struct tagTHREADNAME_INFO | 161 | typedef struct tagTHREADNAME_INFO |
389 | { | 162 | { |
390 | DWORD dwType; // Must be 0x1000. | 163 | DWORD dwType; // Must be 0x1000. |
391 | LPCSTR szName; // Pointer to name (in user addr space). | 164 | LPCSTR szName; // Pointer to name (in user addr space). |
392 | DWORD dwThreadID; // Thread ID (-1=caller thread). | 165 | DWORD dwThreadID; // Thread ID (-1=caller thread). |
393 | DWORD dwFlags; // Reserved for future use, must be zero. | 166 | DWORD dwFlags; // Reserved for future use, must be zero. |
394 | } THREADNAME_INFO; | 167 | } THREADNAME_INFO; |
395 | #pragma pack(pop) | 168 | #pragma pack(pop) |
396 | #endif // !__GNUC__ | 169 | #endif // !__GNUC__ |
397 | 170 | ||
398 | void THREAD_SETNAME( char const* _name) | 171 | void THREAD_SETNAME(char const* _name) |
399 | { | 172 | { |
400 | #if !defined __GNUC__ | 173 | #if !defined __GNUC__ |
401 | THREADNAME_INFO info; | 174 | THREADNAME_INFO info; |
402 | info.dwType = 0x1000; | 175 | info.dwType = 0x1000; |
403 | info.szName = _name; | 176 | info.szName = _name; |
404 | info.dwThreadID = GetCurrentThreadId(); | 177 | info.dwThreadID = GetCurrentThreadId(); |
405 | info.dwFlags = 0; | 178 | 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 | 179 | ||
419 | void SIGNAL_INIT( SIGNAL_T* ref) | 180 | __try |
420 | { | 181 | { |
421 | InitializeCriticalSection( &ref->signalCS); | 182 | RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); |
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 | } | 183 | } |
429 | 184 | __except(EXCEPTION_EXECUTE_HANDLER) | |
430 | void SIGNAL_FREE( SIGNAL_T* ref) | ||
431 | { | 185 | { |
432 | CloseHandle( ref->waitDoneEvent); | ||
433 | CloseHandle( ref->waitEvent); | ||
434 | DeleteCriticalSection( &ref->countCS); | ||
435 | DeleteCriticalSection( &ref->signalCS); | ||
436 | } | 186 | } |
187 | #endif // !__GNUC__ | ||
188 | } | ||
437 | 189 | ||
438 | bool SIGNAL_WAIT( SIGNAL_T* ref, MUTEX_T* mu_ref, time_d abs_secs) | 190 | // ################################################################################################## |
439 | { | 191 | // ################################################################################################## |
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 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 | 192 | #else // THREADAPI == THREADAPI_PTHREAD |
570 | // PThread (Linux, OS X, ...) | 193 | // ################################################################################################## |
571 | // | 194 | // ################################################################################################## |
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 | 195 | ||
578 | # if (defined(__MINGW32__) || defined(__MINGW64__)) && defined pthread_attr_setschedpolicy | 196 | // PThread (Linux, OS X, ...) |
579 | # if pthread_attr_setschedpolicy( A, S) == ENOTSUP | 197 | // |
580 | // from the mingw-w64 team: | 198 | // On OS X, user processes seem to be able to change priorities. |
581 | // Well, we support pthread_setschedparam by which you can specify | 199 | // On Linux, SCHED_RR and su privileges are required.. !-( |
582 | // threading-policy. Nevertheless, yes we lack this function. In | 200 | // |
583 | // general its implementation is pretty much trivial, as on Win32 target | 201 | #include <errno.h> |
584 | // just SCHED_OTHER can be supported. | 202 | #include <sched.h> |
585 | #undef pthread_attr_setschedpolicy | 203 | |
586 | static int pthread_attr_setschedpolicy( pthread_attr_t* attr, int policy) | 204 | #if (defined(__MINGW32__) || defined(__MINGW64__)) && defined pthread_attr_setschedpolicy |
205 | #if pthread_attr_setschedpolicy(A, S) == ENOTSUP | ||
206 | // from the mingw-w64 team: | ||
207 | // Well, we support pthread_setschedparam by which you can specify | ||
208 | // threading-policy. Nevertheless, yes we lack this function. In | ||
209 | // general its implementation is pretty much trivial, as on Win32 target | ||
210 | // just SCHED_OTHER can be supported. | ||
211 | #undef pthread_attr_setschedpolicy | ||
212 | [[nodiscard]] static int pthread_attr_setschedpolicy(pthread_attr_t* attr, int policy) | ||
213 | { | ||
214 | if (policy != SCHED_OTHER) | ||
587 | { | 215 | { |
588 | if( policy != SCHED_OTHER) | 216 | return ENOTSUP; |
589 | { | ||
590 | return ENOTSUP; | ||
591 | } | ||
592 | return 0; | ||
593 | } | 217 | } |
594 | # endif // pthread_attr_setschedpolicy() | 218 | return 0; |
595 | # endif // defined(__MINGW32__) || defined(__MINGW64__) | 219 | } |
220 | #endif // pthread_attr_setschedpolicy() | ||
221 | #endif // defined(__MINGW32__) || defined(__MINGW64__) | ||
596 | 222 | ||
597 | static void _PT_FAIL( int rc, const char *name, const char *file, int line ) { | 223 | static void _PT_FAIL( int rc, const char *name, const char *file, int line ) |
224 | { | ||
598 | const char *why= (rc==EINVAL) ? "EINVAL" : | 225 | const char *why= (rc==EINVAL) ? "EINVAL" : |
599 | (rc==EBUSY) ? "EBUSY" : | 226 | (rc==EBUSY) ? "EBUSY" : |
600 | (rc==EPERM) ? "EPERM" : | 227 | (rc==EPERM) ? "EPERM" : |
601 | (rc==ENOMEM) ? "ENOMEM" : | 228 | (rc==ENOMEM) ? "ENOMEM" : |
602 | (rc==ESRCH) ? "ESRCH" : | 229 | (rc==ESRCH) ? "ESRCH" : |
603 | (rc==ENOTSUP) ? "ENOTSUP": | 230 | (rc==ENOTSUP) ? "ENOTSUP": |
604 | //... | 231 | //... |
605 | "<UNKNOWN>"; | 232 | "<UNKNOWN>"; |
606 | 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 ); |
607 | abort(); | 234 | abort(); |
608 | } | 235 | } |
609 | #define PT_CALL( call ) { int rc= call; if (rc!=0) _PT_FAIL( rc, #call, __FILE__, __LINE__ ); } | 236 | #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, nullptr /*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 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 | 237 | ||
649 | // array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range | 238 | // 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[] = | 239 | static int const gs_prio_remap[] = |
@@ -747,14 +336,6 @@ static int const gs_prio_remap[] = | |||
747 | // | 336 | // |
748 | // TBD: Find right values for Cygwin | 337 | // TBD: Find right values for Cygwin |
749 | // | 338 | // |
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 | 339 | # else |
759 | # error "Unknown OS: not implemented!" | 340 | # error "Unknown OS: not implemented!" |
760 | # endif | 341 | # endif |
@@ -767,7 +348,7 @@ static int const gs_prio_remap[] = | |||
767 | #endif // _PRIO_0 | 348 | #endif // _PRIO_0 |
768 | }; | 349 | }; |
769 | 350 | ||
770 | static int select_prio(int prio /* -3..+3 */) | 351 | [[nodiscard]] static int select_prio(int prio /* -3..+3 */) |
771 | { | 352 | { |
772 | if (prio == THREAD_PRIO_DEFAULT) | 353 | if (prio == THREAD_PRIO_DEFAULT) |
773 | prio = 0; | 354 | prio = 0; |
@@ -775,267 +356,93 @@ static int select_prio(int prio /* -3..+3 */) | |||
775 | return gs_prio_remap[prio + 3]; | 356 | return gs_prio_remap[prio + 3]; |
776 | } | 357 | } |
777 | 358 | ||
778 | void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void*), void* data, int prio /* -3..+3 */) | 359 | void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) |
779 | { | 360 | { |
780 | pthread_attr_t a; | ||
781 | bool const change_priority = | ||
782 | #ifdef PLATFORM_LINUX | 361 | #ifdef PLATFORM_LINUX |
783 | sudo && // only root-privileged process can change priorities | 362 | if (!sudo_) // only root-privileged process can change priorities |
784 | #endif | 363 | return; |
785 | (prio != THREAD_PRIO_DEFAULT); | 364 | #endif // PLATFORM_LINUX |
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 | { int 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 | 365 | ||
884 | PT_CALL( pthread_attr_destroy( &a)); | 366 | struct sched_param sp; |
367 | // prio range [-3,+3] was checked by the caller | ||
368 | sp.sched_priority = gs_prio_remap[prio_ + 3]; | ||
369 | PT_CALL(pthread_setschedparam(pthread_self(), _PRIO_MODE, &sp)); | ||
885 | } | 370 | } |
886 | 371 | ||
372 | // ################################################################################################# | ||
887 | 373 | ||
888 | void THREAD_SET_PRIORITY( int prio) | 374 | void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_, [[maybe_unused]] bool sudo_) |
889 | { | 375 | { |
890 | #ifdef PLATFORM_LINUX | 376 | #ifdef PLATFORM_LINUX |
891 | if( sudo) // only root-privileged process can change priorities | 377 | if (!sudo_) // only root-privileged process can change priorities |
378 | return; | ||
892 | #endif // PLATFORM_LINUX | 379 | #endif // PLATFORM_LINUX |
893 | { | 380 | |
894 | struct sched_param sp; | 381 | struct sched_param sp; |
895 | // prio range [-3,+3] was checked by the caller | 382 | // prio range [-3,+3] was checked by the caller |
896 | sp.sched_priority = gs_prio_remap[ prio + 3]; | 383 | sp.sched_priority = gs_prio_remap[prio_ + 3]; |
897 | PT_CALL( pthread_setschedparam( pthread_self(), _PRIO_MODE, &sp)); | 384 | PT_CALL(pthread_setschedparam(static_cast<pthread_t>(thread_.native_handle()), _PRIO_MODE, &sp)); |
898 | } | ||
899 | } | 385 | } |
900 | 386 | ||
901 | void THREAD_SET_AFFINITY( unsigned int aff) | 387 | // ################################################################################################# |
388 | |||
389 | void THREAD_SET_AFFINITY(unsigned int aff) | ||
902 | { | 390 | { |
903 | int bit = 0; | 391 | int bit = 0; |
904 | #ifdef __NetBSD__ | 392 | #ifdef __NetBSD__ |
905 | cpuset_t *cpuset = cpuset_create(); | 393 | cpuset_t* cpuset = cpuset_create(); |
906 | if (cpuset == nullptr) | 394 | if (cpuset == nullptr) |
907 | _PT_FAIL( errno, "cpuset_create", __FILE__, __LINE__-2 ); | 395 | _PT_FAIL(errno, "cpuset_create", __FILE__, __LINE__ - 2); |
908 | #define CPU_SET(b, s) cpuset_set(b, *(s)) | 396 | #define CPU_SET(b, s) cpuset_set(b, *(s)) |
909 | #else | 397 | #else |
910 | cpu_set_t cpuset; | 398 | cpu_set_t cpuset; |
911 | CPU_ZERO( &cpuset); | 399 | CPU_ZERO(&cpuset); |
912 | #endif | 400 | #endif |
913 | while( aff != 0) | 401 | while (aff != 0) |
914 | { | 402 | { |
915 | if( aff & 1) | 403 | if (aff & 1) |
916 | { | 404 | { |
917 | CPU_SET( bit, &cpuset); | 405 | CPU_SET(bit, &cpuset); |
918 | } | 406 | } |
919 | ++ bit; | 407 | ++bit; |
920 | aff >>= 1; | 408 | aff >>= 1; |
921 | } | 409 | } |
922 | #ifdef __ANDROID__ | 410 | #ifdef __ANDROID__ |
923 | PT_CALL( sched_setaffinity( pthread_self(), sizeof(cpu_set_t), &cpuset)); | 411 | PT_CALL(sched_setaffinity(pthread_self(), sizeof(cpu_set_t), &cpuset)); |
924 | #elif defined(__NetBSD__) | 412 | #elif defined(__NetBSD__) |
925 | PT_CALL( pthread_setaffinity_np( pthread_self(), cpuset_size(cpuset), cpuset)); | 413 | PT_CALL(pthread_setaffinity_np(pthread_self(), cpuset_size(cpuset), cpuset)); |
926 | cpuset_destroy( cpuset); | 414 | cpuset_destroy(cpuset); |
927 | #else | 415 | #else |
928 | PT_CALL( pthread_setaffinity_np( pthread_self(), sizeof(cpu_set_t), &cpuset)); | 416 | PT_CALL(pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset)); |
929 | #endif | 417 | #endif |
930 | } | 418 | } |
931 | 419 | ||
932 | /* | 420 | // ################################################################################################# |
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 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 = nullptr; | ||
944 | bool 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 | 421 | ||
958 | #if THREADWAIT_METHOD == THREADWAIT_TIMEOUT | 422 | void THREAD_SETNAME(char const* _name) |
959 | /* Thread is joinable | 423 | { |
960 | */ | 424 | // exact API to set the thread name is platform-dependant |
961 | if (!timeout) { | 425 | // 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. |
962 | PT_CALL(pthread_join(*ref, nullptr /*ignore exit value*/)); | ||
963 | done = true; | ||
964 | } else { | ||
965 | int rc = PTHREAD_TIMEDJOIN(*ref, nullptr, 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, nullptr); | ||
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, nullptr); | ||
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__ | 426 | #if defined PLATFORM_BSD && !defined __NetBSD__ |
1024 | pthread_set_name_np( pthread_self(), _name); | 427 | pthread_set_name_np(pthread_self(), _name); |
1025 | #elif defined PLATFORM_BSD && defined __NetBSD__ | 428 | #elif defined PLATFORM_BSD && defined __NetBSD__ |
1026 | pthread_setname_np( pthread_self(), "%s", (void *)_name); | 429 | pthread_setname_np(pthread_self(), "%s", (void*) _name); |
1027 | #elif defined PLATFORM_LINUX | 430 | #elif defined PLATFORM_LINUX |
1028 | #if LINUX_USE_PTHREAD_SETNAME_NP | 431 | #if LINUX_USE_PTHREAD_SETNAME_NP |
1029 | pthread_setname_np( pthread_self(), _name); | 432 | pthread_setname_np(pthread_self(), _name); |
1030 | #else // LINUX_USE_PTHREAD_SETNAME_NP | 433 | #else // LINUX_USE_PTHREAD_SETNAME_NP |
1031 | prctl(PR_SET_NAME, _name, 0, 0, 0); | 434 | prctl(PR_SET_NAME, _name, 0, 0, 0); |
1032 | #endif // LINUX_USE_PTHREAD_SETNAME_NP | 435 | #endif // LINUX_USE_PTHREAD_SETNAME_NP |
1033 | #elif defined PLATFORM_QNX || defined PLATFORM_CYGWIN | 436 | #elif defined PLATFORM_QNX || defined PLATFORM_CYGWIN |
1034 | pthread_setname_np( pthread_self(), _name); | 437 | pthread_setname_np(pthread_self(), _name); |
1035 | #elif defined PLATFORM_OSX | 438 | #elif defined PLATFORM_OSX |
1036 | pthread_setname_np(_name); | 439 | pthread_setname_np(_name); |
1037 | #elif defined PLATFORM_WIN32 || defined PLATFORM_POCKETPC | 440 | #else |
1038 | PT_CALL( pthread_setname_np( pthread_self(), _name)); | 441 | fprintf(stderr, "THREAD_SETNAME: unsupported platform\n"); |
442 | abort(); | ||
1039 | #endif | 443 | #endif |
1040 | } | 444 | } |
445 | |||
1041 | #endif // THREADAPI == THREADAPI_PTHREAD | 446 | #endif // THREADAPI == THREADAPI_PTHREAD |
447 | // ################################################################################################# | ||
448 | // ################################################################################################# | ||