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
Родитель 00e8855b3f
Коммит a9e69202d2
5 изменённых файлов: 140 добавлений и 34 удалений

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -226,18 +226,7 @@ void _MD_PauseCPU(PRIntervalTime timeout)
{ {
if (timeout != PR_INTERVAL_NO_WAIT) if (timeout != PR_INTERVAL_NO_WAIT)
{ {
EventRecord theEvent; WaitOnIdleSemaphore(timeout);
/*
** 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);
(void) _MD_IOInterrupt(); (void) _MD_IOInterrupt();
} }
} }
@ -528,19 +517,25 @@ void _MD_SetIntsOff(PRInt32 ints)
#pragma mark - #pragma mark -
#pragma mark CRITICAL REGION SUPPORT #pragma mark CRITICAL REGION SUPPORT
static PRBool RunningOnOSX()
{
long systemVersion;
OSErr err = Gestalt(gestaltSystemVersion, &systemVersion);
return (err == noErr) && (systemVersion >= 0x00001000);
}
#if MAC_CRITICAL_REGIONS #if MAC_CRITICAL_REGIONS
MDCriticalRegionID gCriticalRegion; MDCriticalRegionID gCriticalRegion;
void InitCriticalRegion() void InitCriticalRegion()
{ {
long systemVersion;
OSStatus err; OSStatus err;
// we only need to do critical region stuff on Mac OS X // we only need to do critical region stuff on Mac OS X
err = Gestalt(gestaltSystemVersion, &systemVersion); gUseCriticalRegions = RunningOnOSX();
gUseCriticalRegions = (err == noErr) && (systemVersion >= 0x00001000);
if (!gUseCriticalRegions) return; if (!gUseCriticalRegions) return;
err = MD_CriticalRegionCreate(&gCriticalRegion); err = MD_CriticalRegionCreate(&gCriticalRegion);
@ -586,3 +581,90 @@ void LeaveCritialRegion()
#endif // MAC_CRITICAL_REGIONS #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);
}
}

Просмотреть файл

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