Uncommented beaudio code
[pwlib.git] / src / ptlib / unix / tlibthrd.cxx
blob8f9f02ac84c59162ab85495fdac1d241c7f9c246
1 /*
2 * tlibthrd.cxx
4 * Routines for pre-emptive threading system
6 * Portable Windows Library
8 * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
25 * All Rights Reserved.
27 * Contributor(s): ______________________________________.
29 * $Log$
30 * Revision 1.126 2004/03/24 02:37:04 csoutheren
31 * Fixed problem with incorrect usage of sem_timedwait
33 * Revision 1.125 2004/03/23 04:56:23 csoutheren
34 * Added patches to use XPG6 threading under Linux if available
35 * Thanks to Matthew Hodgson
37 * Revision 1.124 2004/02/01 11:23:16 dsandras
38 * Reverted previous Change and removed Yield call from Current (). Fix from Christian Meder <chris@onestepahead.de>. Thanks for your help, Christian!
40 * Revision 1.123 2004/01/31 13:49:18 dominance
41 * Added 2.6 performance fix as proposed by Christian Meder <chris@onestepahead.de>.
43 * Revision 1.122 2003/09/17 09:02:15 csoutheren
44 * Removed memory leak detection code
46 * Revision 1.121 2003/05/16 17:40:55 shawn
47 * On Mac OS X, thread with the highest priority should use fixed priority
48 * scheduling policy. This avoids starvation caused by desktop activity.
50 * Revision 1.120 2003/05/02 00:58:40 dereks
51 * Add test for linux at the end of PMutex::Signal. Thanks Robert!
53 * Revision 1.119 2003/05/02 00:39:11 dereks
54 * Changes to make threading work on Redhat 9
56 * Revision 1.118 2003/04/24 12:03:13 rogerh
57 * Calling pthread_mutex_unlock() on a mutex which is not locked can be
58 * considered an error. NetBSD now enforce this error so we need to quickly
59 * try locking the mutex before unlocking it in ~PThread and ~PSemaphore.
61 * Revision 1.117 2003/04/08 03:29:31 robertj
62 * Fixed IsSuspeneded() so returns TRUE if thread not started yet, this makes
63 * it the same as the Win32 semantics.
65 * Revision 1.116 2003/03/10 15:37:00 rogerh
66 * fix IsTerminated() function.
68 * Revision 1.115 2003/03/07 00:07:15 robertj
69 * Fixed Mac OS X patch which broke every other platform.
71 * Revision 1.114 2003/03/06 08:58:48 rogerh
72 * P_MACOSX now carries the OSRELEASE. Use this to enable better threads
73 * support on Darwin 6.4. Submitted by Shawn.
75 * Revision 1.113 2003/02/20 23:32:00 robertj
76 * More RTEMS support patches, thanks Sebastian Meyer.
78 * Revision 1.112 2003/01/24 10:21:06 robertj
79 * Fixed issues in RTEMS support, thanks Vladimir Nesic
81 * Revision 1.111 2003/01/20 10:13:18 rogerh
82 * NetBSD thread changes
84 * Revision 1.110 2003/01/20 10:05:46 rogerh
85 * NetBSD thread changes
87 * Revision 1.109 2003/01/08 08:47:51 rogerh
88 * Add new Sleep() function for GNU PTH threads.
89 * Taken from NetBSD's package which uses PTH.
90 * Note: I am not sure this works correctly.
92 * Revision 1.108 2003/01/06 18:49:15 rogerh
93 * Back out pthead_kill to pthread_cancel change on NetBSD
95 * Revision 1.107 2002/12/11 05:39:26 robertj
96 * Added logging for file handle changes.
97 * Fixd bug where internal maxHandles not set when increased.
99 * Revision 1.106 2002/12/02 03:57:18 robertj
100 * More RTEMS support patches, thank you Vladimir Nesic.
102 * Revision 1.105 2002/11/22 10:14:07 robertj
103 * QNX port, thanks Xiaodan Tang
105 * Revision 1.104 2002/11/04 16:01:27 rogerh
106 * Using pthread_cancel and not pthread_kill with SIGKILL to terminate a thread
107 * On FreeBSD the thread does not have a handler for SIGKILL, it passes it up
108 * to the main process which gets killed! Assume the other BSDs are the same.
110 * Revision 1.103 2002/10/24 00:40:56 robertj
111 * Put back ability to terminate a thread from that threads context (removed
112 * in revision 1.101) but requires that destructor not do so.
113 * Changed pipe close to allow for possible EINTR, and retry close.
115 * Revision 1.102 2002/10/24 00:25:13 robertj
116 * Changed high load thread problem fix from the termination function to start
117 * function to finally, once and for all (I hope!) fix the race condition.
119 * Revision 1.101 2002/10/23 14:56:22 craigs
120 * Fixed problem with pipe leak under some circumstances
122 * Revision 1.100 2002/10/23 04:29:32 robertj
123 * Improved debugging for thread create/start/stop/destroy.
124 * Fixed race condition bug if auto-delete thread starts and completes before
125 * pthread_create returns, PX_threadId is not set yet!
127 * Revision 1.99 2002/10/22 07:42:52 robertj
128 * Added extra debugging for file handle and thread leak detection.
130 * Revision 1.98 2002/10/18 03:05:39 robertj
131 * Fixed thread leak caused by fixing the thread crash a few revisions back,
132 * caused by strange pthreads behaviour, at least under Linux.
134 * Revision 1.97 2002/10/17 13:44:27 robertj
135 * Port to RTEMS, thanks Vladimir Nesic.
137 * Revision 1.96 2002/10/17 12:57:24 robertj
138 * Added ability to increase maximum file handles on a process.
140 * Revision 1.95 2002/10/16 11:26:29 rogerh
141 * Add missing include. Noticed by Matthias on the GnomeMeeting IRC
143 * Revision 1.94 2002/10/10 03:09:48 robertj
144 * Fixed high load race condition when starting threads.
146 * Revision 1.93 2002/10/05 05:22:43 robertj
147 * Fixed adding GetThreadId() function.
149 * Revision 1.92 2002/10/01 06:27:48 robertj
150 * Added bullet proofing against possible EINTR error returns on all pthread
151 * functions when under heavy load. Linux really should NOT do this, but ...
153 * Revision 1.91 2002/09/04 03:14:18 robertj
154 * Backed out changes submitted by Martin Froehlich as they do not appear to
155 * actually do anything other than add a sychronisation point. The variables
156 * the patches intended to protect were already protected.
157 * Fixed bug where if a PMutex was signalled by a thread that did not have it
158 * locked, it would assert but continue to alter PMutex variables such that
159 * a deadlock or crash is likely.
161 * Revision 1.90 2002/08/29 01:50:40 robertj
162 * Changed the pthread_create so does retries if get EINTR or EAGAIN errors
163 * which indicate a (possibly) temporary resource limit.
164 * Enabled and adjusted tracing.
166 * Revision 1.89 2002/08/22 13:05:57 craigs
167 * Fixed problems with mutex implementation thanks to Martin Froehlich
169 * Revision 1.88 2002/07/15 06:56:59 craigs
170 * Fixed missing brace
172 * Revision 1.87 2002/07/15 06:39:23 craigs
173 * Added function to allow raising of per-process file handle limit
175 * Revision 1.86 2002/06/27 06:38:58 robertj
176 * Changes to remove memory leak display for things that aren't memory leaks.
178 * Revision 1.85 2002/06/27 02:04:01 robertj
179 * Fixed NetBSD compatibility issue, thanks Motoyuki OHMORI.
181 * Revision 1.84 2002/06/04 00:25:31 robertj
182 * Fixed incorrectly initialised trace indent, thanks Artis Kugevics
184 * Revision 1.83 2002/05/21 09:13:00 robertj
185 * Fixed problem when using posix recursive mutexes, thanks Artis Kugevics
187 * Revision 1.82 2002/04/24 01:11:37 robertj
188 * Fixed problem with PTRACE_BLOCK indent level being correct across threads.
190 * Revision 1.81 2002/04/16 10:57:26 rogerh
191 * Change WaitForTermination() so it does not use 100% CPU.
192 * Reported by Andrea <ghittino@tiscali.it>
194 * Revision 1.80 2002/01/23 04:26:36 craigs
195 * Added copy constructors for PSemaphore, PMutex and PSyncPoint to allow
196 * use of default copy constructors for objects containing instances of
197 * these classes
199 * Revision 1.79 2002/01/10 06:36:58 robertj
200 * Fixed possible resource leak under Solaris, thanks Joegen Baclor
202 * Revision 1.78 2001/12/17 11:06:46 robertj
203 * Removed assert on NULL PThread::Current(), can occur if thread from other
204 * subsystem to pwlib
206 * Revision 1.77 2001/10/03 05:11:50 robertj
207 * Fixed PSyncPoint wait with timeout when have pending signals.
209 * Revision 1.76 2001/09/27 23:50:03 craigs
210 * Fixed typo in PSemaphone destructor
212 * Revision 1.75 2001/09/24 10:09:48 rogerh
213 * Fix an uninitialised variable problem.
215 * Revision 1.74 2001/09/20 05:38:25 robertj
216 * Changed PSyncPoint to use pthread cond so timed wait blocks properly.
217 * Also prevented semaphore from being created if subclass does not use it.
219 * Revision 1.73 2001/09/19 17:37:47 craigs
220 * Added support for nested mutexes under Linux
222 * Revision 1.72 2001/09/18 06:53:35 robertj
223 * Made sure suspend can't exit early if get spurious signal
225 * Revision 1.71 2001/09/18 05:56:03 robertj
226 * Fixed numerous problems with thread suspend/resume and signals handling.
228 * Revision 1.70 2001/09/10 03:03:02 robertj
229 * Major change to fix problem with error codes being corrupted in a
230 * PChannel when have simultaneous reads and writes in threads.
231 * Changed threading so does not actually start thread until Resume(), makes
232 * the logic of start up much simpler and more portable.
233 * Quite a bit of tidyin up of the pthreads code.
235 * Revision 1.69 2001/08/30 08:57:40 robertj
236 * Changed calls to usleep to be PThread::Yield(), normalising single
237 * timeslice process swap out.
239 * Revision 1.68 2001/08/20 06:55:45 robertj
240 * Fixed ability to have PMutex::Wait() with times less than one second.
241 * Fixed small error in return value of block on I/O function, not critical.
243 * Revision 1.67 2001/08/07 02:50:03 craigs
244 * Fixed potential race condition in IO blocking
246 * Revision 1.66 2001/07/09 13:23:37 rogerh
247 * Fix a subtle bug in semaphore wait which showed up on FreeBSD
249 * Revision 1.65 2001/05/29 00:49:18 robertj
250 * Added ability to put in a printf %x in thread name to get thread object
251 * address into user settable thread name.
253 * Revision 1.64 2001/05/23 00:18:55 robertj
254 * Added support for real time threads, thanks Erland Lewin.
256 * Revision 1.63 2001/04/20 09:27:25 robertj
257 * Fixed previous change for auto delete threads, must have thread ID zeroed.
259 * Revision 1.62 2001/04/20 09:09:05 craigs
260 * Removed possible race condition whilst shutting down threads
262 * Revision 1.61 2001/03/20 06:44:25 robertj
263 * Lots of changes to fix the problems with terminating threads that are I/O
264 * blocked, especially when doing orderly shutdown of service via SIGTERM.
266 * Revision 1.60 2001/03/14 01:16:11 robertj
267 * Fixed signals processing, now uses housekeeping thread to handle signals
268 * synchronously. This also fixes issues with stopping PServiceProcess.
270 * Revision 1.59 2001/02/25 19:39:42 rogerh
271 * Use a Semaphore on Mac OS X to support threads which are started as 'suspended'
273 * Revision 1.58 2001/02/24 14:49:22 rogerh
274 * Add missing bracket
276 * Revision 1.57 2001/02/24 13:29:34 rogerh
277 * Mac OS X change to avoid Assertion
279 * Revision 1.56 2001/02/24 13:24:24 rogerh
280 * Add PThread support for Mac OS X and Darwin. There is one major issue. This
281 * OS does not suport pthread_kill() and sigwait() so we cannot support the
282 * Suspend() and Resume() functions to start and stop threads and we cannot
283 * create new threads in 'suspended' mode.
284 * Calling Suspend() raises an assertion. Calling Resume() does nothing.
285 * Threads started in 'suspended' mode start immediatly.
287 * Revision 1.55 2001/02/21 22:48:42 robertj
288 * Fixed incorrect test in PSemaphore::WillBlock() just added, thank Artis Kugevics.
290 * Revision 1.54 2001/02/20 00:21:14 robertj
291 * Fixed major bug in PSemapahore::WillBlock(), thanks Tomas Heran.
293 * Revision 1.53 2000/12/21 12:36:32 craigs
294 * Removed potential to stop threads twice
296 * Revision 1.52 2000/12/05 08:24:50 craigs
297 * Fixed problem with EINTR causing havoc
299 * Revision 1.51 2000/11/16 11:06:38 rogerh
300 * Add a better fix for the "user signal 2" aborts seen on FreeBSD 4.2 and above.
301 * We need to sched_yeild() after the pthread_create() to make sure the new thread
302 * actually has a chance to execute. The abort problem was caused when the
303 * resume signal was issued before the thread was ready for it.
305 * Revision 1.50 2000/11/12 23:30:02 craigs
306 * Added extra WaitForTermination to assist bug location
308 * Revision 1.49 2000/11/12 08:16:07 rogerh
309 * This change and the previous change, make pthreads work on FreeBSD 4.2.
310 * FreeBSD has improved its thread signal handling and now correctly generates a
311 * SIGUSR2 signal on a thread (the Resume Signal). However there was no handler
312 * for this signal and applications would abort with "User signal 2".
313 * So, a dummy sigResumeHandler has been added.
315 * Revision 1.48 2000/11/12 07:57:45 rogerh
316 * *** empty log message ***
318 * Revision 1.47 2000/10/31 08:09:51 rogerh
319 * Change return type of PX_GetThreadId() to save unnecessary typecasting
321 * Revision 1.46 2000/10/31 07:52:06 rogerh
322 * Add type casts to allow the code to compile on FreeBSD 4.1.1
324 * Revision 1.45 2000/10/30 05:48:33 robertj
325 * Added assert when get nested mutex.
327 * Revision 1.44 2000/10/24 03:32:40 robertj
328 * Fixed problem where thread that uses PThread::Current() in dtor crashes.
330 * Revision 1.43 2000/10/20 06:11:48 robertj
331 * Added function to change auto delete flag on a thread.
333 * Revision 1.42 2000/09/20 04:24:09 craigs
334 * Added extra tracing, and removed segv on exit when using tracing
336 * Revision 1.41 2000/06/21 01:01:22 robertj
337 * AIX port, thanks Wolfgang Platzer (wolfgang.platzer@infonova.at).
339 * Revision 1.40 2000/04/13 07:21:10 rogerh
340 * Fix typo in #defined
342 * Revision 1.39 2000/04/11 11:38:49 rogerh
343 * More NetBSD Pthread changes
345 * Revision 1.38 2000/04/10 11:47:02 rogerh
346 * Add initial NetBSD pthreads support
348 * Revision 1.37 2000/04/06 12:19:49 rogerh
349 * Add Mac OS X support submitted by Kevin Packard
351 * Revision 1.36 2000/03/20 22:56:34 craigs
352 * Fixed problems with race conditions caused by testing or changing
353 * attributes on a terminated thread. Only occured on a fast machine!
355 * Revision 1.35 2000/03/17 03:45:40 craigs
356 * Fixed problem with connect call hanging
358 * Revision 1.34 2000/03/08 12:17:09 rogerh
359 * Add OpenBSD support
361 * Revision 1.33 2000/02/29 13:18:21 robertj
362 * Added named threads to tracing, thanks to Dave Harvey
364 * Revision 1.32 2000/01/20 08:20:57 robertj
365 * FreeBSD v3 compatibility changes, thanks Roger Hardiman & Motonori Shindo
367 * Revision 1.31 1999/11/18 14:02:57 craigs
368 * Fixed problem with houskeeping thread termination
370 * Revision 1.30 1999/11/15 01:12:56 craigs
371 * Fixed problem with PSemaphore::Wait consuming 100% CPU
373 * Revision 1.29 1999/10/30 13:44:11 craigs
374 * Added correct method of aborting socket operations asynchronously
376 * Revision 1.28 1999/10/24 13:03:30 craigs
377 * Changed to capture io break signal
379 * Revision 1.27 1999/09/23 06:52:16 robertj
380 * Changed PSemaphore to use Posix semaphores.
382 * Revision 1.26 1999/09/03 02:26:25 robertj
383 * Changes to aid in breaking I/O locks on thread termination. Still needs more work esp in BSD!
385 * Revision 1.25 1999/09/02 11:56:35 robertj
386 * Fixed problem with destroying PMutex that is already locked.
388 * Revision 1.24 1999/08/24 13:40:56 craigs
389 * Fixed problem with condwait destorys failing on linux
391 * Revision 1.23 1999/08/23 05:33:45 robertj
392 * Made last threading changes Linux only.
394 * Revision 1.22 1999/08/23 05:14:13 robertj
395 * Removed blocking of interrupt signals as does not work in Linux threads.
397 * Revision 1.21 1999/07/30 00:40:32 robertj
398 * Fixed problem with signal variable in non-Linux platforms
400 * Revision 1.20 1999/07/19 01:32:24 craigs
401 * Changed signals used in pthreads code, is used by linux version.
403 * Revision 1.19 1999/07/15 13:10:55 craigs
404 * Fixed problem with EINTR in nontimed sempahore waits
406 * Revision 1.18 1999/07/15 13:05:33 robertj
407 * Fixed problem with getting EINTR in semaphore wait, is normal, not error.
409 * Revision 1.17 1999/07/11 13:42:13 craigs
410 * pthreads support for Linux
412 * Revision 1.16 1999/05/12 03:29:20 robertj
413 * Fixed problem with semaphore free, done at wrong time.
415 * Revision 1.15 1999/04/29 08:41:26 robertj
416 * Fixed problems with uninitialised mutexes in PProcess.
418 * Revision 1.14 1999/03/16 10:54:16 robertj
419 * Added parameterless version of WaitForTermination.
421 * Revision 1.13 1999/03/16 10:30:37 robertj
422 * Added missing PThread::WaitForTermination function.
424 * Revision 1.12 1999/01/12 12:09:51 robertj
425 * Removed redundent member variable, was in common.
426 * Fixed BSD threads compatibility.
428 * Revision 1.11 1999/01/11 12:05:56 robertj
429 * Fixed some more race conditions in threads.
431 * Revision 1.10 1999/01/11 03:42:26 robertj
432 * Fixed problem with destroying thread automatically.
434 * Revision 1.9 1999/01/09 03:37:28 robertj
435 * Fixed problem with closing thread waiting on semaphore.
436 * Improved efficiency of mutex to use pthread functions directly.
438 * Revision 1.8 1999/01/08 01:31:03 robertj
439 * Support for pthreads under FreeBSD
441 * Revision 1.7 1998/12/15 12:41:07 robertj
442 * Fixed signal handling so can now ^C a pthread version.
444 * Revision 1.6 1998/11/05 09:45:04 robertj
445 * Removed StartImmediate option in thread construction.
447 * Revision 1.5 1998/09/24 04:12:25 robertj
448 * Added open software license.
452 #include <ptlib/socket.h>
453 #include <sched.h> // for sched_yield
454 #include <pthread.h>
455 #include <sys/resource.h>
457 #ifdef P_RTEMS
458 #define SUSPEND_SIG SIGALRM
459 #include <sched.h>
460 #else
461 #define SUSPEND_SIG SIGVTALRM
462 #endif
464 #ifdef P_MACOSX
465 #include <mach/mach.h>
466 #include <mach/thread_policy.h>
467 #include <sys/param.h>
468 #include <sys/sysctl.h>
469 // going to need the main thread for adjusting relative priority
470 static pthread_t baseThread;
471 #endif
473 #ifdef P_HAS_SEMAPHORES_XPG6
474 #include "semaphore.h"
475 #endif
477 int PX_NewHandle(const char *, int);
480 #define PAssertPTHREAD(func, args) \
482 unsigned threadOpRetry = 0; \
483 while (PAssertThreadOp(func args, threadOpRetry, #func, __FILE__, __LINE__)); \
486 static BOOL PAssertThreadOp(int retval,
487 unsigned & retry,
488 const char * funcname,
489 const char * file,
490 unsigned line)
492 if (retval == 0) {
493 PTRACE_IF(2, retry > 0, "PWLib\t" << funcname << " required " << retry << " retries!");
494 return FALSE;
497 if (errno == EINTR || errno == EAGAIN) {
498 if (++retry < 1000) {
499 #if defined(P_RTEMS)
500 sched_yield();
501 #else
502 usleep(10000); // Basically just swap out thread to try and clear blockage
503 #endif
504 return TRUE; // Return value to try again
506 // Give up and assert
509 PAssertFunc(file, line, NULL, psprintf("Function %s failed", funcname));
510 return FALSE;
514 PDECLARE_CLASS(PHouseKeepingThread, PThread)
515 public:
516 PHouseKeepingThread()
517 : PThread(1000, NoAutoDeleteThread, NormalPriority, "Housekeeper")
518 { closing = FALSE; Resume(); }
520 void Main();
521 void SetClosing() { closing = TRUE; }
523 protected:
524 BOOL closing;
528 static pthread_mutex_t MutexInitialiser = PTHREAD_MUTEX_INITIALIZER;
529 static pthread_cond_t CondInitialiser = PTHREAD_COND_INITIALIZER;
532 #define new PNEW
535 void PHouseKeepingThread::Main()
537 PProcess & process = PProcess::Current();
539 while (!closing) {
540 PTimeInterval delay = process.timers.Process();
542 int fd = process.timerChangePipe[0];
544 P_fd_set read_fds = fd;
545 P_timeval tval = delay;
546 if (::select(fd+1, read_fds, NULL, NULL, tval) == 1) {
547 BYTE ch;
548 ::read(fd, &ch, 1);
551 process.PXCheckSignals();
556 void PProcess::SignalTimerChange()
558 if (housekeepingThread == NULL) {
559 housekeepingThread = new PHouseKeepingThread;
562 BYTE ch;
563 write(timerChangePipe[1], &ch, 1);
567 void PProcess::Construct()
569 #ifndef P_RTEMS
570 // get the file descriptor limit
571 struct rlimit rl;
572 PAssertOS(getrlimit(RLIMIT_NOFILE, &rl) == 0);
573 maxHandles = rl.rlim_cur;
574 PTRACE(4, "PWLib\tMaximum per-process file handles is " << maxHandles);
576 ::pipe(timerChangePipe);
577 #else
578 maxHandles = 500; // arbitrary value
579 socketpair(AF_INET,SOCK_STREAM,0,timerChangePipe);
580 #endif
582 // initialise the housekeeping thread
583 housekeepingThread = NULL;
585 #ifdef P_MACOSX
586 // records the main thread for priority adjusting
587 baseThread = pthread_self();
588 #endif
590 CommonConstruct();
594 BOOL PProcess::SetMaxHandles(int newMax)
596 #ifndef P_RTEMS
597 // get the current process limit
598 struct rlimit rl;
599 PAssertOS(getrlimit(RLIMIT_NOFILE, &rl) == 0);
601 // set the new current limit
602 rl.rlim_cur = newMax;
603 if (setrlimit(RLIMIT_NOFILE, &rl) == 0) {
604 PAssertOS(getrlimit(RLIMIT_NOFILE, &rl) == 0);
605 maxHandles = rl.rlim_cur;
606 if (maxHandles == newMax) {
607 PTRACE(2, "PWLib\tNew maximum per-process file handles set to " << maxHandles);
608 return TRUE;
611 #endif // !P_RTEMS
613 PTRACE(1, "PWLib\tCannot set per-process file handle limit to "
614 << newMax << " (is " << maxHandles << ") - check permissions");
615 return FALSE;
619 PProcess::~PProcess()
621 // Don't wait for housekeeper to stop if Terminate() is called from it.
622 if (housekeepingThread != NULL && PThread::Current() != housekeepingThread) {
623 housekeepingThread->SetClosing();
624 SignalTimerChange();
625 housekeepingThread->WaitForTermination();
626 delete housekeepingThread;
628 CommonDestruct();
632 //////////////////////////////////////////////////////////////////////////////
634 PThread::PThread()
636 // see InitialiseProcessThread()
640 void PThread::InitialiseProcessThread()
642 autoDelete = FALSE;
644 PX_origStackSize = 0;
645 PX_threadId = pthread_self();
646 PX_priority = NormalPriority;
647 PX_suspendCount = 0;
649 #ifndef P_HAS_SEMAPHORES
650 PX_waitingSemaphore = NULL;
651 PX_WaitSemMutex = MutexInitialiser;
652 #endif
654 PX_suspendMutex = MutexInitialiser;
656 #ifdef P_RTEMS
657 PAssertOS(socketpair(AF_INET,SOCK_STREAM,0,unblockPipe) == 0);
658 #else
659 PAssertOS(::pipe(unblockPipe) == 0);
660 #endif
662 ((PProcess *)this)->activeThreads.DisallowDeleteObjects();
663 ((PProcess *)this)->activeThreads.SetAt((unsigned)PX_threadId, this);
665 traceBlockIndentLevel = 0;
669 PThread::PThread(PINDEX stackSize,
670 AutoDeleteFlag deletion,
671 Priority priorityLevel,
672 const PString & name)
673 : threadName(name)
675 autoDelete = (deletion == AutoDeleteThread);
677 PAssert(stackSize > 0, PInvalidParameter);
678 PX_origStackSize = stackSize;
679 PX_threadId = 0;
680 PX_priority = priorityLevel;
681 PX_suspendCount = 1;
683 #ifndef P_HAS_SEMAPHORES
684 PX_waitingSemaphore = NULL;
685 PX_WaitSemMutex = MutexInitialiser;
686 #endif
688 PX_suspendMutex = MutexInitialiser;
690 #ifdef P_RTEMS
691 PAssertOS(socketpair(AF_INET,SOCK_STREAM,0,unblockPipe) == 0);
692 #else
693 PAssertOS(::pipe(unblockPipe) == 0);
694 #endif
695 PX_NewHandle("Thread unblock pipe", PMAX(unblockPipe[0], unblockPipe[1]));
697 // new thread is actually started the first time Resume() is called.
698 PX_firstTimeStart = TRUE;
700 traceBlockIndentLevel = 0;
702 PTRACE(5, "PWLib\tCreated thread " << this << ' ' << threadName);
706 PThread::~PThread()
708 if (PX_threadId != 0 && PX_threadId != pthread_self())
709 Terminate();
711 PAssertPTHREAD(::close, (unblockPipe[0]));
712 PAssertPTHREAD(::close, (unblockPipe[1]));
714 #ifndef P_HAS_SEMAPHORES
715 pthread_mutex_destroy(&PX_WaitSemMutex);
716 #endif
718 #ifdef P_NETBSD
719 // If the mutex was not locked, the unlock will fail */
720 pthread_mutex_trylock(&PX_suspendMutex);
721 #endif
722 pthread_mutex_unlock(&PX_suspendMutex);
723 pthread_mutex_destroy(&PX_suspendMutex);
725 PTRACE(5, "PWLib\tDestroyed thread " << this << ' ' << threadName);
729 void PThread::Restart()
731 if (!IsTerminated())
732 return;
734 pthread_attr_t threadAttr;
735 pthread_attr_init(&threadAttr);
736 pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
738 #if defined(P_LINUX)
740 Set realtime scheduling if our effective user id is root (only then is this
741 allowed) AND our priority is Highest.
742 As far as I can see, we could use either SCHED_FIFO or SCHED_RR here, it
743 doesn't matter.
744 I don't know if other UNIX OSs have SCHED_FIFO and SCHED_RR as well.
746 WARNING: a misbehaving thread (one that never blocks) started with Highest
747 priority can hang the entire machine. That is why root permission is
748 neccessary.
750 if ((geteuid() == 0) && (PX_priority == HighestPriority))
751 PAssertPTHREAD(pthread_attr_setschedpolicy, (&threadAttr, SCHED_FIFO));
752 #elif defined(P_RTEMS)
753 pthread_attr_setstacksize(&threadAttr, 2*PTHREAD_MINIMUM_STACK_SIZE);
754 pthread_attr_setinheritsched(&threadAttr, PTHREAD_EXPLICIT_SCHED);
755 pthread_attr_setschedpolicy(&threadAttr, SCHED_OTHER);
756 struct sched_param sched_param;
757 sched_param.sched_priority = 125; /* set medium priority */
758 pthread_attr_setschedparam(&threadAttr, &sched_param);
759 #endif
761 PAssertPTHREAD(pthread_create, (&PX_threadId, &threadAttr, PX_ThreadStart, this));
763 #ifdef P_MACOSX
764 if (PX_priority == HighestPriority) {
765 PTRACE(1, "set thread to have the highest priority (MACOSX)");
766 SetPriority(HighestPriority);
768 #endif
772 void PX_SuspendSignalHandler(int)
774 PThread * thread = PThread::Current();
775 if (thread == NULL)
776 return;
778 BOOL notResumed = TRUE;
779 while (notResumed) {
780 BYTE ch;
781 notResumed = ::read(thread->unblockPipe[0], &ch, 1) < 0 && errno == EINTR;
782 #if !( defined(P_NETBSD) && defined(P_NO_CANCEL) )
783 pthread_testcancel();
784 #endif
789 void PThread::Suspend(BOOL susp)
791 PAssertPTHREAD(pthread_mutex_lock, (&PX_suspendMutex));
793 // Check for start up condition, first time Resume() is called
794 if (PX_firstTimeStart) {
795 if (susp)
796 PX_suspendCount++;
797 else {
798 if (PX_suspendCount > 0)
799 PX_suspendCount--;
800 if (PX_suspendCount == 0) {
801 PX_firstTimeStart = FALSE;
802 Restart();
806 PAssertPTHREAD(pthread_mutex_unlock, (&PX_suspendMutex));
807 return;
810 #if defined(P_MACOSX) && (P_MACOSX <= 55)
811 // Suspend - warn the user with an Assertion
812 PAssertAlways("Cannot suspend threads on Mac OS X due to lack of pthread_kill()");
813 #else
814 if (pthread_kill(PX_threadId, 0) == 0) {
816 // if suspending, then see if already suspended
817 if (susp) {
818 PX_suspendCount++;
819 if (PX_suspendCount == 1) {
820 if (PX_threadId != pthread_self()) {
821 signal(SUSPEND_SIG, PX_SuspendSignalHandler);
822 pthread_kill(PX_threadId, SUSPEND_SIG);
824 else {
825 PAssertPTHREAD(pthread_mutex_unlock, (&PX_suspendMutex));
826 PX_SuspendSignalHandler(SUSPEND_SIG);
827 return; // Mutex already unlocked
832 // if resuming, then see if to really resume
833 else if (PX_suspendCount > 0) {
834 PX_suspendCount--;
835 if (PX_suspendCount == 0)
836 PXAbortBlock();
840 PAssertPTHREAD(pthread_mutex_unlock, (&PX_suspendMutex));
841 #endif // P_MACOSX
845 void PThread::Resume()
847 Suspend(FALSE);
851 BOOL PThread::IsSuspended() const
853 if (PX_firstTimeStart)
854 return TRUE;
856 if (IsTerminated())
857 return FALSE;
859 PAssertPTHREAD(pthread_mutex_lock, ((pthread_mutex_t *)&PX_suspendMutex));
860 BOOL suspended = PX_suspendCount != 0;
861 PAssertPTHREAD(pthread_mutex_unlock, ((pthread_mutex_t *)&PX_suspendMutex));
862 return suspended;
866 void PThread::SetAutoDelete(AutoDeleteFlag deletion)
868 PAssert(deletion != AutoDeleteThread || this != &PProcess::Current(), PLogicError);
869 autoDelete = deletion == AutoDeleteThread;
872 #ifdef P_MACOSX
873 // obtain thread priority of the main thread
874 static unsigned long
875 GetThreadBasePriority ()
877 thread_basic_info_data_t threadInfo;
878 policy_info_data_t thePolicyInfo;
879 unsigned int count;
881 if (baseThread == 0) {
882 return 0;
885 // get basic info
886 count = THREAD_BASIC_INFO_COUNT;
887 thread_info (pthread_mach_thread_np (baseThread), THREAD_BASIC_INFO,
888 (integer_t*)&threadInfo, &count);
890 switch (threadInfo.policy) {
891 case POLICY_TIMESHARE:
892 count = POLICY_TIMESHARE_INFO_COUNT;
893 thread_info(pthread_mach_thread_np (baseThread),
894 THREAD_SCHED_TIMESHARE_INFO,
895 (integer_t*)&(thePolicyInfo.ts), &count);
896 return thePolicyInfo.ts.base_priority;
897 break;
899 case POLICY_FIFO:
900 count = POLICY_FIFO_INFO_COUNT;
901 thread_info(pthread_mach_thread_np (baseThread),
902 THREAD_SCHED_FIFO_INFO,
903 (integer_t*)&(thePolicyInfo.fifo), &count);
904 if (thePolicyInfo.fifo.depressed) {
905 return thePolicyInfo.fifo.depress_priority;
906 } else {
907 return thePolicyInfo.fifo.base_priority;
909 break;
911 case POLICY_RR:
912 count = POLICY_RR_INFO_COUNT;
913 thread_info(pthread_mach_thread_np (baseThread),
914 THREAD_SCHED_RR_INFO,
915 (integer_t*)&(thePolicyInfo.rr), &count);
916 if (thePolicyInfo.rr.depressed) {
917 return thePolicyInfo.rr.depress_priority;
918 } else {
919 return thePolicyInfo.rr.base_priority;
921 break;
924 return 0;
926 #endif
928 void PThread::SetPriority(Priority priorityLevel)
930 PX_priority = priorityLevel;
932 #if defined(P_LINUX)
933 if (IsTerminated())
934 return;
936 struct sched_param sched_param;
938 if ((priorityLevel == HighestPriority) && (geteuid() == 0) ) {
939 sched_param.sched_priority = sched_get_priority_min( SCHED_FIFO );
941 PAssertPTHREAD(pthread_setschedparam, (PX_threadId, SCHED_FIFO, &sched_param));
943 else if (priorityLevel != HighestPriority) {
944 /* priority 0 is the only permitted value for the SCHED_OTHER scheduler */
945 sched_param.sched_priority = 0;
947 PAssertPTHREAD(pthread_setschedparam, (PX_threadId, SCHED_OTHER, &sched_param));
949 #endif
951 #if defined(P_MACOSX)
952 if (IsTerminated())
953 return;
955 if (priorityLevel == HighestPriority) {
956 /* get fixed priority */
958 int result;
960 thread_extended_policy_data_t theFixedPolicy;
961 thread_precedence_policy_data_t thePrecedencePolicy;
962 long relativePriority;
964 theFixedPolicy.timeshare = false; // set to true for a non-fixed thread
965 result = thread_policy_set (pthread_mach_thread_np(PX_threadId),
966 THREAD_EXTENDED_POLICY,
967 (thread_policy_t)&theFixedPolicy,
968 THREAD_EXTENDED_POLICY_COUNT);
969 if (result != KERN_SUCCESS) {
970 PTRACE(1, "thread_policy - Couldn't set thread as fixed priority.");
973 // set priority
975 // precedency policy's "importance" value is relative to
976 // spawning thread's priority
978 relativePriority = 62 - GetThreadBasePriority();
979 PTRACE(1, "relativePriority is " << relativePriority <<
980 " base priority is " << GetThreadBasePriority());
982 thePrecedencePolicy.importance = relativePriority;
983 result = thread_policy_set (pthread_mach_thread_np(PX_threadId),
984 THREAD_PRECEDENCE_POLICY,
985 (thread_policy_t)&thePrecedencePolicy,
986 THREAD_PRECEDENCE_POLICY_COUNT);
987 if (result != KERN_SUCCESS) {
988 PTRACE(1, "thread_policy - Couldn't set thread priority.");
992 #endif
996 PThread::Priority PThread::GetPriority() const
998 #if defined(LINUX)
999 int schedulingPolicy;
1000 struct sched_param schedParams;
1002 PAssertPTHREAD(pthread_getschedparam, (PX_threadId, &schedulingPolicy, &schedParams));
1004 switch( schedulingPolicy )
1006 case SCHED_OTHER:
1007 break;
1009 case SCHED_FIFO:
1010 case SCHED_RR:
1011 return HighestPriority;
1013 default:
1014 /* Unknown scheduler. We don't know what priority this thread has. */
1015 PTRACE(1, "PWLib\tPThread::GetPriority: unknown scheduling policy #" << schedulingPolicy);
1017 #endif
1019 return NormalPriority; /* as good a guess as any */
1023 #ifndef P_HAS_SEMAPHORES
1024 void PThread::PXSetWaitingSemaphore(PSemaphore * sem)
1026 PAssertPTHREAD(pthread_mutex_lock, (&PX_WaitSemMutex));
1027 PX_waitingSemaphore = sem;
1028 PAssertPTHREAD(pthread_mutex_unlock, (&PX_WaitSemMutex));
1030 #endif
1033 #ifdef P_GNU_PTH
1034 // GNU PTH threads version (used by NetBSD)
1035 // Taken from NetBSD pkg patches
1036 void PThread::Sleep(const PTimeInterval & timeout)
1038 PTime lastTime;
1039 PTime targetTime = PTime() + timeout;
1041 sched_yield();
1042 lastTime = PTime();
1044 while (lastTime < targetTime) {
1045 P_timeval tval = targetTime - lastTime;
1046 if (select(0, NULL, NULL, NULL, tval) < 0 && errno != EINTR)
1047 break;
1049 pthread_testcancel();
1051 lastTime = PTime();
1055 #else
1056 // Normal Posix threads version
1057 void PThread::Sleep(const PTimeInterval & timeout)
1059 PTime lastTime;
1060 PTime targetTime = lastTime + timeout;
1061 do {
1062 P_timeval tval = targetTime - lastTime;
1063 if (select(0, NULL, NULL, NULL, tval) < 0 && errno != EINTR)
1064 break;
1066 #if !( defined(P_NETBSD) && defined(P_NO_CANCEL) )
1067 pthread_testcancel();
1068 #endif
1070 lastTime = PTime();
1071 } while (lastTime < targetTime);
1073 #endif
1075 void PThread::Yield()
1077 sched_yield();
1081 PThread * PThread::Current()
1083 PProcess & process = PProcess::Current();
1084 process.threadMutex.Wait();
1085 PThread * thread = process.activeThreads.GetAt((unsigned)pthread_self());
1086 process.threadMutex.Signal();
1087 return thread;
1091 void PThread::Terminate()
1093 if (PX_origStackSize <= 0)
1094 return;
1096 // don't use PThread::Current, as the thread may already not be in the
1097 // active threads list
1098 if (PX_threadId == pthread_self()) {
1099 pthread_exit(0);
1100 return;
1103 if (IsTerminated())
1104 return;
1106 PTRACE(2, "PWLib\tForcing termination of thread " << (void *)this);
1108 PXAbortBlock();
1109 WaitForTermination(20);
1111 #ifndef P_HAS_SEMAPHORES
1112 PAssertPTHREAD(pthread_mutex_lock, (&PX_WaitSemMutex));
1113 if (PX_waitingSemaphore != NULL) {
1114 PAssertPTHREAD(pthread_mutex_lock, (&PX_waitingSemaphore->mutex));
1115 PX_waitingSemaphore->queuedLocks--;
1116 PAssertPTHREAD(pthread_mutex_unlock, (&PX_waitingSemaphore->mutex));
1117 PX_waitingSemaphore = NULL;
1119 PAssertPTHREAD(pthread_mutex_unlock, (&PX_WaitSemMutex));
1120 #endif
1122 #if ( defined(P_NETBSD) && defined(P_NO_CANCEL) )
1123 pthread_kill(PX_threadId,SIGKILL);
1124 #else
1125 if (PX_threadId) {
1126 pthread_cancel(PX_threadId);
1128 #endif
1132 BOOL PThread::IsTerminated() const
1134 if (PX_threadId == 0)
1135 return TRUE;
1137 #if defined(P_MACOSX) && (P_MACOSX <= 55)
1138 // MacOS X (darwin 5.5) does not support pthread_kill so we cannot use it
1139 // to test the validity of the thread
1140 #else
1141 if (pthread_kill(PX_threadId, 0) != 0)
1142 return TRUE;
1143 #endif
1145 PTRACE(7, "PWLib\tIsTerminated(" << (void *)this << ") not dead yet");
1146 return FALSE;
1150 void PThread::WaitForTermination() const
1152 PAssert(Current() != this, "Waiting for self termination!");
1154 while (!IsTerminated()) {
1155 Sleep(10); // sleep for 10ms. This slows down the busy loop removing 100%
1156 // CPU usage and also yeilds so other threads can run.
1161 BOOL PThread::WaitForTermination(const PTimeInterval & maxWait) const
1163 PAssert(Current() != this, "Waiting for self termination!");
1165 PTRACE(6, "PWLib\tWaitForTermination(" << maxWait << ')');
1167 PTimer timeout = maxWait;
1168 while (!IsTerminated()) {
1169 if (timeout == 0)
1170 return FALSE;
1171 Sleep(10); // sleep for 10ms. This slows down the busy loop removing 100%
1172 // CPU usage and also yeilds so other threads can run.
1174 return TRUE;
1178 void * PThread::PX_ThreadStart(void * arg)
1180 pthread_t threadId = pthread_self();
1182 // self-detach
1183 pthread_detach(threadId);
1185 PThread * thread = (PThread *)arg;
1187 // Added this to guarantee that the thread creation (PThread::Restart)
1188 // has completed before we start the thread. Then the PX_threadId has
1189 // been set.
1190 pthread_mutex_lock(&thread->PX_suspendMutex);
1192 thread->SetThreadName(thread->GetThreadName());
1194 pthread_mutex_unlock(&thread->PX_suspendMutex);
1196 PProcess & process = PProcess::Current();
1198 PINDEX newHighWaterMark = 0;
1199 static PINDEX highWaterMark = 0;
1201 // add thread to thread list
1202 process.threadMutex.Wait();
1203 process.activeThreads.SetAt((unsigned)threadId, thread);
1204 if (process.activeThreads.GetSize() > highWaterMark)
1205 newHighWaterMark = highWaterMark = process.activeThreads.GetSize();
1206 process.threadMutex.Signal();
1208 PTRACE_IF(4, newHighWaterMark > 0, "PWLib\tThread high water mark set: " << newHighWaterMark);
1210 // make sure the cleanup routine is called when the thread exits
1211 pthread_cleanup_push(PThread::PX_ThreadEnd, arg);
1213 PTRACE(5, "PWLib\tStarted thread " << thread << ' ' << thread->threadName);
1215 // now call the the thread main routine
1216 thread->Main();
1218 // execute the cleanup routine
1219 pthread_cleanup_pop(1);
1221 return NULL;
1225 void PThread::PX_ThreadEnd(void * arg)
1227 PThread * thread = (PThread *)arg;
1228 pthread_t id = thread->GetThreadId();
1229 if (id == 0) {
1230 // Don't know why, but pthreads under Linux at least can call this function
1231 // multiple times! Probably a bug, but we have to allow for it.
1232 PTRACE(2, "PWLib\tAttempted to multiply end thread " << thread << " ThreadID=" << (void *)id);
1233 return;
1236 PTRACE(5, "PWLib\tEnded thread " << thread << ' ' << thread->threadName);
1238 // remove this thread from the active thread list
1239 PProcess & process = PProcess::Current();
1240 process.threadMutex.Wait();
1241 process.activeThreads.SetAt((unsigned)id, NULL);
1242 process.threadMutex.Signal();
1244 // delete the thread if required, note this is done this way to avoid
1245 // a race condition, the thread ID cannot be zeroed before the if!
1246 if (thread->autoDelete) {
1247 thread->PX_threadId = 0; // Prevent terminating terminated thread
1249 // Now should be safe to delete the thread!
1250 delete thread;
1252 else
1253 thread->PX_threadId = 0;
1257 int PThread::PXBlockOnIO(int handle, int type, const PTimeInterval & timeout)
1259 PTRACE(7, "PWLib\tPThread::PXBlockOnIO(" << handle << ',' << type << ')');
1261 if ((handle < 0) || (handle >= PProcess::Current().GetMaxHandles())) {
1262 PTRACE(2, "PWLib\tAttempt to use illegal handle in PThread::PXBlockOnIO, handle=" << handle);
1263 errno = EBADF;
1264 return -1;
1267 // make sure we flush the buffer before doing a write
1268 P_fd_set read_fds;
1269 P_fd_set write_fds;
1270 P_fd_set exception_fds;
1272 int retval;
1273 do {
1274 switch (type) {
1275 case PChannel::PXReadBlock:
1276 case PChannel::PXAcceptBlock:
1277 read_fds = handle;
1278 write_fds.Zero();
1279 exception_fds.Zero();
1280 break;
1281 case PChannel::PXWriteBlock:
1282 read_fds.Zero();
1283 write_fds = handle;
1284 exception_fds.Zero();
1285 break;
1286 case PChannel::PXConnectBlock:
1287 read_fds.Zero();
1288 write_fds = handle;
1289 exception_fds = handle;
1290 break;
1291 default:
1292 PAssertAlways(PLogicError);
1293 return 0;
1296 // include the termination pipe into all blocking I/O functions
1297 read_fds += unblockPipe[0];
1299 P_timeval tval = timeout;
1300 retval = ::select(PMAX(handle, unblockPipe[0])+1,
1301 read_fds, write_fds, exception_fds, tval);
1302 } while (retval < 0 && errno == EINTR);
1304 if ((retval == 1) && read_fds.IsPresent(unblockPipe[0])) {
1305 BYTE ch;
1306 ::read(unblockPipe[0], &ch, 1);
1307 errno = EINTR;
1308 retval = -1;
1309 PTRACE(6, "PWLib\tUnblocked I/O");
1312 return retval;
1315 void PThread::PXAbortBlock() const
1317 BYTE ch;
1318 ::write(unblockPipe[1], &ch, 1);
1322 ///////////////////////////////////////////////////////////////////////////////
1324 PSemaphore::PSemaphore(PXClass pxc)
1326 pxClass = pxc;
1327 mutex = MutexInitialiser;
1328 condVar = CondInitialiser;
1330 // these should never be used, as this constructor is
1331 // only used for PMutex and PSyncPoint and they have their
1332 // own copy constructors
1333 initialVar = maxCountVar = 0;
1337 PSemaphore::PSemaphore(unsigned initial, unsigned maxCount)
1339 pxClass = PXSemaphore;
1340 mutex = MutexInitialiser;
1341 condVar = CondInitialiser;
1343 initialVar = initial;
1344 maxCountVar = maxCount;
1346 #ifdef P_HAS_SEMAPHORES
1347 PAssertPTHREAD(sem_init, (&semId, 0, initial));
1348 #else
1349 PAssert(maxCount > 0, "Invalid semaphore maximum.");
1350 if (initial > maxCount)
1351 initial = maxCount;
1353 currentCount = initial;
1354 maximumCount = maxCount;
1355 queuedLocks = 0;
1356 #endif
1359 PSemaphore::PSemaphore(const PSemaphore & sem)
1361 pxClass = sem.GetSemClass();
1362 mutex = MutexInitialiser;
1363 condVar = CondInitialiser;
1365 initialVar = sem.GetInitial();
1366 maxCountVar = sem.GetMaxCount();
1368 #ifdef P_HAS_SEMAPHORES
1369 PAssertPTHREAD(sem_init, (&semId, 0, initialVar));
1370 #else
1371 PAssert(maxCountVar > 0, "Invalid semaphore maximum.");
1372 if (initialVar > maxCountVar)
1373 initialVar = maxCountVar;
1375 currentCount = initialVar;
1376 maximumCount = maxCountVar;
1377 queuedLocks = 0;
1378 #endif
1381 PSemaphore::~PSemaphore()
1383 pthread_cond_destroy(&condVar);
1385 #ifdef P_NETBSD
1386 // If the mutex was not locked, the unlock will fail */
1387 pthread_mutex_trylock(&mutex);
1388 #endif
1390 pthread_mutex_unlock(&mutex);
1391 pthread_mutex_destroy(&mutex);
1393 if (pxClass == PXSemaphore) {
1394 #ifdef P_HAS_SEMAPHORES
1395 PAssertPTHREAD(sem_destroy, (&semId));
1396 #else
1397 PAssert(queuedLocks == 0, "Semaphore destroyed with queued locks");
1398 #endif
1403 void PSemaphore::Wait()
1405 #ifdef P_HAS_SEMAPHORES
1406 PAssertPTHREAD(sem_wait, (&semId));
1407 #else
1408 PAssertPTHREAD(pthread_mutex_lock, (&mutex));
1410 queuedLocks++;
1411 PThread::Current()->PXSetWaitingSemaphore(this);
1413 while (currentCount == 0) {
1414 int err = pthread_cond_wait(&condVar, &mutex);
1415 PAssert(err == 0 || err == EINTR, psprintf("wait error = %i", err));
1418 PThread::Current()->PXSetWaitingSemaphore(NULL);
1419 queuedLocks--;
1421 currentCount--;
1423 PAssertPTHREAD(pthread_mutex_unlock, (&mutex));
1424 #endif
1428 BOOL PSemaphore::Wait(const PTimeInterval & waitTime)
1430 if (waitTime == PMaxTimeInterval) {
1431 Wait();
1432 return TRUE;
1435 // create absolute finish time
1436 PTime finishTime;
1437 finishTime += waitTime;
1439 #ifdef P_HAS_SEMAPHORES
1440 #ifdef P_HAS_SEMAPHORES_XPG6
1441 // use proper timed spinlocks if supported.
1442 // http://www.opengroup.org/onlinepubs/007904975/functions/sem_timedwait.html
1444 struct timespec absTime;
1445 absTime.tv_sec = finishTime.GetTimeInSeconds();
1446 absTime.tv_nsec = finishTime.GetMicrosecond() * 1000;
1448 if (sem_timedwait(&semId, &absTime) == 0) {
1449 return TRUE;
1451 else {
1452 return FALSE;
1455 #else
1456 // loop until timeout, or semaphore becomes available
1457 // don't use a PTimer, as this causes the housekeeping
1458 // thread to get very busy
1459 do {
1460 if (sem_trywait(&semId) == 0)
1461 return TRUE;
1463 #if defined(P_LINUX)
1464 // sched_yield in a tight loop is bad karma
1465 // for the linux scheduler: http://www.ussg.iu.edu/hypermail/linux/kernel/0312.2/1127.html
1466 PThread::Current()->Sleep(10);
1467 #else
1468 PThread::Yield();
1469 #endif
1470 } while (PTime() < finishTime);
1472 return FALSE;
1474 #endif
1475 #else
1477 struct timespec absTime;
1478 absTime.tv_sec = finishTime.GetTimeInSeconds();
1479 absTime.tv_nsec = finishTime.GetMicrosecond() * 1000;
1481 PAssertPTHREAD(pthread_mutex_lock, (&mutex));
1483 PThread * thread = PThread::Current();
1484 thread->PXSetWaitingSemaphore(this);
1485 queuedLocks++;
1487 BOOL ok = TRUE;
1488 while (currentCount == 0) {
1489 int err = pthread_cond_timedwait(&condVar, &mutex, &absTime);
1490 if (err == ETIMEDOUT) {
1491 ok = FALSE;
1492 break;
1494 else
1495 PAssert(err == 0 || err == EINTR, psprintf("timed wait error = %i", err));
1498 thread->PXSetWaitingSemaphore(NULL);
1499 queuedLocks--;
1501 if (ok)
1502 currentCount--;
1504 PAssertPTHREAD(pthread_mutex_unlock, ((pthread_mutex_t *)&mutex));
1506 return ok;
1507 #endif
1511 void PSemaphore::Signal()
1513 #ifdef P_HAS_SEMAPHORES
1514 PAssertPTHREAD(sem_post, (&semId));
1515 #else
1516 PAssertPTHREAD(pthread_mutex_lock, (&mutex));
1518 if (currentCount < maximumCount)
1519 currentCount++;
1521 if (queuedLocks > 0)
1522 PAssertPTHREAD(pthread_cond_signal, (&condVar));
1524 PAssertPTHREAD(pthread_mutex_unlock, (&mutex));
1525 #endif
1529 BOOL PSemaphore::WillBlock() const
1531 #ifdef P_HAS_SEMAPHORES
1532 if (sem_trywait((sem_t *)&semId) != 0) {
1533 PAssertOS(errno == EAGAIN || errno == EINTR);
1534 return TRUE;
1536 PAssertPTHREAD(sem_post, ((sem_t *)&semId));
1537 return FALSE;
1538 #else
1539 return currentCount == 0;
1540 #endif
1543 #if defined(P_QNX) && defined(P_HAS_RECURSIVE_MUTEX)
1544 #define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
1545 #endif
1547 PMutex::PMutex()
1548 : PSemaphore(PXMutex)
1550 #ifdef P_HAS_RECURSIVE_MUTEX
1551 pthread_mutexattr_t attr;
1552 pthread_mutexattr_init(&attr);
1553 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
1554 pthread_mutex_init(&mutex, &attr);
1555 #else
1556 ownerThreadId = (pthread_t)-1;
1557 lockCount = 0;
1558 #endif
1561 PMutex::PMutex(const PMutex & /*mut*/)
1562 : PSemaphore(PXMutex)
1564 #ifdef P_HAS_RECURSIVE_MUTEX
1565 pthread_mutexattr_t attr;
1566 pthread_mutexattr_init(&attr);
1567 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
1568 pthread_mutex_init(&mutex, &attr);
1569 #else
1570 ownerThreadId = (pthread_t)-1;
1571 lockCount = 0;
1572 #endif
1575 void PMutex::Wait()
1577 #ifndef P_HAS_RECURSIVE_MUTEX
1578 pthread_t currentThreadId = pthread_self();
1580 // if the mutex is already acquired by this thread,
1581 // then just increment the lock count
1582 if (pthread_equal(ownerThreadId, currentThreadId)) {
1583 // Note this does not need a lock as it can only be touched by the thread
1584 // which already has the mutex locked.
1585 lockCount++;
1586 return;
1588 #endif
1590 // acquire the lock for real
1591 PAssertPTHREAD(pthread_mutex_lock, (&mutex));
1593 #ifndef P_HAS_RECURSIVE_MUTEX
1594 PAssert((ownerThreadId == (pthread_t)-1) && (lockCount == 0),
1595 "PMutex acquired whilst locked by another thread");
1596 // Note this is protected by the mutex itself only the thread with
1597 // the lock can alter it.
1598 ownerThreadId = currentThreadId;
1599 #endif
1603 BOOL PMutex::Wait(const PTimeInterval & waitTime)
1605 // if waiting indefinitely, then do so
1606 if (waitTime == PMaxTimeInterval) {
1607 Wait();
1608 return TRUE;
1611 #ifndef P_HAS_RECURSIVE_MUTEX
1612 // get the current thread ID
1613 pthread_t currentThreadId = pthread_self();
1615 // if we already have the mutex, return immediately
1616 if (pthread_equal(ownerThreadId, currentThreadId)) {
1617 // Note this does not need a lock as it can only be touched by the thread
1618 // which already has the mutex locked.
1619 lockCount++;
1620 return TRUE;
1622 #endif
1624 // create absolute finish time
1625 PTime finishTime;
1626 finishTime += waitTime;
1628 do {
1629 if (pthread_mutex_trylock(&mutex) == 0) {
1630 #ifndef P_HAS_RECURSIVE_MUTEX
1631 PAssert((ownerThreadId == (pthread_t)-1) && (lockCount == 0),
1632 "PMutex acquired whilst locked by another thread");
1633 // Note this is protected by the mutex itself only the thread with
1634 // the lock can alter it.
1635 ownerThreadId = currentThreadId;
1636 #endif
1638 return TRUE;
1641 PThread::Current()->Sleep(10); // sleep for 10ms
1642 } while (PTime() < finishTime);
1644 return FALSE;
1648 void PMutex::Signal()
1650 #ifndef P_HAS_RECURSIVE_MUTEX
1651 if (!pthread_equal(ownerThreadId, pthread_self())) {
1652 PAssertAlways("PMutex signal failed - no matching wait or signal by wrong thread");
1653 return;
1656 // if lock was recursively acquired, then decrement the counter
1657 // Note this does not need a separate lock as it can only be touched by the thread
1658 // which already has the mutex locked.
1659 if (lockCount > 0) {
1660 lockCount--;
1661 return;
1664 // otherwise mark mutex as available
1665 ownerThreadId = (pthread_t)-1;
1666 #endif
1668 PAssertPTHREAD(pthread_mutex_unlock, (&mutex));
1672 BOOL PMutex::WillBlock() const
1674 #ifndef P_HAS_RECURSIVE_MUTEX
1675 pthread_t currentThreadId = pthread_self();
1676 if (currentThreadId == ownerThreadId)
1677 return FALSE;
1678 #endif
1680 pthread_mutex_t * mp = (pthread_mutex_t*)&mutex;
1681 if (pthread_mutex_trylock(mp) != 0)
1682 return TRUE;
1684 PAssertPTHREAD(pthread_mutex_unlock, (mp));
1685 return FALSE;
1689 PSyncPoint::PSyncPoint()
1690 : PSemaphore(PXSyncPoint)
1692 signalCount = 0;
1695 PSyncPoint::PSyncPoint(const PSyncPoint &)
1696 : PSemaphore(PXSyncPoint)
1698 signalCount = 0;
1701 void PSyncPoint::Wait()
1703 PAssertPTHREAD(pthread_mutex_lock, (&mutex));
1704 while (signalCount == 0)
1705 pthread_cond_wait(&condVar, &mutex);
1706 signalCount--;
1707 PAssertPTHREAD(pthread_mutex_unlock, (&mutex));
1711 BOOL PSyncPoint::Wait(const PTimeInterval & waitTime)
1713 PAssertPTHREAD(pthread_mutex_lock, (&mutex));
1715 PTime finishTime;
1716 finishTime += waitTime;
1717 struct timespec absTime;
1718 absTime.tv_sec = finishTime.GetTimeInSeconds();
1719 absTime.tv_nsec = finishTime.GetMicrosecond() * 1000;
1721 int err = 0;
1722 while (signalCount == 0) {
1723 err = pthread_cond_timedwait(&condVar, &mutex, &absTime);
1724 if (err == 0 || err == ETIMEDOUT)
1725 break;
1727 PAssertOS(err == EINTR && errno == EINTR);
1730 if (err == 0)
1731 signalCount--;
1733 PAssertPTHREAD(pthread_mutex_unlock, (&mutex));
1735 return err == 0;
1739 void PSyncPoint::Signal()
1741 PAssertPTHREAD(pthread_mutex_lock, (&mutex));
1742 signalCount++;
1743 PAssertPTHREAD(pthread_cond_signal, (&condVar));
1744 PAssertPTHREAD(pthread_mutex_unlock, (&mutex));
1748 BOOL PSyncPoint::WillBlock() const
1750 return signalCount == 0;