Uncommented beaudio code
[pwlib.git] / src / ptlib / unix / tlibmpthrd.cxx
blob09d840167be62f10f15933d45d50d94938fb2cf2
1 /*
2 * tlibmpthrd.cxx
4 * Routines for Macintosh 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.4 2002/06/27 06:38:58 robertj
31 * Changes to remove memory leak display for things that aren't memory leaks.
33 * Revision 1.3 2002/02/19 07:40:59 rogerh
34 * Remove PMutex destructor for Carbon.
36 * Revision 1.2 2002/02/19 07:28:02 rogerh
37 * PXAbortIO -> PXAbortBlock. Submitted by Peter Johnson <paj@mac.com>
39 * Revision 1.1 2001/08/11 15:38:43 rogerh
40 * Add Mac OS Carbon changes from John Woods <jfw@jfwhome.funhouse.com>
44 #include <sys/resource.h>
45 #include <new> // just because I want to throw std::bad_alloc...
47 #ifndef NDEBUG
48 #define DEBUG_THREADS
49 extern int debug_mpthreads;
50 #endif
52 PDECLARE_CLASS(PHouseKeepingThread, PThread)
53 public:
54 PHouseKeepingThread()
55 : PThread(1000, NoAutoDeleteThread, NormalPriority, "Housekeeper")
56 { closing = FALSE; Resume(); }
58 void Main();
59 void SetClosing() { closing = TRUE; }
61 protected:
62 BOOL closing;
66 #define new PNEW
69 int PThread::PXBlockOnIO(int handle, int type, const PTimeInterval & timeout)
71 //PTRACE(1,"PThread::PXBlockOnIO(" << handle << ',' << type << ')');
73 // make sure we flush the buffer before doing a write
74 fd_set tmp_rfd, tmp_wfd, tmp_efd;
75 fd_set * read_fds = &tmp_rfd;
76 fd_set * write_fds = &tmp_wfd;
77 fd_set * exception_fds = &tmp_efd;
79 struct timeval * tptr = NULL;
80 struct timeval timeout_val;
81 if (timeout != PMaxTimeInterval) {
82 static const PTimeInterval oneDay(0, 0, 0, 0, 1);
83 if (timeout < oneDay) {
84 timeout_val.tv_usec = (timeout.GetMilliSeconds() % 1000) * 1000;
85 timeout_val.tv_sec = timeout.GetSeconds();
86 tptr = &timeout_val;
90 int retval;
92 for (;;) {
94 FD_ZERO (read_fds);
95 FD_ZERO (write_fds);
96 FD_ZERO (exception_fds);
98 switch (type) {
99 case PChannel::PXReadBlock:
100 case PChannel::PXAcceptBlock:
101 FD_SET (handle, read_fds);
102 break;
103 case PChannel::PXWriteBlock:
104 FD_SET (handle, write_fds);
105 break;
106 case PChannel::PXConnectBlock:
107 FD_SET (handle, write_fds);
108 FD_SET (handle, exception_fds);
109 break;
110 default:
111 PAssertAlways(PLogicError);
112 return 0;
115 // include the termination pipe into all blocking I/O functions
116 int width = handle+1;
117 FD_SET(unblockPipe[0], read_fds);
118 width = PMAX(width, unblockPipe[0]+1);
120 retval = ::select(width, read_fds, write_fds, exception_fds, tptr);
122 if ((retval >= 0) || (errno != EINTR))
123 break;
126 if ((retval == 1) && FD_ISSET(unblockPipe[0], read_fds)) {
127 BYTE ch;
128 ::read(unblockPipe[0], &ch, 1);
129 errno = EINTR;
130 retval = -1;
131 //PTRACE(1,"Unblocked I/O");
134 return retval;
137 void PThread::PXAbortBlock() const
139 BYTE ch;
140 ::write(unblockPipe[1], &ch, 1);
144 // For Mac OS, the housekeeping thread has two jobs:
145 // First, poll for synchronous signals (as is traditional), and
146 // second, to poll the MPThread termination notification queue and clean up
147 // deceased PThreads.
148 // There is an ickiness here which depends on a current restriction of
149 // Mac OS X: synchronous signals (i.e. not signals resulting from
150 // exceptions) are only delivered to the main thread. I assume that
151 // it is therefore safe for the main thread to call MPNotifyQueue from
152 // a signal handler if and only if the main thread never calls MPNotifyQueue
153 // on the termination notification queue from its main code. This ought to
154 // be acceptable if notifying a queue is single-threaded per queue; if
155 // MPNotifyQueue has a global critical section, this will work very badly.
157 static MPQueueID terminationNotificationQueue = 0;
158 // This bites. Threads don't know what process they come from (even though
159 // there can be only one), yet when we're winding down the process thread
160 // kills the housekeeper before it can clean up all the other threads.
161 // So the process thread has to poll the termination queue, but it does so
162 // from PThread context, so it can't know there's no housekeeper. yuck.
163 static BOOL noHousekeeper = 0;
165 static void SetUpTermQueue()
167 OSStatus err;
168 // Let us PLEASE try not to get into any "create/delete" loops here.
169 // Create it and be DONE with it.
170 while (!terminationNotificationQueue) {
171 MPQueueID tempQueue;
172 err = MPCreateQueue(&tempQueue);
173 PAssert(err == noErr, "MPCreateQueue failed");
174 // When Motorola finally finishes the 620, there's a lot of Mac code
175 // gonna need to be rewritten.
176 // HAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA!!!
177 // "finishes the 620". hee hee hee!
178 if (!OTCompareAndSwap32(0, (UInt32)tempQueue,
179 (UInt32*)&terminationNotificationQueue)) {
180 // then someone else snuck in and initialized it.
181 MPDeleteQueue(tempQueue);
182 } else {
183 #ifdef DEBUG_THREADS
184 if (debug_mpthreads)
185 fprintf(stderr,"set up notification queue\n");
186 #endif
187 // XXX MPNotifyQueue is perfectly willing to allocate memory
188 // XXX for the items dropped in the queue. However, if it can't,
189 // XXX then life just goes on -- and we miss a thread exit message.
190 // XXX If we reserve queue space, however, then we guarantee two
191 // XXX things: 1, we absolutely will be able to receive N
192 // XXX notifications, and 2, we absolutely will drop the N+1st
193 // XXX on the floor, spare memory or no. The target applications
194 // XXX for this library do not appear (currently) to generate
195 // XXX absurd numbers of threads, so I'll reserve an absurd number
196 // XXX of messages, and pretend that nothing can go wrong.
197 // XXX n go wrong.n go wrong.n go wrong.n go wrong.n go wrong.
198 // XXX If the following fails, it's probably for lack of memory,
199 // XXX in which case the queue will just try dynamic allocation.
200 (void)MPSetQueueReserve(terminationNotificationQueue, 128);
205 static BOOL PollNotificationQueue(Duration timeout)
207 OSStatus err = noErr;
208 void *parm1, *parm2, *parm3;
210 err = MPWaitOnQueue(terminationNotificationQueue,
211 &parm1, &parm2, &parm3,
212 timeout);
213 if (err == noErr) {
214 // then we got a notification
215 if ((int)parm1 == 1) {
216 // then it was a thread death notification, parm2 is
217 // the PThread pointer
218 #ifdef DEBUG_THREADS
219 if (debug_mpthreads)
220 fprintf(stderr,"notified of %p death\n", parm2);
221 #endif
222 PThread::PX_ThreadEnd(parm2);
223 } // else parm1 == 0 and it's just a wakeup notice
225 return err == noErr;
228 void PHouseKeepingThread::Main()
230 PProcess & process = PProcess::Current();
232 SetUpTermQueue();
234 while (!closing) {
235 PTimeInterval waitTime = process.timers.Process();
237 Duration timeout;
238 if (waitTime == PMaxTimeInterval)
239 timeout = kDurationForever;
240 else {
241 // "Values of type Duration are 32 bits long. They are intepreted
242 // in a manner consistend with the Time Manager -- positive values
243 // are in units of milliseconds, negative values are in units of
244 // microseconds."
245 // Fortunately, PMaxTimeInterval is limited to a positive 32-bit
246 // number of milliseconds.
247 timeout = (long)waitTime.GetMilliSeconds();
250 // Block on the notification queue
252 (void)PollNotificationQueue(timeout);
254 // whether we timed out or got notified, check the signals.
255 process.PXCheckSignals();
257 noHousekeeper = 1;
258 #ifdef DEBUG_THREADS
259 if (debug_mpthreads)
260 fprintf(stderr,"housekeeper exiting\n");
261 #endif
265 void PProcess::Construct()
267 // set the file descriptor limit to something sensible
268 struct rlimit rl;
269 PAssertOS(getrlimit(RLIMIT_NOFILE, &rl) == 0);
270 rl.rlim_cur = rl.rlim_max;
271 PAssertOS(setrlimit(RLIMIT_NOFILE, &rl) == 0);
273 SetUpTermQueue();
275 // initialise the housekeeping thread
276 housekeepingThread = NULL;
278 CommonConstruct();
282 PProcess::~PProcess()
284 // Don't wait for housekeeper to stop if Terminate() is called from it.
285 if (housekeepingThread != NULL && PThread::Current() != housekeepingThread) {
286 housekeepingThread->SetClosing();
287 SignalTimerChange();
288 housekeepingThread->WaitForTermination();
289 delete housekeepingThread;
290 housekeepingThread = 0;
292 // XXX try to gracefully handle shutdown transient where the housekeeping
293 // XXX thread hasn't managed to clean up all the threads
294 while (PollNotificationQueue(kDurationImmediate)) ;
296 CommonDestruct();
300 PThread::PThread()
302 // see InitialiseProcessThread()
306 void PThread::InitialiseProcessThread()
308 OSStatus err = 0;
309 PX_origStackSize = 0;
310 autoDelete = FALSE;
311 PX_threadId = MPCurrentTaskID();
312 PX_suspendCount = 0;
314 ::pipe(unblockPipe);
316 // Sadly, Mac OS MPThreads can't just initialize a block of memory into
317 // an MPSemaphore (XXX ought to be a CriticalRegion, but they're broken
318 // in Mac OS X 10.0.x!)
319 PX_suspendMutex = 0;
320 if ((err = MPCreateSemaphore(1,1,&PX_suspendMutex))
321 != 0) {
322 PAssertOS(err == 0);
323 throw std::bad_alloc();
326 ((PProcess *)this)->activeThreads.DisallowDeleteObjects();
327 ((PProcess *)this)->activeThreads.SetAt((unsigned)PX_threadId, this);
331 PThread::PThread(PINDEX stackSize,
332 AutoDeleteFlag deletion,
333 Priority /*priorityLevel*/,
334 const PString & name)
335 : threadName(name), PX_signature(kMPThreadSig)
337 OSStatus err = 0;
339 PAssert(stackSize > 0, PInvalidParameter);
341 PX_origStackSize = stackSize;
342 autoDelete = (deletion == AutoDeleteThread);
344 // Sadly, Mac OS MPThreads can't just initialize a block of memory into
345 // an MPSemaphore (XXX ought to be a CriticalRegion, but they're broken
346 // in Mac OS X 10.0.x!)
347 PX_suspendMutex = 0;
348 if ((err = MPCreateSemaphore(1,1,&PX_suspendMutex)) != 0) {
349 PAssert(err == 0, "MPCreateSemaphore failed");
350 throw std::bad_alloc();
353 ::pipe(unblockPipe);
355 // throw the new thread
356 PX_NewThread(TRUE);
360 PThread::~PThread()
362 if (!IsTerminated())
363 Terminate();
365 ::close(unblockPipe[0]);
366 ::close(unblockPipe[1]);
368 if (PX_suspendMutex)
369 MPDeleteSemaphore(PX_suspendMutex);
370 #ifdef DEBUG_THREADS
371 if (debug_mpthreads)
372 fprintf(stderr,"thread %p destructing\n", this);
373 #endif
374 PX_signature = kMPDeadSig;
377 void PThread::PX_NewThread(BOOL startSuspended)
379 OSErr err;
380 // initialise suspend counter and create mutex
381 PX_suspendCount = startSuspended ? 1 : 0;
383 // initialise Suspend/Resume semaphore (for Mac OS X)
384 // XXX The MPThread manager allows for starting tasks "suspended", but I
385 // XXX suspect that only works if you have a debugger registered.
386 suspend_semaphore = new PSemaphore(0,1);
388 // throw the thread
390 SetUpTermQueue();
392 // create the task.
393 #ifdef DEBUG_THREADS
394 if (debug_mpthreads)
395 fprintf(stderr,"thread %p being started\n", (void *)this);
396 #endif
397 err = MPCreateTask( (TaskProc)PX_ThreadStart, (void*)this,
398 65536, // stacksize
399 terminationNotificationQueue,
400 (void *)1, // param 1 == "death"
401 (void *)this, // param 2 == "PThread to clean up"
402 0, // no options
403 &PX_threadId);
404 PAssert(err == 0, "MPCreateTask failed");
405 if (err) throw std::bad_alloc();
409 long PThread::PX_ThreadStart(void * arg)
411 MPTaskID threadId = MPCurrentTaskID();
413 // self-detach (no need)
415 PThread * thread = (PThread *)arg;
416 thread->SetThreadName(thread->GetThreadName());
418 PProcess & process = PProcess::Current();
420 // add thread to thread list
421 process.threadMutex.Wait();
422 process.activeThreads.SetAt((unsigned)threadId, thread);
423 process.threadMutex.Signal();
425 // if we are not supposed to start suspended, then don't wait
426 // if we are supposed to start suspended, then wait for a resume
428 if (thread->PX_suspendCount != 0) {
429 thread->suspend_semaphore->Wait(); // Wait for the Resume
432 // now call the the thread main routine
433 //PTRACE(1, "tlibthrd\tAbout to call Main");
434 thread->Main();
436 #ifdef DEBUG_THREADS
437 if (debug_mpthreads)
438 fprintf(stderr,"thread %p returning\n", thread);
439 #endif
440 return 0;
444 void PProcess::SignalTimerChange()
446 if (housekeepingThread == NULL) {
447 #if PMEMORY_CHECK
448 BOOL oldIgnoreAllocations = PMemoryHeap::SetIgnoreAllocations(TRUE);
449 #endif
450 housekeepingThread = new PHouseKeepingThread;
451 #if PMEMORY_CHECK
452 PMemoryHeap::SetIgnoreAllocations(oldIgnoreAllocations);
453 #endif
456 SetUpTermQueue();
457 MPNotifyQueue(terminationNotificationQueue, 0, 0, 0);
461 void PThread::PX_ThreadEnd(void * arg)
463 PThread * thread = (PThread *)arg;
464 PProcess & process = PProcess::Current();
466 MPTaskID id = thread->PX_GetThreadId();
467 if (id != 0) {
469 // remove this thread from the active thread list
470 process.threadMutex.Wait();
471 process.activeThreads.SetAt((unsigned)id, NULL);
472 process.threadMutex.Signal();
475 // delete the thread if required, note this is done this way to avoid
476 // a race condition, the thread ID cannot be zeroed before the if!
477 if (thread->autoDelete) {
478 thread->PX_threadId = 0; // Prevent terminating terminated thread
479 delete thread;
481 else
482 thread->PX_threadId = 0;
486 MPTaskID PThread::PX_GetThreadId() const
488 return PX_threadId;
492 void PThread::Restart()
494 if (IsTerminated())
495 return;
497 PX_NewThread(FALSE);
501 void PThread::Terminate()
503 if (PX_origStackSize <= 0)
504 return;
506 if (IsTerminated())
507 return;
509 PTRACE(1, "tlibthrd\tForcing termination of thread " << (void *)this);
511 if (Current() == this)
512 MPExit(0);
513 else {
514 MPTaskID taskId = PX_threadId;
515 WaitForTermination();
516 // XXX Dire Consequences[TM] are warned of when one uses MPTerminateTask.
517 // XXX However, the same Dire Consequences are predicted (I think) for
518 // XXX pthread_kill which the PWLIB code already uses.
519 // XXX However, the only thing the cleanup function does is removes the
520 // XXX thread from the thread table, which is already performed by the
521 // XXX housekeeping thread; PWLIB doesn't try to salvage locks or
522 // XXX anything clever like that.
523 // XXX I just hope taskIds aren't quickly reused.
524 if (taskId != 0)
525 (void)MPTerminateTask(taskId, kMPTaskAbortedErr);
530 void PThread::PXSetWaitingSemaphore(PSemaphore * sem)
532 // not needed
536 BOOL PThread::IsTerminated() const
538 if (PX_threadId == 0) {
539 //PTRACE(1, "tlibthrd\tIsTerminated(" << (void *)this << ") = 0");
540 return TRUE;
543 #ifdef _not_def_ // Sigh. no MPGetNextTaskID on MOSX
544 // This seems like a silly way to do this, but I think it might work.
545 // The end condition for MPGetNextTaskID isn't documented, so I try both
546 // logical possibilities.
547 MPTaskID sometask = 0;
548 MPProcessID myproc = 0;
549 while (MPGetNextTaskID(myproc, &sometask) == noErr) {
550 if (sometask == 0) break;
551 if (sometask == PX_threadId) {
552 //PTRACE(1, "tlibthrd\tIsTerminated(" << (void *)this << ") not dead yet");
553 return FALSE;
556 // didn't find it, it's dead
557 //PTRACE(1, "tlibthrd\tIsTerminated(" << (void *)this << ") = 0");
558 return TRUE;
559 #else
560 return FALSE; // ENOCLUE
561 #endif
564 // Mac OS X and Darwin 1.2 does not support pthread_kill() or sigwait()
565 // so we cannot implement suspend and resume using signals. Instead we have a
566 // partial implementation using a Semaphore.
567 // As a result, we can create a thread in a suspended state and then 'resume'
568 // it, but once it is going, we can no longer suspend it.
569 // So, for Mac OS X, we will accept Resume() calls (or Suspend(FALSE))
570 // but reject Suspend(TRUE) calls with an Assertion. This will indicate
571 // to a user that we cannot Suspend threads on Mac OS X
573 void PThread::Suspend(BOOL susp)
575 OSStatus err;
576 err = MPWaitOnSemaphore(PX_suspendMutex,kDurationForever);
577 PAssert(err == 0, "MPWaitOnSemaphore failed");
579 if (susp) {
580 // Suspend - warn the user with an Assertion
581 PAssertAlways("Cannot suspend threads on Mac OS X due to lack of pthread_kill()");
584 // if resuming, then see if to really resume
585 else if (PX_suspendCount > 0) {
586 PX_suspendCount--;
587 if (PX_suspendCount == 0) {
588 suspend_semaphore->Signal();
592 err = MPSignalSemaphore(PX_suspendMutex);
593 PAssert( err == 0, "MPSignalSemaphore failed");
596 void PThread::Resume()
598 Suspend(FALSE);
602 BOOL PThread::IsSuspended() const
604 OSStatus err;
606 if (IsTerminated())
607 return FALSE;
609 err = MPWaitOnSemaphore(PX_suspendMutex, kDurationForever);
610 PAssert(err == 0, "MPWaitOnSemaphore failed");
611 BOOL suspended = PX_suspendCount > 0;
612 err = MPSignalSemaphore(PX_suspendMutex);
613 PAssert(err == 0, "MPSignalSemaphore failed");
614 return suspended;
618 void PThread::SetAutoDelete(AutoDeleteFlag deletion)
620 PAssert(deletion != AutoDeleteThread || this != &PProcess::Current(), PLogicError);
621 autoDelete = deletion == AutoDeleteThread;
625 void PThread::SetPriority(Priority /*priorityLevel*/)
630 PThread::Priority PThread::GetPriority() const
632 return LowestPriority;
636 void PThread::Yield()
638 ::sleep(0);
642 PThread * PThread::Current()
644 PProcess & process = PProcess::Current();
645 process.threadMutex.Wait();
646 PThread * thread = process.activeThreads.GetAt((unsigned)MPCurrentTaskID());
647 process.threadMutex.Signal();
648 return PAssertNULL(thread);
652 void PThread::Sleep(const PTimeInterval & timeout)
654 AbsoluteTime expiry;
655 Duration delta = kDurationForever;
657 if (timeout != PMaxTimeInterval) {
658 delta = timeout.GetMilliSeconds();
660 expiry = AddDurationToAbsolute(delta, UpTime());
662 (void)MPDelayUntil(&expiry);
666 void PThread::WaitForTermination() const
668 PAssert(Current() != this, "Waiting for self termination!");
670 PXAbortBlock();
672 while (!IsTerminated()) {
673 PAssert(PX_signature == kMPThreadSig, "bad signature in living thread");
674 Current()->Sleep(10);
675 #ifdef DEBUG_THREADS
676 if (debug_mpthreads)
677 fprintf(stderr,"spinning for termination of thread %p\n", (void *)this);
678 #endif
679 if (noHousekeeper) PollNotificationQueue(kDurationImmediate);
684 BOOL PThread::WaitForTermination(const PTimeInterval & maxWait) const
686 PAssert(Current() != this, "Waiting for self termination!");
688 //PTRACE(1, "tlibthrd\tWaitForTermination(delay)");
689 PXAbortBlock();
691 PTimer timeout = maxWait;
692 while (!IsTerminated()) {
693 if (timeout == 0)
694 return FALSE;
695 Current()->Sleep(10);
697 return TRUE;
701 ///////////////////////////////////////////////////////////////////////////////
703 PSemaphore::PSemaphore(unsigned initial, unsigned maxCount)
705 OSStatus err = MPCreateSemaphore(maxCount, initial, &semId);
706 PAssert(err == 0, "MPCreateSemaphore failed");
707 PAssert((long)semId != 0 && (long)semId != -1, "stupid semId");
711 PSemaphore::~PSemaphore()
713 OSStatus err = MPDeleteSemaphore(semId);
714 PAssert(err == 0, "MPDeleteSemaphore failed");
715 *(long *)&semId = -1;
719 void PSemaphore::Wait()
721 assert((long)semId != 0);
722 assert((long)semId != -1);
724 PAssert((long)semId != -1, "wait on destructed PSemaphore");
725 PAssert((long)semId != 0, "semId stomped");
726 OSStatus err = MPWaitOnSemaphore(semId, kDurationForever);
727 PAssert(err == 0, "MPWaitOnSemaphore failed");
731 BOOL PSemaphore::Wait(const PTimeInterval & waitTime)
733 OSErr err = 0;
735 if (waitTime == PMaxTimeInterval) {
736 Wait();
737 return TRUE;
740 Duration timeout = waitTime.GetMilliSeconds();
741 if ((err = MPWaitOnSemaphore(semId, timeout)) == noErr)
742 return TRUE;
743 if (err == kMPTimeoutErr)
744 return FALSE;
745 PAssert(err == 0, psprintf("timed wait error = %i", err));
746 return FALSE;
749 void PSemaphore::Signal()
751 OSStatus err = MPSignalSemaphore(semId);
752 // was it already signalled?
753 if (err == kMPInsufficientResourcesErr) err = 0;
754 PAssert(err == 0, "MPSignalSemaphore failed");
758 BOOL PSemaphore::WillBlock() const
760 OSStatus err = MPWaitOnSemaphore(semId, kDurationImmediate);
761 if (err == kMPTimeoutErr)
762 return TRUE;
763 PAssert(err == 0, psprintf("timed wait error = %i", err));
764 (void)MPSignalSemaphore(semId);
765 return FALSE;
768 // Ideally, a PMutex would contain an MPCriticalSection instead of a
769 // semaphore, but the class derivation is outside the machine-specific
770 // code, and I'm unwilling to do something gross like implement a bogus
771 // constructor for PSemaphore which doesn't allocate a semaphore.
773 PMutex::PMutex()
774 : PSemaphore(1, 1)
778 void PMutex::Wait()
780 PSemaphore::Wait();
783 BOOL PMutex::Wait(const PTimeInterval & timeout)
785 return PSemaphore::Wait(timeout);
788 void PMutex::Signal()
790 PSemaphore::Signal();
793 BOOL PMutex::WillBlock() const
795 return PSemaphore::WillBlock();
798 PSyncPoint::PSyncPoint()
799 : PSemaphore(0, 1)