mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-19 09:30:44 +00:00
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:
parent
9104b49a5c
commit
821e14b019
Binary file not shown.
@ -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___ */
|
||||
|
@ -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); \
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
if ((_PR_RUNQREADYMASK(cpu)) >> ((_PR_MD_CURRENT_THREAD()->priority)))
|
||||
_PR_SET_RESCHED_FLAG();
|
||||
|
||||
_PR_MD_SET_INTSOFF(0);
|
||||
_PR_INTSOFF(is);
|
||||
|
||||
// Reset the clock timer so that we fire again.
|
||||
PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
|
||||
// And tell nspr that a clock interrupt occured.
|
||||
_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)
|
||||
{
|
||||
#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
|
||||
|
||||
|
169
nsprpub/pr/src/md/mac/mdcriticalregion.c
Normal file
169
nsprpub/pr/src/md/mac/mdcriticalregion.c
Normal file
@ -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;
|
||||
}
|
||||
|
56
nsprpub/pr/src/md/mac/mdcriticalregion.h
Normal file
56
nsprpub/pr/src/md/mac/mdcriticalregion.h
Normal file
@ -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;
|
||||
|
||||
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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user