aboutsummaryrefslogtreecommitdiff
path: root/src/threading.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/threading.cpp')
-rw-r--r--src/threading.cpp1041
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
14Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com>
15Copyright (C) 2009-14, Benoit Germain <bnt.germain@gmail.com>
16
17Permission is hereby granted, free of charge, to any person obtaining a copy
18of this software and associated documentation files (the "Software"), to deal
19in the Software without restriction, including without limitation the rights
20to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21copies of the Software, and to permit persons to whom the Software is
22furnished to do so, subject to the following conditions:
23
24The above copyright notice and this permission notice shall be included in
25all copies or substantial portions of the Software.
26
27THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
33THE 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)
104static 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*/
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, 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*/
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 ===---*/
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
295static 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)."
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( 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
338void 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
347void THREAD_SET_AFFINITY( unsigned int aff)
348{
349 if( !SetThreadAffinityMask( GetCurrentThread(), aff))
350 {
351 FAIL( "THREAD_SET_AFFINITY", GetLastError());
352 }
353}
354
355bool_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
650static 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
770static 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
778void 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
888void 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
901void 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 */
940bool_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