diff options
Diffstat (limited to 'src/threading.c')
-rw-r--r-- | src/threading.c | 721 |
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 | |||
13 | Copyright (C) 2007-08 Asko Kauppi <akauppi@gmail.com> | ||
14 | |||
15 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
16 | of this software and associated documentation files (the "Software"), to deal | ||
17 | in the Software without restriction, including without limitation the rights | ||
18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
19 | copies of the Software, and to permit persons to whom the Software is | ||
20 | furnished to do so, subject to the following conditions: | ||
21 | |||
22 | The above copyright notice and this permission notice shall be included in | ||
23 | all copies or substantial portions of the Software. | ||
24 | |||
25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
31 | THE 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) | ||
75 | static 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 | */ | ||
88 | time_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 | */ | ||
162 | time_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 | */ | ||
172 | static 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 | |||
721 | static const lua_Alloc alloc_f= 0; | ||