зеркало из https://github.com/mozilla/gecko-dev.git
Fix for bug 71718. Make Mac NSPR work on dual CPU Mac OS X macines by using MP critical regions to fix threading synchronization problems. Uses hand-rolled critical regions because the critical section API is broke on Mac OS 10.0.x. r=gordon, wtc.
This commit is contained in:
Родитель
9104b49a5c
Коммит
821e14b019
Двоичные данные
nsprpub/macbuild/NSPR20PPC.mcp
Двоичные данные
nsprpub/macbuild/NSPR20PPC.mcp
Двоичный файл не отображается.
|
@ -138,6 +138,23 @@ struct _MDFileDesc {
|
||||||
** Interrupts Related definitions
|
** Interrupts Related definitions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _MD_GET_INTSOFF() (_pr_intsOff)
|
||||||
|
|
||||||
|
#define _MD_INTSOFF(_is) \
|
||||||
|
PR_BEGIN_MACRO \
|
||||||
|
ENTER_CRITICAL_REGION(); \
|
||||||
|
(_is) = _PR_MD_GET_INTSOFF(); \
|
||||||
|
_PR_MD_SET_INTSOFF(1); \
|
||||||
|
LEAVE_CRITICAL_REGION(); \
|
||||||
|
PR_END_MACRO
|
||||||
|
|
||||||
|
#if TARGET_CARBON
|
||||||
|
extern void _MD_SetIntsOff(PRInt32 ints);
|
||||||
|
#define _MD_SET_INTSOFF(_val) _MD_SetIntsOff(_val)
|
||||||
|
#else /* not TARGET_CARBON */
|
||||||
|
#define _MD_SET_INTSOFF(_val) (_pr_intsOff = _val)
|
||||||
|
#endif /* TARGET_CARBON */
|
||||||
|
|
||||||
#define _MD_START_INTERRUPTS _MD_StartInterrupts
|
#define _MD_START_INTERRUPTS _MD_StartInterrupts
|
||||||
#define _MD_STOP_INTERRUPTS _MD_StopInterrupts
|
#define _MD_STOP_INTERRUPTS _MD_StopInterrupts
|
||||||
#define _MD_BLOCK_CLOCK_INTERRUPTS()
|
#define _MD_BLOCK_CLOCK_INTERRUPTS()
|
||||||
|
@ -238,6 +255,8 @@ extern PRStatus _MD_InitThread(PRThread *thread);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Initialize the thread context preparing it to execute _main.
|
** Initialize the thread context preparing it to execute _main.
|
||||||
|
** *sp = 0 zeros out the sp for the first stack frame so that
|
||||||
|
** stack walking code can find the top of the stack.
|
||||||
*/
|
*/
|
||||||
#if defined(powerc) || defined(__powerc)
|
#if defined(powerc) || defined(__powerc)
|
||||||
#define _MD_INIT_CONTEXT(_thread, _sp, _main, _status) \
|
#define _MD_INIT_CONTEXT(_thread, _sp, _main, _status) \
|
||||||
|
@ -248,6 +267,7 @@ extern PRStatus _MD_InitThread(PRThread *thread);
|
||||||
*((PRBool *)_status) = PR_TRUE; \
|
*((PRBool *)_status) = PR_TRUE; \
|
||||||
(void) setjmp(jb); \
|
(void) setjmp(jb); \
|
||||||
sp = INIT_STACKPTR(_sp); \
|
sp = INIT_STACKPTR(_sp); \
|
||||||
|
*sp = 0; \
|
||||||
(_MD_GET_SP(_thread)) = (long) sp; \
|
(_MD_GET_SP(_thread)) = (long) sp; \
|
||||||
tvect = (unsigned long *)_main; \
|
tvect = (unsigned long *)_main; \
|
||||||
(_MD_GET_PC(_thread)) = (int) *tvect; \
|
(_MD_GET_PC(_thread)) = (int) *tvect; \
|
||||||
|
@ -627,4 +647,36 @@ extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap);
|
||||||
extern void SetLogFileTypeCreator(const char *logFile);
|
extern void SetLogFileTypeCreator(const char *logFile);
|
||||||
extern int _MD_mac_get_nonblocking_connect_error(PRInt32 osfd);
|
extern int _MD_mac_get_nonblocking_connect_error(PRInt32 osfd);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Critical section support
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MAC_CRITICAL_REGIONS TARGET_CARBON
|
||||||
|
|
||||||
|
#if MAC_CRITICAL_REGIONS
|
||||||
|
|
||||||
|
extern void InitCriticalRegion();
|
||||||
|
extern void TermCriticalRegion();
|
||||||
|
|
||||||
|
extern void EnterCritialRegion();
|
||||||
|
extern void LeaveCritialRegion();
|
||||||
|
|
||||||
|
#define INIT_CRITICAL_REGION() InitCriticalRegion()
|
||||||
|
#define TERM_CRITICAL_REGION() TermCriticalRegion()
|
||||||
|
|
||||||
|
#define ENTER_CRITICAL_REGION() EnterCritialRegion()
|
||||||
|
#define LEAVE_CRITICAL_REGION() LeaveCritialRegion()
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define INIT_CRITICAL_REGION()
|
||||||
|
#define TERM_CRITICAL_REGION()
|
||||||
|
|
||||||
|
#define ENTER_CRITICAL_REGION()
|
||||||
|
#define LEAVE_CRITICAL_REGION()
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#endif /* prmacos_h___ */
|
#endif /* prmacos_h___ */
|
||||||
|
|
|
@ -328,8 +328,10 @@ NSPR_API(PRInt32) _pr_intsOff;
|
||||||
#define _MD_LAST_THREAD() (_pr_lastThread)
|
#define _MD_LAST_THREAD() (_pr_lastThread)
|
||||||
#define _MD_SET_LAST_THREAD(t) (_pr_lastThread = t)
|
#define _MD_SET_LAST_THREAD(t) (_pr_lastThread = t)
|
||||||
|
|
||||||
|
#ifndef XP_MAC
|
||||||
#define _MD_GET_INTSOFF() (_pr_intsOff)
|
#define _MD_GET_INTSOFF() (_pr_intsOff)
|
||||||
#define _MD_SET_INTSOFF(_val) (_pr_intsOff = _val)
|
#define _MD_SET_INTSOFF(_val) (_pr_intsOff = _val)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* The unbalanced curly braces in these two macros are intentional */
|
/* The unbalanced curly braces in these two macros are intentional */
|
||||||
|
@ -374,12 +376,20 @@ extern PRInt32 _native_threads_only;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
#ifdef XP_MAC
|
||||||
|
|
||||||
|
#define _PR_INTSOFF(_is) _MD_INTSOFF(_is)
|
||||||
|
|
||||||
|
#else /* XP_MAC */
|
||||||
|
|
||||||
#define _PR_INTSOFF(_is) \
|
#define _PR_INTSOFF(_is) \
|
||||||
PR_BEGIN_MACRO \
|
PR_BEGIN_MACRO \
|
||||||
(_is) = _PR_MD_GET_INTSOFF(); \
|
(_is) = _PR_MD_GET_INTSOFF(); \
|
||||||
_PR_MD_SET_INTSOFF(1); \
|
_PR_MD_SET_INTSOFF(1); \
|
||||||
PR_END_MACRO
|
PR_END_MACRO
|
||||||
|
|
||||||
|
#endif /* XP_MAC */
|
||||||
|
|
||||||
#define _PR_FAST_INTSON(_is) \
|
#define _PR_FAST_INTSON(_is) \
|
||||||
PR_BEGIN_MACRO \
|
PR_BEGIN_MACRO \
|
||||||
_PR_MD_SET_INTSOFF(_is); \
|
_PR_MD_SET_INTSOFF(_is); \
|
||||||
|
|
|
@ -75,20 +75,21 @@ typedef struct ExtendedParamBlock ExtendedParamBlock;
|
||||||
static void AsyncIOCompletion (ExtendedParamBlock *pbAsyncPtr)
|
static void AsyncIOCompletion (ExtendedParamBlock *pbAsyncPtr)
|
||||||
{
|
{
|
||||||
_PRCPU *cpu = _PR_MD_CURRENT_CPU();
|
_PRCPU *cpu = _PR_MD_CURRENT_CPU();
|
||||||
PRThread *thread = pbAsyncPtr->thread;
|
PRThread *thread = pbAsyncPtr->thread;
|
||||||
|
PRIntn is;
|
||||||
|
|
||||||
if (_PR_MD_GET_INTSOFF()) {
|
if (_PR_MD_GET_INTSOFF()) {
|
||||||
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
|
|
||||||
thread->md.missedIONotify = PR_TRUE;
|
thread->md.missedIONotify = PR_TRUE;
|
||||||
return;
|
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
_PR_MD_SET_INTSOFF(1);
|
|
||||||
|
|
||||||
thread->md.osErrCode = noErr;
|
_PR_INTSOFF(is);
|
||||||
DoneWaitingOnThisThread(thread);
|
|
||||||
|
|
||||||
_PR_MD_SET_INTSOFF(0);
|
thread->md.osErrCode = noErr;
|
||||||
|
DoneWaitingOnThisThread(thread);
|
||||||
|
|
||||||
|
_PR_FAST_INTSON(is);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _MD_SetError(OSErr oserror)
|
void _MD_SetError(OSErr oserror)
|
||||||
|
|
|
@ -171,8 +171,8 @@ static pascal void DNSNotifierRoutine(void * contextPtr, OTEventCode otEvent, O
|
||||||
switch (otEvent) {
|
switch (otEvent) {
|
||||||
case T_DNRSTRINGTOADDRCOMPLETE:
|
case T_DNRSTRINGTOADDRCOMPLETE:
|
||||||
if (_PR_MD_GET_INTSOFF()) {
|
if (_PR_MD_GET_INTSOFF()) {
|
||||||
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
|
|
||||||
dnsContext.thread->md.missedIONotify = PR_TRUE;
|
dnsContext.thread->md.missedIONotify = PR_TRUE;
|
||||||
|
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DoneWaitingOnThisThread(dnsContext.thread);
|
DoneWaitingOnThisThread(dnsContext.thread);
|
||||||
|
@ -187,8 +187,8 @@ static pascal void DNSNotifierRoutine(void * contextPtr, OTEventCode otEvent, O
|
||||||
dnsContext.serviceRef = nil;
|
dnsContext.serviceRef = nil;
|
||||||
|
|
||||||
if (_PR_MD_GET_INTSOFF()) {
|
if (_PR_MD_GET_INTSOFF()) {
|
||||||
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
|
|
||||||
dnsContext.thread->md.missedIONotify = PR_TRUE;
|
dnsContext.thread->md.missedIONotify = PR_TRUE;
|
||||||
|
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DoneWaitingOnThisThread(dnsContext.thread);
|
DoneWaitingOnThisThread(dnsContext.thread);
|
||||||
|
@ -294,8 +294,8 @@ WakeUpNotifiedThread(PRThread *thread, OTResult result)
|
||||||
if (thread) {
|
if (thread) {
|
||||||
thread->md.osErrCode = result;
|
thread->md.osErrCode = result;
|
||||||
if (_PR_MD_GET_INTSOFF()) {
|
if (_PR_MD_GET_INTSOFF()) {
|
||||||
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
|
|
||||||
thread->md.missedIONotify = PR_TRUE;
|
thread->md.missedIONotify = PR_TRUE;
|
||||||
|
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DoneWaitingOnThisThread(thread);
|
DoneWaitingOnThisThread(thread);
|
||||||
|
@ -1171,8 +1171,8 @@ static pascal void RawEndpointNotifierRoutine(void * contextPtr, OTEventCode co
|
||||||
if (thread) {
|
if (thread) {
|
||||||
thread->md.osErrCode = result;
|
thread->md.osErrCode = result;
|
||||||
if (_PR_MD_GET_INTSOFF()) {
|
if (_PR_MD_GET_INTSOFF()) {
|
||||||
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
|
|
||||||
thread->md.asyncNotifyPending = PR_TRUE;
|
thread->md.asyncNotifyPending = PR_TRUE;
|
||||||
|
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DoneWaitingOnThisThread(thread);
|
DoneWaitingOnThisThread(thread);
|
||||||
|
|
|
@ -36,12 +36,15 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <Types.h>
|
#include <MacTypes.h>
|
||||||
#include <Timer.h>
|
#include <Timer.h>
|
||||||
#include <OSUtils.h>
|
#include <OSUtils.h>
|
||||||
|
|
||||||
#include <LowMem.h>
|
#include <LowMem.h>
|
||||||
|
#include <Multiprocessing.h>
|
||||||
|
#include <Gestalt.h>
|
||||||
|
|
||||||
|
#include "mdcriticalregion.h"
|
||||||
|
|
||||||
TimerUPP gTimerCallbackUPP = NULL;
|
TimerUPP gTimerCallbackUPP = NULL;
|
||||||
PRThread * gPrimaryThread = NULL;
|
PRThread * gPrimaryThread = NULL;
|
||||||
|
@ -168,24 +171,26 @@ _PRInterruptTable _pr_interruptTable[] = {
|
||||||
pascal void TimerCallback(TMTaskPtr tmTaskPtr)
|
pascal void TimerCallback(TMTaskPtr tmTaskPtr)
|
||||||
{
|
{
|
||||||
_PRCPU *cpu = _PR_MD_CURRENT_CPU();
|
_PRCPU *cpu = _PR_MD_CURRENT_CPU();
|
||||||
|
PRIntn is;
|
||||||
|
|
||||||
if (_PR_MD_GET_INTSOFF()) {
|
if (_PR_MD_GET_INTSOFF()) {
|
||||||
cpu->u.missed[cpu->where] |= _PR_MISSED_CLOCK;
|
cpu->u.missed[cpu->where] |= _PR_MISSED_CLOCK;
|
||||||
PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
|
PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_PR_MD_SET_INTSOFF(1);
|
|
||||||
|
|
||||||
// And tell nspr that a clock interrupt occured.
|
_PR_INTSOFF(is);
|
||||||
_PR_ClockInterrupt();
|
|
||||||
|
|
||||||
if ((_PR_RUNQREADYMASK(cpu)) >> ((_PR_MD_CURRENT_THREAD()->priority)))
|
|
||||||
_PR_SET_RESCHED_FLAG();
|
|
||||||
|
|
||||||
_PR_MD_SET_INTSOFF(0);
|
|
||||||
|
|
||||||
// Reset the clock timer so that we fire again.
|
// And tell nspr that a clock interrupt occured.
|
||||||
PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
|
_PR_ClockInterrupt();
|
||||||
|
|
||||||
|
if ((_PR_RUNQREADYMASK(cpu)) >> ((_PR_MD_CURRENT_THREAD()->priority)))
|
||||||
|
_PR_SET_RESCHED_FLAG();
|
||||||
|
|
||||||
|
_PR_FAST_INTSON(is);
|
||||||
|
|
||||||
|
// Reset the clock timer so that we fire again.
|
||||||
|
PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -219,25 +224,22 @@ void _MD_StopInterrupts(void)
|
||||||
|
|
||||||
void _MD_PauseCPU(PRIntervalTime timeout)
|
void _MD_PauseCPU(PRIntervalTime timeout)
|
||||||
{
|
{
|
||||||
#pragma unused (timeout)
|
if (timeout != PR_INTERVAL_NO_WAIT)
|
||||||
|
{
|
||||||
/* unsigned long finalTicks; */
|
EventRecord theEvent;
|
||||||
EventRecord theEvent;
|
|
||||||
|
|
||||||
if (timeout != PR_INTERVAL_NO_WAIT) {
|
/*
|
||||||
/* Delay(1,&finalTicks); */
|
** 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();
|
||||||
** Rather than calling Delay() which basically just wedges the processor
|
}
|
||||||
** we'll instead call WaitNextEvent() with a mask that ignores all events
|
|
||||||
** which gives other apps a chance to get time rather than just locking up
|
|
||||||
** the machine when we're waiting for a long time (or in an infinite loop,
|
|
||||||
** whichever comes first)
|
|
||||||
*/
|
|
||||||
(void)WaitNextEvent(nullEvent, &theEvent, 1, NULL);
|
|
||||||
|
|
||||||
(void) _MD_IOInterrupt();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -276,6 +278,11 @@ void WaitOnThisThread(PRThread *thread, PRIntervalTime timeout)
|
||||||
PRIntervalTime timein = PR_IntervalNow();
|
PRIntervalTime timein = PR_IntervalNow();
|
||||||
PRStatus status = PR_SUCCESS;
|
PRStatus status = PR_SUCCESS;
|
||||||
|
|
||||||
|
// Turn interrupts off to avoid a race over lock ownership with the callback
|
||||||
|
// (which can fire at any time). Interrupts may stay off until we leave
|
||||||
|
// this function, or another NSPR thread turns them back on. They certainly
|
||||||
|
// stay off until PR_WaitCondVar() relinquishes the asyncIOLock lock, which
|
||||||
|
// is what we care about.
|
||||||
_PR_INTSOFF(is);
|
_PR_INTSOFF(is);
|
||||||
PR_Lock(thread->md.asyncIOLock);
|
PR_Lock(thread->md.asyncIOLock);
|
||||||
if (timeout == PR_INTERVAL_NO_TIMEOUT) {
|
if (timeout == PR_INTERVAL_NO_TIMEOUT) {
|
||||||
|
@ -302,9 +309,24 @@ void DoneWaitingOnThisThread(PRThread *thread)
|
||||||
{
|
{
|
||||||
intn is;
|
intn is;
|
||||||
|
|
||||||
|
PR_ASSERT(thread->md.asyncIOLock->owner == NULL);
|
||||||
|
|
||||||
|
// DoneWaitingOnThisThread() is called from OT notifiers and async file I/O
|
||||||
|
// callbacks that can run at "interrupt" time (Classic Mac OS) or on pthreads
|
||||||
|
// that may run concurrently with the main threads (Mac OS X). They can thus
|
||||||
|
// be called when any NSPR thread is running, or even while NSPR is in a
|
||||||
|
// thread context switch. It is therefore vital that we can guarantee to
|
||||||
|
// be able to get the asyncIOLock without blocking (thus avoiding code
|
||||||
|
// that makes assumptions about the current NSPR thread etc). To achieve
|
||||||
|
// this, we use NSPR interrrupts as a semaphore on the lock; all code
|
||||||
|
// that grabs the lock also disables interrupts for the time the lock
|
||||||
|
// is held. Callers of DoneWaitingOnThisThread() thus have to check whether
|
||||||
|
// interrupts are already off, and, if so, simply set the missed_IO flag on
|
||||||
|
// the CPU rather than calling this function.
|
||||||
|
|
||||||
_PR_INTSOFF(is);
|
_PR_INTSOFF(is);
|
||||||
PR_Lock(thread->md.asyncIOLock);
|
PR_Lock(thread->md.asyncIOLock);
|
||||||
thread->io_pending = PR_FALSE;
|
thread->io_pending = PR_FALSE;
|
||||||
/* let the waiting thread know that async IO completed */
|
/* let the waiting thread know that async IO completed */
|
||||||
PR_NotifyCondVar(thread->md.asyncIOCVar);
|
PR_NotifyCondVar(thread->md.asyncIOCVar);
|
||||||
PR_Unlock(thread->md.asyncIOLock);
|
PR_Unlock(thread->md.asyncIOLock);
|
||||||
|
@ -319,6 +341,7 @@ PR_IMPLEMENT(void) PR_Mac_WaitForAsyncNotify(PRIntervalTime timeout)
|
||||||
PRStatus status = PR_SUCCESS;
|
PRStatus status = PR_SUCCESS;
|
||||||
PRThread *thread = _PR_MD_CURRENT_THREAD();
|
PRThread *thread = _PR_MD_CURRENT_THREAD();
|
||||||
|
|
||||||
|
// See commments in WaitOnThisThread()
|
||||||
_PR_INTSOFF(is);
|
_PR_INTSOFF(is);
|
||||||
PR_Lock(thread->md.asyncIOLock);
|
PR_Lock(thread->md.asyncIOLock);
|
||||||
if (timeout == PR_INTERVAL_NO_TIMEOUT) {
|
if (timeout == PR_INTERVAL_NO_TIMEOUT) {
|
||||||
|
@ -344,11 +367,14 @@ void AsyncNotify(PRThread *thread)
|
||||||
{
|
{
|
||||||
intn is;
|
intn is;
|
||||||
|
|
||||||
|
PR_ASSERT(thread->md.asyncIOLock->owner == NULL);
|
||||||
|
|
||||||
|
// See commments in DoneWaitingOnThisThread()
|
||||||
_PR_INTSOFF(is);
|
_PR_INTSOFF(is);
|
||||||
PR_Lock(thread->md.asyncIOLock);
|
PR_Lock(thread->md.asyncIOLock);
|
||||||
thread->md.asyncNotifyPending = PR_TRUE;
|
thread->md.asyncNotifyPending = PR_TRUE;
|
||||||
/* let the waiting thread know that async IO completed */
|
/* let the waiting thread know that async IO completed */
|
||||||
PR_NotifyCondVar(thread->md.asyncIOCVar); // let thread know that async IO completed
|
PR_NotifyCondVar(thread->md.asyncIOCVar);
|
||||||
PR_Unlock(thread->md.asyncIOLock);
|
PR_Unlock(thread->md.asyncIOLock);
|
||||||
_PR_FAST_INTSON(is);
|
_PR_FAST_INTSON(is);
|
||||||
}
|
}
|
||||||
|
@ -359,8 +385,8 @@ PR_IMPLEMENT(void) PR_Mac_PostAsyncNotify(PRThread *thread)
|
||||||
_PRCPU * cpu = _PR_MD_CURRENT_CPU();
|
_PRCPU * cpu = _PR_MD_CURRENT_CPU();
|
||||||
|
|
||||||
if (_PR_MD_GET_INTSOFF()) {
|
if (_PR_MD_GET_INTSOFF()) {
|
||||||
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
|
|
||||||
thread->md.missedAsyncNotify = PR_TRUE;
|
thread->md.missedAsyncNotify = PR_TRUE;
|
||||||
|
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
|
||||||
} else {
|
} else {
|
||||||
AsyncNotify(thread);
|
AsyncNotify(thread);
|
||||||
}
|
}
|
||||||
|
@ -407,3 +433,136 @@ PRStatus _MD_KillProcess(PRProcess *process)
|
||||||
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
|
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
|
||||||
return PR_FAILURE;
|
return PR_FAILURE;
|
||||||
}
|
}
|
||||||
|
//##############################################################################
|
||||||
|
//##############################################################################
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark INTERRUPT SUPPORT
|
||||||
|
|
||||||
|
#if TARGET_CARBON
|
||||||
|
|
||||||
|
/*
|
||||||
|
This critical region support is required for Mac NSPR to work correctly on dual CPU
|
||||||
|
machines on Mac OS X. This note explains why.
|
||||||
|
|
||||||
|
NSPR uses a timer task, and has callbacks for async file I/O and Open Transport
|
||||||
|
whose runtime behaviour differs depending on environment. On "Classic" Mac OS
|
||||||
|
these run at "interrupt" time (OS-level interrupts, that is, not NSPR interrupts),
|
||||||
|
and can thus preempt other code, but they always run to completion.
|
||||||
|
|
||||||
|
On Mac OS X, these are all emulated using MP tasks, which sit atop pthreads. Thus,
|
||||||
|
they can be preempted at any time (and not necessarily run to completion), and can
|
||||||
|
also run *concurrently* with eachother, and with application code, on multiple
|
||||||
|
CPU machines. Note that all NSPR threads are emulated, and all run on the main
|
||||||
|
application MP task.
|
||||||
|
|
||||||
|
We thus have to use MP critical sections to protect data that is shared between
|
||||||
|
the various callbacks and the main MP thread. It so happens that NSPR has this
|
||||||
|
concept of software interrupts, and making interrupt-off times be critical
|
||||||
|
sections works.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Whether to use critical regions. True if running on Mac OS X and later
|
||||||
|
*/
|
||||||
|
|
||||||
|
PRBool gUseCriticalRegions;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Count of the number of times we've entered the critical region.
|
||||||
|
We need this because ENTER_CRITICAL_REGION() will *not* block when
|
||||||
|
called from different NSPR threads (which all run on one MP thread),
|
||||||
|
and we need to ensure that when code turns interrupts back on (by
|
||||||
|
settings _pr_intsOff to 0) we exit the critical section enough times
|
||||||
|
to leave it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
PRInt32 gCriticalRegionEntryCount;
|
||||||
|
|
||||||
|
|
||||||
|
void _MD_SetIntsOff(PRInt32 ints)
|
||||||
|
{
|
||||||
|
ENTER_CRITICAL_REGION();
|
||||||
|
gCriticalRegionEntryCount ++;
|
||||||
|
|
||||||
|
_pr_intsOff = ints;
|
||||||
|
|
||||||
|
if (!ints)
|
||||||
|
{
|
||||||
|
PRInt32 i = gCriticalRegionEntryCount;
|
||||||
|
|
||||||
|
gCriticalRegionEntryCount = 0;
|
||||||
|
for ( ;i > 0; i --) {
|
||||||
|
LEAVE_CRITICAL_REGION();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* TARGET_CARBON */
|
||||||
|
|
||||||
|
|
||||||
|
//##############################################################################
|
||||||
|
//##############################################################################
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark CRITICAL REGION SUPPORT
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
|
if (!gUseCriticalRegions) return;
|
||||||
|
|
||||||
|
err = MD_CriticalRegionCreate(&gCriticalRegion);
|
||||||
|
PR_ASSERT(err == noErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TermCriticalRegion()
|
||||||
|
{
|
||||||
|
OSStatus err;
|
||||||
|
|
||||||
|
if (!gUseCriticalRegions) return;
|
||||||
|
|
||||||
|
err = MD_CriticalRegionDelete(gCriticalRegion);
|
||||||
|
PR_ASSERT(err == noErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void EnterCritialRegion()
|
||||||
|
{
|
||||||
|
OSStatus err;
|
||||||
|
|
||||||
|
if (!gUseCriticalRegions) return;
|
||||||
|
|
||||||
|
PR_ASSERT(gCriticalRegion != kInvalidID);
|
||||||
|
|
||||||
|
/* Change to a non-infinite timeout for debugging purposes */
|
||||||
|
err = MD_CriticalRegionEnter(gCriticalRegion, kDurationForever /* 10000 * kDurationMillisecond */ );
|
||||||
|
PR_ASSERT(err == noErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LeaveCritialRegion()
|
||||||
|
{
|
||||||
|
OSStatus err;
|
||||||
|
|
||||||
|
if (!gUseCriticalRegions) return;
|
||||||
|
|
||||||
|
PR_ASSERT(gCriticalRegion != kInvalidID);
|
||||||
|
|
||||||
|
err = MD_CriticalRegionExit(gCriticalRegion);
|
||||||
|
PR_ASSERT(err == noErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // MAC_CRITICAL_REGIONS
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: NULL; c-basic-offset: 2 -*- */
|
||||||
|
/*
|
||||||
|
* The contents of this file are subject to the Mozilla Public
|
||||||
|
* License Version 1.1 (the "License"); you may not use this file
|
||||||
|
* except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS
|
||||||
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||||
|
* implied. See the License for the specific language governing
|
||||||
|
* rights and limitations under the License.
|
||||||
|
*
|
||||||
|
* The Original Code is the Netscape Portable Runtime (NSPR).
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is Netscape
|
||||||
|
* Communications Corporation. Portions created by Netscape are
|
||||||
|
* Copyright (C) 1998-2000 Netscape Communications Corporation. All
|
||||||
|
* Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* George Warner, Apple Computer Inc.
|
||||||
|
* Simon Fraser <sfraser@netscape.com>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the
|
||||||
|
* terms of the GNU General Public License Version 2 or later (the
|
||||||
|
* "GPL"), in which case the provisions of the GPL are applicable
|
||||||
|
* instead of those above. If you wish to allow use of your
|
||||||
|
* version of this file only under the terms of the GPL and not to
|
||||||
|
* allow others to use your version of this file under the MPL,
|
||||||
|
* indicate your decision by deleting the provisions above and
|
||||||
|
* replace them with the notice and other provisions required by
|
||||||
|
* the GPL. If you do not delete the provisions above, a recipient
|
||||||
|
* may use your version of this file under either the MPL or the
|
||||||
|
* GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mdcriticalregion.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
This code is a replacement for MPEnterCriticalRegion/MPLeaveCriticalRegion,
|
||||||
|
which is broken on Mac OS 10.0.x builds, but fixed in 10.1. This code works
|
||||||
|
everywhere.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct MDCriticalRegionData_struct {
|
||||||
|
MPTaskID mMPTaskID; /* Who's in the critical region? */
|
||||||
|
UInt32 mDepthCount; /* How deep? */
|
||||||
|
MPSemaphoreID mMPSemaphoreID; /* ready semaphore */
|
||||||
|
} MDCriticalRegionData, *MDCriticalRegionDataPtr;
|
||||||
|
|
||||||
|
|
||||||
|
OSStatus
|
||||||
|
MD_CriticalRegionCreate(MDCriticalRegionID * outCriticalRegionID)
|
||||||
|
{
|
||||||
|
MDCriticalRegionDataPtr newCriticalRegionPtr;
|
||||||
|
MPSemaphoreID mpSemaphoreID;
|
||||||
|
OSStatus err = noErr;
|
||||||
|
|
||||||
|
if (outCriticalRegionID == NULL)
|
||||||
|
return paramErr;
|
||||||
|
|
||||||
|
*outCriticalRegionID = NULL;
|
||||||
|
|
||||||
|
newCriticalRegionPtr = (MDCriticalRegionDataPtr)MPAllocateAligned(sizeof(MDCriticalRegionData),
|
||||||
|
kMPAllocateDefaultAligned, kMPAllocateClearMask);
|
||||||
|
if (newCriticalRegionPtr == NULL)
|
||||||
|
return memFullErr;
|
||||||
|
|
||||||
|
// Note: this semaphore is pre-fired (ready!)
|
||||||
|
err = MPCreateBinarySemaphore(&mpSemaphoreID);
|
||||||
|
if (err == noErr)
|
||||||
|
{
|
||||||
|
newCriticalRegionPtr->mMPTaskID = kInvalidID;
|
||||||
|
newCriticalRegionPtr->mDepthCount = 0;
|
||||||
|
newCriticalRegionPtr->mMPSemaphoreID = mpSemaphoreID;
|
||||||
|
|
||||||
|
*outCriticalRegionID = (MDCriticalRegionID)newCriticalRegionPtr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MPFree((LogicalAddress)newCriticalRegionPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSStatus
|
||||||
|
MD_CriticalRegionDelete(MDCriticalRegionID inCriticalRegionID)
|
||||||
|
{
|
||||||
|
MDCriticalRegionDataPtr criticalRegion = (MDCriticalRegionDataPtr)inCriticalRegionID;
|
||||||
|
OSStatus err = noErr;
|
||||||
|
|
||||||
|
if (criticalRegion == NULL)
|
||||||
|
return paramErr;
|
||||||
|
|
||||||
|
if ((criticalRegion->mMPTaskID != kInvalidID) && (criticalRegion->mDepthCount > 0))
|
||||||
|
return kMPInsufficientResourcesErr;
|
||||||
|
|
||||||
|
if (criticalRegion->mMPSemaphoreID != kInvalidID)
|
||||||
|
err = MPDeleteSemaphore(criticalRegion->mMPSemaphoreID);
|
||||||
|
if (noErr != err) return err;
|
||||||
|
|
||||||
|
criticalRegion->mMPSemaphoreID = kInvalidID;
|
||||||
|
MPFree((LogicalAddress) criticalRegion);
|
||||||
|
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSStatus
|
||||||
|
MD_CriticalRegionEnter(MDCriticalRegionID inCriticalRegionID, Duration inTimeout)
|
||||||
|
{
|
||||||
|
MDCriticalRegionDataPtr criticalRegion = (MDCriticalRegionDataPtr)inCriticalRegionID;
|
||||||
|
MPTaskID currentTaskID = MPCurrentTaskID();
|
||||||
|
OSStatus err = noErr;
|
||||||
|
|
||||||
|
if (criticalRegion == NULL)
|
||||||
|
return paramErr;
|
||||||
|
|
||||||
|
// if I'm inside the critical region...
|
||||||
|
if (currentTaskID == criticalRegion->mMPTaskID)
|
||||||
|
{
|
||||||
|
// bump my depth
|
||||||
|
criticalRegion->mDepthCount++;
|
||||||
|
// and continue
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for the ready semaphore
|
||||||
|
err = MPWaitOnSemaphore(criticalRegion->mMPSemaphoreID, inTimeout);
|
||||||
|
// we didn't get it. return the error
|
||||||
|
if (noErr != err) return err;
|
||||||
|
|
||||||
|
// we got it!
|
||||||
|
criticalRegion->mMPTaskID = currentTaskID;
|
||||||
|
criticalRegion->mDepthCount = 1;
|
||||||
|
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSStatus
|
||||||
|
MD_CriticalRegionExit(MDCriticalRegionID inCriticalRegionID)
|
||||||
|
{
|
||||||
|
MDCriticalRegionDataPtr criticalRegion = (MDCriticalRegionDataPtr)inCriticalRegionID;
|
||||||
|
MPTaskID currentTaskID = MPCurrentTaskID();
|
||||||
|
OSStatus err = noErr;
|
||||||
|
|
||||||
|
// if we don't own the critical region...
|
||||||
|
if (currentTaskID != criticalRegion->mMPTaskID)
|
||||||
|
return kMPInsufficientResourcesErr;
|
||||||
|
|
||||||
|
// if we aren't at a depth...
|
||||||
|
if (criticalRegion->mDepthCount == 0)
|
||||||
|
return kMPInsufficientResourcesErr;
|
||||||
|
|
||||||
|
// un-bump my depth
|
||||||
|
criticalRegion->mDepthCount--;
|
||||||
|
|
||||||
|
// if we just bottomed out...
|
||||||
|
if (criticalRegion->mDepthCount == 0)
|
||||||
|
{
|
||||||
|
// release ownership of the structure
|
||||||
|
criticalRegion->mMPTaskID = kInvalidID;
|
||||||
|
// and signal the ready semaphore
|
||||||
|
err = MPSignalSemaphore(criticalRegion->mMPSemaphoreID);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/*
|
||||||
|
* The contents of this file are subject to the Mozilla Public
|
||||||
|
* License Version 1.1 (the "License"); you may not use this file
|
||||||
|
* except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS
|
||||||
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||||
|
* implied. See the License for the specific language governing
|
||||||
|
* rights and limitations under the License.
|
||||||
|
*
|
||||||
|
* The Original Code is the Netscape Portable Runtime (NSPR).
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is Netscape
|
||||||
|
* Communications Corporation. Portions created by Netscape are
|
||||||
|
* Copyright (C) 1998-2000 Netscape Communications Corporation. All
|
||||||
|
* Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* George Warner, Apple Computer Inc.
|
||||||
|
* Simon Fraser <sfraser@netscape.com>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the
|
||||||
|
* terms of the GNU General Public License Version 2 or later (the
|
||||||
|
* "GPL"), in which case the provisions of the GPL are applicable
|
||||||
|
* instead of those above. If you wish to allow use of your
|
||||||
|
* version of this file only under the terms of the GPL and not to
|
||||||
|
* allow others to use your version of this file under the MPL,
|
||||||
|
* indicate your decision by deleting the provisions above and
|
||||||
|
* replace them with the notice and other provisions required by
|
||||||
|
* the GPL. If you do not delete the provisions above, a recipient
|
||||||
|
* may use your version of this file under either the MPL or the
|
||||||
|
* GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef mdcriticalregion_h___
|
||||||
|
#define mdcriticalregion_h___
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __MULTIPROCESSING__
|
||||||
|
#include <Multiprocessing.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct OpaqueMDCriticalRegionID* MDCriticalRegionID;
|
||||||
|
|
||||||
|
OSStatus MD_CriticalRegionCreate(MDCriticalRegionID * pMDCriticalRegionID);
|
||||||
|
|
||||||
|
OSStatus MD_CriticalRegionDelete(MDCriticalRegionID pMDCriticalRegionID);
|
||||||
|
|
||||||
|
OSStatus MD_CriticalRegionEnter(MDCriticalRegionID pMDCriticalRegionID, Duration pTimeout);
|
||||||
|
|
||||||
|
OSStatus MD_CriticalRegionExit(MDCriticalRegionID pMDCriticalRegionID);
|
||||||
|
|
||||||
|
#endif /* mdcriticalregion_h___ */
|
||||||
|
|
|
@ -288,6 +288,8 @@ void _MD_EarlyInit()
|
||||||
{
|
{
|
||||||
Handle environmentVariables;
|
Handle environmentVariables;
|
||||||
|
|
||||||
|
INIT_CRITICAL_REGION();
|
||||||
|
|
||||||
#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)
|
||||||
#else
|
#else
|
||||||
|
@ -374,6 +376,7 @@ void CleanupTermProc(void)
|
||||||
_MD_StopInterrupts(); // deactive Time Manager task
|
_MD_StopInterrupts(); // deactive Time Manager task
|
||||||
|
|
||||||
CLOSE_OPEN_TRANSPORT();
|
CLOSE_OPEN_TRANSPORT();
|
||||||
|
TERM_CRITICAL_REGION();
|
||||||
|
|
||||||
__NSTerminate();
|
__NSTerminate();
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче