aboutsummaryrefslogtreecommitdiff
path: root/src/threading.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/threading.c')
-rw-r--r--src/threading.c721
1 files changed, 721 insertions, 0 deletions
diff --git a/src/threading.c b/src/threading.c
new file mode 100644
index 0000000..68d1e41
--- /dev/null
+++ b/src/threading.c
@@ -0,0 +1,721 @@
1/*
2 * THREADING.C Copyright (c) 2007-08, Asko Kauppi
3 *
4 * Lua Lanes OS threading specific code.
5 *
6 * References:
7 * <http://www.cse.wustl.edu/~schmidt/win32-cv-1.html>
8*/
9
10/*
11===============================================================================
12
13Copyright (C) 2007-08 Asko Kauppi <akauppi@gmail.com>
14
15Permission is hereby granted, free of charge, to any person obtaining a copy
16of this software and associated documentation files (the "Software"), to deal
17in the Software without restriction, including without limitation the rights
18to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19copies of the Software, and to permit persons to whom the Software is
20furnished to do so, subject to the following conditions:
21
22The above copyright notice and this permission notice shall be included in
23all copies or substantial portions of the Software.
24
25THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31THE SOFTWARE.
32
33===============================================================================
34*/
35#include <stdio.h>
36#include <stdlib.h>
37#include <assert.h>
38#include <errno.h>
39#include <math.h>
40
41#include "threading.h"
42#include "lua.h"
43
44#if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC))
45# include <sys/time.h>
46#endif
47
48
49#if defined(PLATFORM_LINUX) || defined(PLATFORM_CYGWIN)
50# include <sys/types.h>
51# include <unistd.h>
52#endif
53
54/* Linux needs to check, whether it's been run as root
55*/
56#ifdef PLATFORM_LINUX
57 volatile bool_t sudo;
58#endif
59
60#ifdef _MSC_VER
61// ".. selected for automatic inline expansion" (/O2 option)
62# pragma warning( disable : 4711 )
63// ".. type cast from function pointer ... to data pointer"
64# pragma warning( disable : 4054 )
65#endif
66
67//#define THREAD_CREATE_RETRIES_MAX 20
68 // loops (maybe retry forever?)
69
70/*
71* FAIL is for unexpected API return values - essentially programming
72* error in _this_ code.
73*/
74#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
75static void FAIL( const char *funcname, int rc ) {
76 fprintf( stderr, "%s() failed! (%d)\n", funcname, rc );
77 abort();
78}
79#endif
80
81
82/*
83* Returns millisecond timing (in seconds) for the current time.
84*
85* Note: This function should be called once in single-threaded mode in Win32,
86* to get it initialized.
87*/
88time_d now_secs(void) {
89
90#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
91 /*
92 * Windows FILETIME values are "100-nanosecond intervals since
93 * January 1, 1601 (UTC)" (MSDN). Well, we'd want Unix Epoch as
94 * the offset and it seems, so would they:
95 *
96 * <http://msdn.microsoft.com/en-us/library/ms724928(VS.85).aspx>
97 */
98 SYSTEMTIME st;
99 FILETIME ft;
100 ULARGE_INTEGER uli;
101 static ULARGE_INTEGER uli_epoch; // Jan 1st 1970 0:0:0
102
103 if (uli_epoch.HighPart==0) {
104 st.wYear= 1970;
105 st.wMonth= 1; // Jan
106 st.wDay= 1;
107 st.wHour= st.wMinute= st.wSecond= st.wMilliseconds= 0;
108
109 if (!SystemTimeToFileTime( &st, &ft ))
110 FAIL( "SystemTimeToFileTime", GetLastError() );
111
112 uli_epoch.LowPart= ft.dwLowDateTime;
113 uli_epoch.HighPart= ft.dwHighDateTime;
114 }
115
116 GetSystemTime( &st ); // current system date/time in UTC
117 if (!SystemTimeToFileTime( &st, &ft ))
118 FAIL( "SystemTimeToFileTime", GetLastError() );
119
120 uli.LowPart= ft.dwLowDateTime;
121 uli.HighPart= ft.dwHighDateTime;
122
123 /* 'double' has less accuracy than 64-bit int, but if it were to degrade,
124 * it would do so gracefully. In practise, the integer accuracy is not
125 * of the 100ns class but just 1ms (Windows XP).
126 */
127# if 1
128 // >= 2.0.3 code
129 return (double) ((uli.QuadPart - uli_epoch.QuadPart)/10000) / 1000.0;
130# elif 0
131 // fix from Kriss Daniels, see:
132 // <http://luaforge.net/forum/forum.php?thread_id=22704&forum_id=1781>
133 //
134 // "seem to be getting negative numbers from the old version, probably number
135 // conversion clipping, this fixes it and maintains ms resolution"
136 //
137 // This was a bad fix, and caused timer test 5 sec timers to disappear.
138 // --AKa 25-Jan-2009
139 //
140 return ((double)((signed)((uli.QuadPart/10000) - (uli_epoch.QuadPart/10000)))) / 1000.0;
141# else
142 // <= 2.0.2 code
143 return (double)(uli.QuadPart - uli_epoch.QuadPart) / 10000000.0;
144# endif
145#else
146 struct timeval tv;
147 // {
148 // time_t tv_sec; /* seconds since Jan. 1, 1970 */
149 // suseconds_t tv_usec; /* and microseconds */
150 // };
151
152 int rc= gettimeofday( &tv, NULL /*time zone not used any more (in Linux)*/ );
153 assert( rc==0 );
154
155 return ((double)tv.tv_sec) + ((tv.tv_usec)/1000) / 1000.0;
156#endif
157}
158
159
160/*
161*/
162time_d SIGNAL_TIMEOUT_PREPARE( double secs ) {
163 if (secs<=0.0) return secs;
164 else return now_secs() + secs;
165}
166
167
168#if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC))
169/*
170* Prepare 'abs_secs' kind of timeout to 'timespec' format
171*/
172static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
173 assert(ts);
174 assert( abs_secs >= 0.0 );
175
176 if (abs_secs==0.0)
177 abs_secs= now_secs();
178
179 ts->tv_sec= floor( abs_secs );
180 ts->tv_nsec= ((long)((abs_secs - ts->tv_sec) * 1000.0 +0.5)) * 1000000UL; // 1ms = 1000000ns
181}
182#endif
183
184
185/*---=== Threading ===---*/
186
187//---
188// It may be meaningful to explicitly limit the new threads' C stack size.
189// We should know how much Lua needs in the C stack, all Lua side allocations
190// are done in heap so they don't count.
191//
192// Consequence of _not_ limiting the stack is running out of virtual memory
193// with 1000-5000 threads on 32-bit systems.
194//
195// Note: using external C modules may be affected by the stack size check.
196// if having problems, set back to '0' (default stack size of the system).
197//
198// Win32: 64K (?)
199// Win64: xxx
200//
201// Linux x86: 2MB Ubuntu 7.04 via 'pthread_getstacksize()'
202// Linux x64: xxx
203// Linux ARM: xxx
204//
205// OS X 10.4.9: 512K <http://developer.apple.com/qa/qa2005/qa1419.html>
206// valid values N * 4KB
207//
208#ifndef _THREAD_STACK_SIZE
209# if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PLATFORM_CYGWIN)
210# define _THREAD_STACK_SIZE 0
211 // Win32: does it work with less?
212# elif (defined PLATFORM_OSX)
213# define _THREAD_STACK_SIZE (524288/2) // 262144
214 // OS X: "make test" works on 65536 and even below
215 // "make perftest" works on >= 4*65536 == 262144 (not 3*65536)
216# elif (defined PLATFORM_LINUX) && (defined __i386)
217# define _THREAD_STACK_SIZE (2097152/16) // 131072
218 // Linux x86 (Ubuntu 7.04): "make perftest" works on /16 (not on /32)
219# elif (defined PLATFORM_BSD) && (defined __i386)
220# define _THREAD_STACK_SIZE (1048576/8) // 131072
221 // FreeBSD 6.2 SMP i386: ("gmake perftest" works on /8 (not on /16)
222# endif
223#endif
224
225#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
226 //
227 void MUTEX_INIT( MUTEX_T *ref ) {
228 *ref= CreateMutex( NULL /*security attr*/, FALSE /*not locked*/, NULL );
229 if (!ref) FAIL( "CreateMutex", GetLastError() );
230 }
231 void MUTEX_FREE( MUTEX_T *ref ) {
232 if (!CloseHandle(*ref)) FAIL( "CloseHandle (mutex)", GetLastError() );
233 *ref= NULL;
234 }
235 void MUTEX_LOCK( MUTEX_T *ref ) {
236 DWORD rc= WaitForSingleObject(*ref,INFINITE);
237 if (rc!=0) FAIL( "WaitForSingleObject", rc==WAIT_FAILED ? GetLastError() : rc );
238 }
239 void MUTEX_UNLOCK( MUTEX_T *ref ) {
240 if (!ReleaseMutex(*ref))
241 FAIL( "ReleaseMutex", GetLastError() );
242 }
243 /* MSDN: "If you would like to use the CRT in ThreadProc, use the
244 _beginthreadex function instead (of CreateThread)."
245 MSDN: "you can create at most 2028 threads"
246 */
247 void
248 THREAD_CREATE( THREAD_T *ref,
249 THREAD_RETURN_T (__stdcall *func)( void * ),
250 // Note: Visual C++ requires '__stdcall' where it is
251 void *data, int prio /* -3..+3 */ ) {
252
253 HANDLE h= (HANDLE)_beginthreadex( NULL, // security
254 _THREAD_STACK_SIZE,
255 func,
256 data,
257 0, // flags (0/CREATE_SUSPENDED)
258 NULL // thread id (not used)
259 );
260
261 if (h == INVALID_HANDLE_VALUE) FAIL( "CreateThread", GetLastError() );
262
263 if (prio!= 0) {
264 int win_prio= (prio == +3) ? THREAD_PRIORITY_TIME_CRITICAL :
265 (prio == +2) ? THREAD_PRIORITY_HIGHEST :
266 (prio == +1) ? THREAD_PRIORITY_ABOVE_NORMAL :
267 (prio == -1) ? THREAD_PRIORITY_BELOW_NORMAL :
268 (prio == -2) ? THREAD_PRIORITY_LOWEST :
269 THREAD_PRIORITY_IDLE; // -3
270
271 if (!SetThreadPriority( h, win_prio ))
272 FAIL( "SetThreadPriority", GetLastError() );
273 }
274 *ref= h;
275 }
276 //
277 bool_t THREAD_WAIT( THREAD_T *ref, double secs ) {
278 long ms= (long)((secs*1000.0)+0.5);
279
280 DWORD rc= WaitForSingleObject( *ref, ms<0 ? INFINITE:ms /*timeout*/ );
281 //
282 // (WAIT_ABANDONED)
283 // WAIT_OBJECT_0 success (0)
284 // WAIT_TIMEOUT
285 // WAIT_FAILED more info via GetLastError()
286
287 if (rc == WAIT_TIMEOUT) return FALSE;
288 if (rc != 0) FAIL( "WaitForSingleObject", rc );
289 *ref= NULL; // thread no longer usable
290 return TRUE;
291 }
292 //
293 void THREAD_KILL( THREAD_T *ref ) {
294 if (!TerminateThread( *ref, 0 )) FAIL("TerminateThread", GetLastError());
295 *ref= NULL;
296 }
297 //
298 void SIGNAL_INIT( SIGNAL_T *ref ) {
299 // 'manual reset' event type selected, to be able to wake up all the
300 // waiting threads.
301 //
302 HANDLE h= CreateEvent( NULL, // security attributes
303 TRUE, // TRUE: manual event
304 FALSE, // Initial state
305 NULL ); // name
306
307 if (h == NULL) FAIL( "CreateEvent", GetLastError() );
308 *ref= h;
309 }
310 void SIGNAL_FREE( SIGNAL_T *ref ) {
311 if (!CloseHandle(*ref)) FAIL( "CloseHandle (event)", GetLastError() );
312 *ref= NULL;
313 }
314 //
315 bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu_ref, time_d abs_secs ) {
316 DWORD rc;
317 long ms;
318
319 if (abs_secs<0.0)
320 ms= INFINITE;
321 else if (abs_secs==0.0)
322 ms= 0;
323 else {
324 ms= (long) ((abs_secs - now_secs())*1000.0 + 0.5);
325
326 // If the time already passed, still try once (ms==0). A short timeout
327 // may have turned negative or 0 because of the two time samples done.
328 //
329 if (ms<0) ms= 0;
330 }
331
332 // Unlock and start a wait, atomically (like condition variables do)
333 //
334 rc= SignalObjectAndWait( *mu_ref, // "object to signal" (unlock)
335 *ref, // "object to wait on"
336 ms,
337 FALSE ); // not alertable
338
339 // All waiting locks are woken here; each competes for the lock in turn.
340 //
341 // Note: We must get the lock even if we've timed out; it makes upper
342 // level code equivalent to how PThread does it.
343 //
344 MUTEX_LOCK(mu_ref);
345
346 if (rc==WAIT_TIMEOUT) return FALSE;
347 if (rc!=0) FAIL( "SignalObjectAndWait", rc );
348 return TRUE;
349 }
350 void SIGNAL_ALL( SIGNAL_T *ref ) {
351/*
352 * MSDN tries to scare that 'PulseEvent' is bad, unreliable and should not be
353 * used. Use condition variables instead (wow, they have that!?!); which will
354 * ONLY WORK on Vista and 2008 Server, it seems... so MS, isn't it.
355 *
356 * I refuse to believe that; using 'PulseEvent' is probably just as good as
357 * using Windows (XP) in the first place. Just don't use APC's (asynchronous
358 * process calls) in your C side coding.
359 */
360 // PulseEvent on manual event:
361 //
362 // Release ALL threads waiting for it (and go instantly back to unsignalled
363 // status = future threads to start a wait will wait)
364 //
365 if (!PulseEvent( *ref ))
366 FAIL( "PulseEvent", GetLastError() );
367 }
368#else
369 // PThread (Linux, OS X, ...)
370 //
371 // On OS X, user processes seem to be able to change priorities.
372 // On Linux, SCHED_RR and su privileges are required.. !-(
373 //
374 #include <errno.h>
375 #include <sys/time.h>
376 //
377 static void _PT_FAIL( int rc, const char *name, const char *file, uint_t line ) {
378 const char *why= (rc==EINVAL) ? "EINVAL" :
379 (rc==EBUSY) ? "EBUSY" :
380 (rc==EPERM) ? "EPERM" :
381 (rc==ENOMEM) ? "ENOMEM" :
382 (rc==ESRCH) ? "ESRCH" :
383 //...
384 "";
385 fprintf( stderr, "%s %d: %s failed, %d %s\n", file, line, name, rc, why );
386 abort();
387 }
388 #define PT_CALL( call ) { int rc= call; if (rc!=0) _PT_FAIL( rc, #call, __FILE__, __LINE__ ); }
389 //
390 void SIGNAL_INIT( SIGNAL_T *ref ) {
391 PT_CALL( pthread_cond_init(ref,NULL /*attr*/) );
392 }
393 void SIGNAL_FREE( SIGNAL_T *ref ) {
394 PT_CALL( pthread_cond_destroy(ref) );
395 }
396 //
397 /*
398 * Timeout is given as absolute since we may have fake wakeups during
399 * a timed out sleep. A Linda with some other key read, or just because
400 * PThread cond vars can wake up unwantedly.
401 */
402 bool_t SIGNAL_WAIT( SIGNAL_T *ref, pthread_mutex_t *mu, time_d abs_secs ) {
403 if (abs_secs<0.0) {
404 PT_CALL( pthread_cond_wait( ref, mu ) ); // infinite
405 } else {
406 int rc;
407 struct timespec ts;
408
409 assert( abs_secs != 0.0 );
410 prepare_timeout( &ts, abs_secs );
411
412 rc= pthread_cond_timedwait( ref, mu, &ts );
413
414 if (rc==ETIMEDOUT) return FALSE;
415 if (rc) { _PT_FAIL( rc, "pthread_cond_timedwait()", __FILE__, __LINE__ ); }
416 }
417 return TRUE;
418 }
419 //
420 void SIGNAL_ONE( SIGNAL_T *ref ) {
421 PT_CALL( pthread_cond_signal(ref) ); // wake up ONE (or no) waiting thread
422 }
423 //
424 void SIGNAL_ALL( SIGNAL_T *ref ) {
425 PT_CALL( pthread_cond_broadcast(ref) ); // wake up ALL waiting threads
426 }
427 //
428 void THREAD_CREATE( THREAD_T* ref,
429 THREAD_RETURN_T (*func)( void * ),
430 void *data, int prio /* -2..+2 */ ) {
431 pthread_attr_t _a;
432 pthread_attr_t *a= &_a;
433 struct sched_param sp;
434
435 PT_CALL( pthread_attr_init(a) );
436
437#ifndef PTHREAD_TIMEDJOIN
438 // We create a NON-JOINABLE thread. This is mainly due to the lack of
439 // 'pthread_timedjoin()', but does offer other benefits (s.a. earlier
440 // freeing of the thread's resources).
441 //
442 PT_CALL( pthread_attr_setdetachstate(a,PTHREAD_CREATE_DETACHED) );
443#endif
444
445 // Use this to find a system's default stack size (DEBUG)
446#if 0
447 { size_t n; pthread_attr_getstacksize( a, &n );
448 fprintf( stderr, "Getstack: %u\n", (unsigned int)n ); }
449 // 524288 on OS X
450 // 2097152 on Linux x86 (Ubuntu 7.04)
451 // 1048576 on FreeBSD 6.2 SMP i386
452#endif
453
454#if (defined _THREAD_STACK_SIZE) && (_THREAD_STACK_SIZE > 0)
455 PT_CALL( pthread_attr_setstacksize( a, _THREAD_STACK_SIZE ) );
456#endif
457
458 bool_t normal=
459#if defined(PLATFORM_LINUX) && defined(LINUX_SCHED_RR)
460 !sudo; // with sudo, even normal thread must use SCHED_RR
461#else
462 prio == 0; // create a default thread if
463#endif
464 if (!normal) {
465 // NB: PThreads priority handling is about as twisty as one can get it
466 // (and then some). DON*T TRUST ANYTHING YOU READ ON THE NET!!!
467
468 // "The specified scheduling parameters are only used if the scheduling
469 // parameter inheritance attribute is PTHREAD_EXPLICIT_SCHED."
470 //
471 PT_CALL( pthread_attr_setinheritsched( a, PTHREAD_EXPLICIT_SCHED ) );
472
473 //---
474 // "Select the scheduling policy for the thread: one of SCHED_OTHER
475 // (regular, non-real-time scheduling), SCHED_RR (real-time,
476 // round-robin) or SCHED_FIFO (real-time, first-in first-out)."
477 //
478 // "Using the RR policy ensures that all threads having the same
479 // priority level will be scheduled equally, regardless of their activity."
480 //
481 // "For SCHED_FIFO and SCHED_RR, the only required member of the
482 // sched_param structure is the priority sched_priority. For SCHED_OTHER,
483 // the affected scheduling parameters are implementation-defined."
484 //
485 // "The priority of a thread is specified as a delta which is added to
486 // the priority of the process."
487 //
488 // ".. priority is an integer value, in the range from 1 to 127.
489 // 1 is the least-favored priority, 127 is the most-favored."
490 //
491 // "Priority level 0 cannot be used: it is reserved for the system."
492 //
493 // "When you use specify a priority of -99 in a call to
494 // pthread_setschedparam(), the priority of the target thread is
495 // lowered to the lowest possible value."
496 //
497 // ...
498
499 // ** CONCLUSION **
500 //
501 // PThread priorities are _hugely_ system specific, and we need at
502 // least OS specific settings. Hopefully, Linuxes and OS X versions
503 // are uniform enough, among each other...
504 //
505#ifdef PLATFORM_OSX
506 // AK 10-Apr-07 (OS X PowerPC 10.4.9):
507 //
508 // With SCHED_RR, 26 seems to be the "normal" priority, where setting
509 // it does not seem to affect the order of threads processed.
510 //
511 // With SCHED_OTHER, the range 25..32 is normal (maybe the same 26,
512 // but the difference is not so clear with OTHER).
513 //
514 // 'sched_get_priority_min()' and '..max()' give 15, 47 as the
515 // priority limits. This could imply, user mode applications won't
516 // be able to use values outside of that range.
517 //
518 #define _PRIO_MODE SCHED_OTHER
519
520 // OS X 10.4.9 (PowerPC) gives ENOTSUP for process scope
521 //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
522
523 #define _PRIO_HI 32 // seems to work (_carefully_ picked!)
524 #define _PRIO_0 26 // detected
525 #define _PRIO_LO 1 // seems to work (tested)
526
527#elif defined(PLATFORM_LINUX)
528 // (based on Ubuntu Linux 2.6.15 kernel)
529 //
530 // SCHED_OTHER is the default policy, but does not allow for priorities.
531 // SCHED_RR allows priorities, all of which (1..99) are higher than
532 // a thread with SCHED_OTHER policy.
533 //
534 // <http://kerneltrap.org/node/6080>
535 // <http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library>
536 // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html>
537 //
538 // Manuals suggest checking #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING,
539 // but even Ubuntu does not seem to define it.
540 //
541 #define _PRIO_MODE SCHED_RR
542
543 // NTLP 2.5: only system scope allowed (being the basic reason why
544 // root privileges are required..)
545 //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
546
547 #define _PRIO_HI 99
548 #define _PRIO_0 50
549 #define _PRIO_LO 1
550
551#elif defined(PLATFORM_BSD)
552 //
553 // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html>
554 //
555 // "When control over the thread scheduling is desired, then FreeBSD
556 // with the libpthread implementation is by far the best choice .."
557 //
558 #define _PRIO_MODE SCHED_OTHER
559 #define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
560 #define _PRIO_HI 31
561 #define _PRIO_0 15
562 #define _PRIO_LO 1
563
564#elif defined(PLATFORM_CYGWIN)
565 //
566 // TBD: Find right values for Cygwin
567 //
568#else
569 #error "Unknown OS: not implemented!"
570#endif
571
572#ifdef _PRIO_SCOPE
573 PT_CALL( pthread_attr_setscope( a, _PRIO_SCOPE ) );
574#endif
575 PT_CALL( pthread_attr_setschedpolicy( a, _PRIO_MODE ) );
576
577#define _PRIO_AN (_PRIO_0 + ((_PRIO_HI-_PRIO_0)/2) )
578#define _PRIO_BN (_PRIO_LO + ((_PRIO_0-_PRIO_LO)/2) )
579
580 sp.sched_priority=
581 (prio == +2) ? _PRIO_HI :
582 (prio == +1) ? _PRIO_AN :
583#if defined(PLATFORM_LINUX) && defined(LINUX_SCHED_RR)
584 (prio == 0) ? _PRIO_0 :
585#endif
586 (prio == -1) ? _PRIO_BN : _PRIO_LO;
587
588 PT_CALL( pthread_attr_setschedparam( a, &sp ) );
589 }
590
591 //---
592 // Seems on OS X, _POSIX_THREAD_THREADS_MAX is some kind of system
593 // thread limit (not userland thread). Actual limit for us is way higher.
594 // PTHREAD_THREADS_MAX is not defined (even though man page refers to it!)
595 //
596# ifndef THREAD_CREATE_RETRIES_MAX
597 // Don't bother with retries; a failure is a failure
598 //
599 {
600 int rc= pthread_create( ref, a, func, data );
601 if (rc) _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__-1 );
602 }
603# else
604# error "This code deprecated"
605/*
606 // Wait slightly if thread creation has exchausted the system
607 //
608 { uint_t retries;
609 for( retries=0; retries<THREAD_CREATE_RETRIES_MAX; retries++ ) {
610
611 int rc= pthread_create( ref, a, func, data );
612 //
613 // OS X / Linux:
614 // EAGAIN: ".. lacked the necessary resources to create
615 // another thread, or the system-imposed limit on the
616 // total number of threads in a process
617 // [PTHREAD_THREADS_MAX] would be exceeded."
618 // EINVAL: attr is invalid
619 // Linux:
620 // EPERM: no rights for given parameters or scheduling (no sudo)
621 // ENOMEM: (known to fail with this code, too - not listed in man)
622
623 if (rc==0) break; // ok!
624
625 // In practise, exhaustion seems to be coming from memory, not a
626 // maximum number of threads. Keep tuning... ;)
627 //
628 if (rc==EAGAIN) {
629//fprintf( stderr, "Looping (retries=%d) ", retries ); // DEBUG
630
631 // Try again, later.
632
633 Yield();
634 } else {
635 _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__ );
636 }
637 }
638 }
639*/
640# endif
641
642 if (a) {
643 PT_CALL( pthread_attr_destroy(a) );
644 }
645 }
646 //
647 /*
648 * Wait for a thread to finish.
649 *
650 * 'mu_ref' is a lock we should use for the waiting; initially unlocked.
651 * Same lock as passed to THREAD_EXIT.
652 *
653 * Returns TRUE for succesful wait, FALSE for timed out
654 */
655#ifdef PTHREAD_TIMEDJOIN
656 bool_t THREAD_WAIT( THREAD_T *ref, double secs )
657#else
658 bool_t THREAD_WAIT( THREAD_T *ref, SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref, double secs )
659#endif
660{
661 struct timespec ts_store;
662 const struct timespec *timeout= NULL;
663 bool_t done;
664
665 // Do timeout counting before the locks
666 //
667#ifdef PTHREAD_TIMEDJOIN
668 if (secs>=0.0) {
669#else
670 if (secs>0.0) {
671#endif
672 prepare_timeout( &ts_store, now_secs()+secs );
673 timeout= &ts_store;
674 }
675
676#ifdef PTHREAD_TIMEDJOIN
677 /* Thread is joinable
678 */
679 if (!timeout) {
680 PT_CALL( pthread_join( *ref, NULL /*ignore exit value*/ ));
681 done= TRUE;
682 } else {
683 int rc= PTHREAD_TIMEDJOIN( *ref, NULL, timeout );
684 if ((rc!=0) && (rc!=ETIMEDOUT)) {
685 _PT_FAIL( rc, "PTHREAD_TIMEDJOIN", __FILE__, __LINE__-2 );
686 }
687 done= rc==0;
688 }
689#else
690 /* Since we've set the thread up as PTHREAD_CREATE_DETACHED, we cannot
691 * join with it. Use the cond.var.
692 */
693 MUTEX_LOCK( mu_ref );
694
695 // 'secs'==0.0 does not need to wait, just take the current status
696 // within the 'mu_ref' locks
697 //
698 if (secs != 0.0) {
699 while( *st_ref < DONE ) {
700 if (!timeout) {
701 PT_CALL( pthread_cond_wait( signal_ref, mu_ref ));
702 } else {
703 int rc= pthread_cond_timedwait( signal_ref, mu_ref, timeout );
704 if (rc==ETIMEDOUT) break;
705 if (rc!=0) _PT_FAIL( rc, "pthread_cond_timedwait", __FILE__, __LINE__-2 );
706 }
707 }
708 }
709 done= *st_ref >= DONE; // DONE|ERROR_ST|CANCELLED
710
711 MUTEX_UNLOCK( mu_ref );
712#endif
713 return done;
714 }
715 //
716 void THREAD_KILL( THREAD_T *ref ) {
717 pthread_cancel( *ref );
718 }
719#endif
720
721static const lua_Alloc alloc_f= 0;