aboutsummaryrefslogtreecommitdiff
path: root/src/threading.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/threading.cpp')
-rw-r--r--src/threading.cpp887
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
14Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> 14Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com>
15Copyright (C) 2009-14, Benoit Germain <bnt.germain@gmail.com> 15Copyright (C) 2009-24, Benoit Germain <bnt.germain@gmail.com>
16 16
17Permission is hereby granted, free of charge, to any person obtaining a copy 17Permission is hereby granted, free of charge, to any person obtaining a copy
18of this software and associated documentation files (the "Software"), to deal 18of 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)
104static 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*/
127time_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*/
201time_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*/
211static 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
295static int const gs_prio_remap[] = 112static 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)."
308MSDN: "you can create at most 2028 threads"
309*/
310// Note: Visual C++ requires '__stdcall' where it is
311void 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) 125void 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
338void THREAD_SET_PRIORITY( int prio) 136void 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
347void THREAD_SET_AFFINITY( unsigned int aff) 145// ###############################################################################################
348{
349 if( !SetThreadAffinityMask( GetCurrentThread(), aff))
350 {
351 FAIL( "THREAD_SET_AFFINITY", GetLastError());
352 }
353}
354 146
355bool THREAD_WAIT_IMPL( THREAD_T *ref, double secs) 147void 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 161typedef 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) 171void 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 ) { 223static 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
650static int const gs_prio_remap[] = 239static 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
770static 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
778void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void*), void* data, int prio /* -3..+3 */) 359void 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
888void THREAD_SET_PRIORITY( int prio) 374void 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
901void THREAD_SET_AFFINITY( unsigned int aff) 387// #################################################################################################
388
389void 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 */
940bool 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 422void 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// #################################################################################################