зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
00e8855b3f
Коммит
a9e69202d2
|
@ -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();
|
||||||
|
|
Загрузка…
Ссылка в новой задаче