Fix #99561 (on NSPR tip). Use MPSemaphore calls rather than WaitNextEvent to pause CPU under Mac OS X - fixes thread deadlock and improves performance. r=wtc,sr=sfraser,a=dbaron

This commit is contained in:
sdagley%netscape.com 2002-01-22 22:13:03 +00:00
parent 058b857938
commit c974647b28
5 changed files with 140 additions and 34 deletions

View File

@ -681,6 +681,18 @@ extern void LeaveCritialRegion();
#endif
/*
* CPU Idle support
*/
extern void InitIdleSemaphore();
extern void TermIdleSemaphore();
extern void WaitOnIdleSemaphore();
extern void SignalIdleSemaphore();
/*
* Atomic operations
*/

View File

@ -81,15 +81,17 @@ static void AsyncIOCompletion (ExtendedParamBlock *pbAsyncPtr)
if (_PR_MD_GET_INTSOFF()) {
thread->md.missedIONotify = PR_TRUE;
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
return;
}
else {
_PR_INTSOFF(is);
thread->md.osErrCode = noErr;
DoneWaitingOnThisThread(thread);
_PR_FAST_INTSON(is);
}
_PR_INTSOFF(is);
thread->md.osErrCode = noErr;
DoneWaitingOnThisThread(thread);
_PR_FAST_INTSON(is);
SignalIdleSemaphore();
}
void _MD_SetError(OSErr oserror)

View File

@ -173,9 +173,9 @@ static pascal void DNSNotifierRoutine(void * contextPtr, OTEventCode otEvent, O
if (_PR_MD_GET_INTSOFF()) {
dnsContext.thread->md.missedIONotify = PR_TRUE;
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
return;
}
DoneWaitingOnThisThread(dnsContext.thread);
else
DoneWaitingOnThisThread(dnsContext.thread);
break;
case kOTProviderWillClose:
@ -189,9 +189,10 @@ static pascal void DNSNotifierRoutine(void * contextPtr, OTEventCode otEvent, O
if (_PR_MD_GET_INTSOFF()) {
dnsContext.thread->md.missedIONotify = PR_TRUE;
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
return;
}
DoneWaitingOnThisThread(dnsContext.thread);
else {
DoneWaitingOnThisThread(dnsContext.thread);
}
break;
default: // or else we don't handle the event
@ -199,6 +200,8 @@ static pascal void DNSNotifierRoutine(void * contextPtr, OTEventCode otEvent, O
}
// or else we don't handle the event
SignalIdleSemaphore();
}
@ -296,10 +299,13 @@ WakeUpNotifiedThread(PRThread *thread, OTResult result)
if (_PR_MD_GET_INTSOFF()) {
thread->md.missedIONotify = PR_TRUE;
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
return;
}
DoneWaitingOnThisThread(thread);
else {
DoneWaitingOnThisThread(thread);
}
}
SignalIdleSemaphore();
}
// Notification routine
@ -1169,10 +1175,13 @@ static pascal void RawEndpointNotifierRoutine(void * contextPtr, OTEventCode co
if (_PR_MD_GET_INTSOFF()) {
thread->md.asyncNotifyPending = PR_TRUE;
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
return;
}
DoneWaitingOnThisThread(thread);
else {
DoneWaitingOnThisThread(thread);
}
}
SignalIdleSemaphore();
}
PRInt32 _MD_accept(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
@ -1583,7 +1592,6 @@ static PRInt32 SendReceiveDgram(PRFileDesc *fd, void *buf, PRInt32 amount,
PRThread *me = _PR_MD_CURRENT_THREAD();
PRInt32 bytesLeft = amount;
TUnitData dgram;
OTResult result;
PR_ASSERT(flags == 0);
@ -1618,13 +1626,13 @@ static PRInt32 SendReceiveDgram(PRFileDesc *fd, void *buf, PRInt32 amount,
fd->secret->md.write.thread = me;
fd->secret->md.writeReady = PR_FALSE; // expect the worst
err = OTSndUData(endpoint, &dgram);
if (result != kOTFlowErr) // hope for the best
if (err != kOTFlowErr) // hope for the best
fd->secret->md.writeReady = PR_TRUE;
} else {
fd->secret->md.read.thread = me;
fd->secret->md.readReady = PR_FALSE; // expect the worst
err = OTRcvUData(endpoint, &dgram, NULL);
if (result != kOTNoDataErr) // hope for the best
if (err != kOTNoDataErr) // hope for the best
fd->secret->md.readReady = PR_TRUE;
}

View File

@ -226,18 +226,7 @@ void _MD_PauseCPU(PRIntervalTime timeout)
{
if (timeout != PR_INTERVAL_NO_WAIT)
{
EventRecord theEvent;
/*
** Calling WaitNextEvent() here is suboptimal. This routine should
** pause the process until IO or the timeout occur, yielding time to
** other processes on operating systems that require this (Mac OS classic).
** WaitNextEvent() may incur too much latency, and has other problems,
** such as the potential to drop suspend/resume events, and to handle
** AppleEvents at a time at which we're not prepared to handle them.
*/
(void) WaitNextEvent(nullEvent, &theEvent, 1, NULL);
WaitOnIdleSemaphore(timeout);
(void) _MD_IOInterrupt();
}
}
@ -528,19 +517,25 @@ void _MD_SetIntsOff(PRInt32 ints)
#pragma mark -
#pragma mark CRITICAL REGION SUPPORT
static PRBool RunningOnOSX()
{
long systemVersion;
OSErr err = Gestalt(gestaltSystemVersion, &systemVersion);
return (err == noErr) && (systemVersion >= 0x00001000);
}
#if MAC_CRITICAL_REGIONS
MDCriticalRegionID gCriticalRegion;
void InitCriticalRegion()
{
long systemVersion;
OSStatus err;
// we only need to do critical region stuff on Mac OS X
err = Gestalt(gestaltSystemVersion, &systemVersion);
gUseCriticalRegions = (err == noErr) && (systemVersion >= 0x00001000);
gUseCriticalRegions = RunningOnOSX();
if (!gUseCriticalRegions) return;
err = MD_CriticalRegionCreate(&gCriticalRegion);
@ -586,3 +581,90 @@ void LeaveCritialRegion()
#endif // MAC_CRITICAL_REGIONS
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark IDLE SEMAPHORE SUPPORT
/*
Since the WaitNextEvent() in _MD_PauseCPU() is causing all sorts of
headache under Mac OS X we're going to switch to MPWaitOnSemaphore()
which should do what we want
*/
#if TARGET_CARBON
PRBool gUseIdleSemaphore = PR_FALSE;
MPSemaphoreID gIdleSemaphore = NULL;
#endif
ProcessSerialNumber gApplicationProcess;
void InitIdleSemaphore()
{
// we only need to do idle semaphore stuff on Mac OS X
#if TARGET_CARBON
gUseIdleSemaphore = RunningOnOSX();
if (gUseIdleSemaphore)
{
OSStatus err = MPCreateSemaphore(1 /* max value */, 0 /* initial value */, &gIdleSemaphore);
PR_ASSERT(err == noErr);
}
else
#endif
{
GetCurrentProcess(&gApplicationProcess);
}
}
void TermIdleSemaphore()
{
#if TARGET_CARBON
if (gUseIdleSemaphore)
{
OSStatus err = MPDeleteSemaphore(gIdleSemaphore);
PR_ASSERT(err == noErr);
gUseIdleSemaphore = NULL;
}
#endif
}
void WaitOnIdleSemaphore(PRIntervalTime timeout)
{
#if TARGET_CARBON
if (gUseIdleSemaphore)
{
OSStatus err = MPWaitOnSemaphore(gIdleSemaphore, kDurationMillisecond * PR_IntervalToMilliseconds(timeout));
PR_ASSERT(err == noErr);
}
else
#endif
{
EventRecord theEvent;
/*
** Calling WaitNextEvent() here is suboptimal. This routine should
** pause the process until IO or the timeout occur, yielding time to
** other processes on operating systems that require this (Mac OS classic).
** WaitNextEvent() may incur too much latency, and has other problems,
** such as the potential to drop suspend/resume events.
*/
(void)WaitNextEvent(nullEvent, &theEvent, 1, NULL);
}
}
void SignalIdleSemaphore()
{
#if TARGET_CARBON
if (gUseIdleSemaphore)
{
// often we won't be waiting on the semaphore here, so ignore any errors
(void)MPSignalSemaphore(gIdleSemaphore);
}
else
#endif
{
WakeUpProcess(&gApplicationProcess);
}
}

View File

@ -289,6 +289,7 @@ void _MD_EarlyInit()
Handle environmentVariables;
INIT_CRITICAL_REGION();
InitIdleSemaphore();
#if !defined(MAC_NSPR_STANDALONE)
// MacintoshInitializeMemory(); Moved to mdmacmem.c: AllocateRawMemory(Size blockSize)
@ -376,6 +377,7 @@ void CleanupTermProc(void)
_MD_StopInterrupts(); // deactive Time Manager task
CLOSE_OPEN_TRANSPORT();
TermIdleSemaphore();
TERM_CRITICAL_REGION();
__NSTerminate();