summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsfraser%netscape.com <devnull@localhost>2001-09-27 23:35:25 +0000
committersfraser%netscape.com <devnull@localhost>2001-09-27 23:35:25 +0000
commit9eee09f99d10e83bd38311d4da1ff04b2b46f6af (patch)
tree29eed98cb5deb1251eeef52407220a6b3882b9fe
parentf46e73e95e84b1d201e0054cba73f4de26cd4d49 (diff)
downloadnspr-hg-9eee09f99d10e83bd38311d4da1ff04b2b46f6af.tar.gz
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.
-rw-r--r--macbuild/NSPR20PPC.mcpbin167357 -> 163969 bytes
-rw-r--r--pr/include/md/_macos.h52
-rw-r--r--pr/include/private/primpl.h10
-rw-r--r--pr/src/md/mac/macio.c17
-rw-r--r--pr/src/md/mac/macsockotpt.c8
-rw-r--r--pr/src/md/mac/macthr.c223
-rw-r--r--pr/src/md/mac/mdcriticalregion.c169
-rw-r--r--pr/src/md/mac/mdcriticalregion.h56
-rw-r--r--pr/src/md/mac/mdmac.c3
9 files changed, 494 insertions, 44 deletions
diff --git a/macbuild/NSPR20PPC.mcp b/macbuild/NSPR20PPC.mcp
index a0a17543..aae83165 100644
--- a/macbuild/NSPR20PPC.mcp
+++ b/macbuild/NSPR20PPC.mcp
Binary files differ
diff --git a/pr/include/md/_macos.h b/pr/include/md/_macos.h
index 2cc737d7..565cb94a 100644
--- a/pr/include/md/_macos.h
+++ b/pr/include/md/_macos.h
@@ -138,6 +138,23 @@ struct _MDFileDesc {
** 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_STOP_INTERRUPTS _MD_StopInterrupts
#define _MD_BLOCK_CLOCK_INTERRUPTS()
@@ -238,6 +255,8 @@ extern PRStatus _MD_InitThread(PRThread *thread);
/*
** 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)
#define _MD_INIT_CONTEXT(_thread, _sp, _main, _status) \
@@ -248,6 +267,7 @@ extern PRStatus _MD_InitThread(PRThread *thread);
*((PRBool *)_status) = PR_TRUE; \
(void) setjmp(jb); \
sp = INIT_STACKPTR(_sp); \
+ *sp = 0; \
(_MD_GET_SP(_thread)) = (long) sp; \
tvect = (unsigned long *)_main; \
(_MD_GET_PC(_thread)) = (int) *tvect; \
@@ -627,4 +647,36 @@ extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap);
extern void SetLogFileTypeCreator(const char *logFile);
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___ */
diff --git a/pr/include/private/primpl.h b/pr/include/private/primpl.h
index 983fc5d6..7fa9d278 100644
--- a/pr/include/private/primpl.h
+++ b/pr/include/private/primpl.h
@@ -328,8 +328,10 @@ NSPR_API(PRInt32) _pr_intsOff;
#define _MD_LAST_THREAD() (_pr_lastThread)
#define _MD_SET_LAST_THREAD(t) (_pr_lastThread = t)
+#ifndef XP_MAC
#define _MD_GET_INTSOFF() (_pr_intsOff)
#define _MD_SET_INTSOFF(_val) (_pr_intsOff = _val)
+#endif
/* The unbalanced curly braces in these two macros are intentional */
@@ -374,12 +376,20 @@ extern PRInt32 _native_threads_only;
#else
+#ifdef XP_MAC
+
+#define _PR_INTSOFF(_is) _MD_INTSOFF(_is)
+
+#else /* XP_MAC */
+
#define _PR_INTSOFF(_is) \
PR_BEGIN_MACRO \
(_is) = _PR_MD_GET_INTSOFF(); \
_PR_MD_SET_INTSOFF(1); \
PR_END_MACRO
+#endif /* XP_MAC */
+
#define _PR_FAST_INTSON(_is) \
PR_BEGIN_MACRO \
_PR_MD_SET_INTSOFF(_is); \
diff --git a/pr/src/md/mac/macio.c b/pr/src/md/mac/macio.c
index 39e7c9fe..0a546635 100644
--- a/pr/src/md/mac/macio.c
+++ b/pr/src/md/mac/macio.c
@@ -75,20 +75,21 @@ typedef struct ExtendedParamBlock ExtendedParamBlock;
static void AsyncIOCompletion (ExtendedParamBlock *pbAsyncPtr)
{
_PRCPU *cpu = _PR_MD_CURRENT_CPU();
- PRThread *thread = pbAsyncPtr->thread;
-
+ PRThread *thread = pbAsyncPtr->thread;
+ PRIntn is;
+
if (_PR_MD_GET_INTSOFF()) {
- cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
thread->md.missedIONotify = PR_TRUE;
- return;
+ cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
+ return;
}
- _PR_MD_SET_INTSOFF(1);
- thread->md.osErrCode = noErr;
- DoneWaitingOnThisThread(thread);
+ _PR_INTSOFF(is);
- _PR_MD_SET_INTSOFF(0);
+ thread->md.osErrCode = noErr;
+ DoneWaitingOnThisThread(thread);
+ _PR_FAST_INTSON(is);
}
void _MD_SetError(OSErr oserror)
diff --git a/pr/src/md/mac/macsockotpt.c b/pr/src/md/mac/macsockotpt.c
index 00cc9022..9eccf69c 100644
--- a/pr/src/md/mac/macsockotpt.c
+++ b/pr/src/md/mac/macsockotpt.c
@@ -171,8 +171,8 @@ static pascal void DNSNotifierRoutine(void * contextPtr, OTEventCode otEvent, O
switch (otEvent) {
case T_DNRSTRINGTOADDRCOMPLETE:
if (_PR_MD_GET_INTSOFF()) {
- cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
dnsContext.thread->md.missedIONotify = PR_TRUE;
+ cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
return;
}
DoneWaitingOnThisThread(dnsContext.thread);
@@ -187,8 +187,8 @@ static pascal void DNSNotifierRoutine(void * contextPtr, OTEventCode otEvent, O
dnsContext.serviceRef = nil;
if (_PR_MD_GET_INTSOFF()) {
- cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
dnsContext.thread->md.missedIONotify = PR_TRUE;
+ cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
return;
}
DoneWaitingOnThisThread(dnsContext.thread);
@@ -294,8 +294,8 @@ WakeUpNotifiedThread(PRThread *thread, OTResult result)
if (thread) {
thread->md.osErrCode = result;
if (_PR_MD_GET_INTSOFF()) {
- cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
thread->md.missedIONotify = PR_TRUE;
+ cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
return;
}
DoneWaitingOnThisThread(thread);
@@ -1171,8 +1171,8 @@ static pascal void RawEndpointNotifierRoutine(void * contextPtr, OTEventCode co
if (thread) {
thread->md.osErrCode = result;
if (_PR_MD_GET_INTSOFF()) {
- cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
thread->md.asyncNotifyPending = PR_TRUE;
+ cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
return;
}
DoneWaitingOnThisThread(thread);
diff --git a/pr/src/md/mac/macthr.c b/pr/src/md/mac/macthr.c
index 43f9a079..b01eda61 100644
--- a/pr/src/md/mac/macthr.c
+++ b/pr/src/md/mac/macthr.c
@@ -36,12 +36,15 @@
#include <string.h>
-#include <Types.h>
+#include <MacTypes.h>
#include <Timer.h>
#include <OSUtils.h>
#include <LowMem.h>
+#include <Multiprocessing.h>
+#include <Gestalt.h>
+#include "mdcriticalregion.h"
TimerUPP gTimerCallbackUPP = NULL;
PRThread * gPrimaryThread = NULL;
@@ -168,24 +171,26 @@ _PRInterruptTable _pr_interruptTable[] = {
pascal void TimerCallback(TMTaskPtr tmTaskPtr)
{
_PRCPU *cpu = _PR_MD_CURRENT_CPU();
+ PRIntn is;
if (_PR_MD_GET_INTSOFF()) {
cpu->u.missed[cpu->where] |= _PR_MISSED_CLOCK;
- PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
- return;
+ PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
+ return;
}
- _PR_MD_SET_INTSOFF(1);
- // And tell nspr that a clock interrupt occured.
- _PR_ClockInterrupt();
+ _PR_INTSOFF(is);
+
+ // And tell nspr that a clock interrupt occured.
+ _PR_ClockInterrupt();
- if ((_PR_RUNQREADYMASK(cpu)) >> ((_PR_MD_CURRENT_THREAD()->priority)))
- _PR_SET_RESCHED_FLAG();
+ if ((_PR_RUNQREADYMASK(cpu)) >> ((_PR_MD_CURRENT_THREAD()->priority)))
+ _PR_SET_RESCHED_FLAG();
- _PR_MD_SET_INTSOFF(0);
+ _PR_FAST_INTSON(is);
- // Reset the clock timer so that we fire again.
- PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
+ // 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)
{
-#pragma unused (timeout)
-
- /* unsigned long finalTicks; */
- EventRecord theEvent;
+ if (timeout != PR_INTERVAL_NO_WAIT)
+ {
+ 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);
- /*
- ** 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();
- }
+ (void) _MD_IOInterrupt();
+ }
}
@@ -276,6 +278,11 @@ void WaitOnThisThread(PRThread *thread, PRIntervalTime timeout)
PRIntervalTime timein = PR_IntervalNow();
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_Lock(thread->md.asyncIOLock);
if (timeout == PR_INTERVAL_NO_TIMEOUT) {
@@ -302,9 +309,24 @@ void DoneWaitingOnThisThread(PRThread *thread)
{
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_Lock(thread->md.asyncIOLock);
- thread->io_pending = PR_FALSE;
+ thread->io_pending = PR_FALSE;
/* let the waiting thread know that async IO completed */
PR_NotifyCondVar(thread->md.asyncIOCVar);
PR_Unlock(thread->md.asyncIOLock);
@@ -319,6 +341,7 @@ PR_IMPLEMENT(void) PR_Mac_WaitForAsyncNotify(PRIntervalTime timeout)
PRStatus status = PR_SUCCESS;
PRThread *thread = _PR_MD_CURRENT_THREAD();
+ // See commments in WaitOnThisThread()
_PR_INTSOFF(is);
PR_Lock(thread->md.asyncIOLock);
if (timeout == PR_INTERVAL_NO_TIMEOUT) {
@@ -344,11 +367,14 @@ void AsyncNotify(PRThread *thread)
{
intn is;
+ PR_ASSERT(thread->md.asyncIOLock->owner == NULL);
+
+ // See commments in DoneWaitingOnThisThread()
_PR_INTSOFF(is);
PR_Lock(thread->md.asyncIOLock);
- thread->md.asyncNotifyPending = PR_TRUE;
+ thread->md.asyncNotifyPending = PR_TRUE;
/* 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_FAST_INTSON(is);
}
@@ -359,8 +385,8 @@ PR_IMPLEMENT(void) PR_Mac_PostAsyncNotify(PRThread *thread)
_PRCPU * cpu = _PR_MD_CURRENT_CPU();
if (_PR_MD_GET_INTSOFF()) {
- cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
thread->md.missedAsyncNotify = PR_TRUE;
+ cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
} else {
AsyncNotify(thread);
}
@@ -407,3 +433,136 @@ PRStatus _MD_KillProcess(PRProcess *process)
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
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
+
diff --git a/pr/src/md/mac/mdcriticalregion.c b/pr/src/md/mac/mdcriticalregion.c
new file mode 100644
index 00000000..927b9246
--- /dev/null
+++ b/pr/src/md/mac/mdcriticalregion.c
@@ -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;
+}
+
diff --git a/pr/src/md/mac/mdcriticalregion.h b/pr/src/md/mac/mdcriticalregion.h
new file mode 100644
index 00000000..eba4c82b
--- /dev/null
+++ b/pr/src/md/mac/mdcriticalregion.h
@@ -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___ */
+
diff --git a/pr/src/md/mac/mdmac.c b/pr/src/md/mac/mdmac.c
index 8819492f..f7e9a4fa 100644
--- a/pr/src/md/mac/mdmac.c
+++ b/pr/src/md/mac/mdmac.c
@@ -288,6 +288,8 @@ void _MD_EarlyInit()
{
Handle environmentVariables;
+ INIT_CRITICAL_REGION();
+
#if !defined(MAC_NSPR_STANDALONE)
// MacintoshInitializeMemory(); Moved to mdmacmem.c: AllocateRawMemory(Size blockSize)
#else
@@ -374,6 +376,7 @@ void CleanupTermProc(void)
_MD_StopInterrupts(); // deactive Time Manager task
CLOSE_OPEN_TRANSPORT();
+ TERM_CRITICAL_REGION();
__NSTerminate();
}