diff options
author | sfraser%netscape.com <devnull@localhost> | 2001-09-27 23:35:25 +0000 |
---|---|---|
committer | sfraser%netscape.com <devnull@localhost> | 2001-09-27 23:35:25 +0000 |
commit | 9eee09f99d10e83bd38311d4da1ff04b2b46f6af (patch) | |
tree | 29eed98cb5deb1251eeef52407220a6b3882b9fe | |
parent | f46e73e95e84b1d201e0054cba73f4de26cd4d49 (diff) | |
download | nspr-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.mcp | bin | 167357 -> 163969 bytes | |||
-rw-r--r-- | pr/include/md/_macos.h | 52 | ||||
-rw-r--r-- | pr/include/private/primpl.h | 10 | ||||
-rw-r--r-- | pr/src/md/mac/macio.c | 17 | ||||
-rw-r--r-- | pr/src/md/mac/macsockotpt.c | 8 | ||||
-rw-r--r-- | pr/src/md/mac/macthr.c | 223 | ||||
-rw-r--r-- | pr/src/md/mac/mdcriticalregion.c | 169 | ||||
-rw-r--r-- | pr/src/md/mac/mdcriticalregion.h | 56 | ||||
-rw-r--r-- | pr/src/md/mac/mdmac.c | 3 |
9 files changed, 494 insertions, 44 deletions
diff --git a/macbuild/NSPR20PPC.mcp b/macbuild/NSPR20PPC.mcp Binary files differindex a0a17543..aae83165 100644 --- a/macbuild/NSPR20PPC.mcp +++ b/macbuild/NSPR20PPC.mcp 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(); } |