From 58601f660e420b0aa1ece5d5228659eb25ce20b7 Mon Sep 17 00:00:00 2001 From: cvs2hg Date: Tue, 9 Oct 2001 02:16:58 +0000 Subject: fixup commit for branch 'NETSCAPE_6_2_3_BRANCH' --- config/nspr-config.in | 2 +- config/prdepend.h | 1 + macbuild/NSPR20PPC.mcp | Bin 167357 -> 163969 bytes pr/include/md/_macos.h | 55 ++++++- pr/include/md/_pth.h | 46 ++++++ pr/include/private/primpl.h | 12 +- pr/src/md/mac/macio.c | 72 +++++---- pr/src/md/mac/macrng.c | 7 - pr/src/md/mac/macsockotpt.c | 309 ++++++++++++++++++++++----------------- pr/src/md/mac/macthr.c | 223 ++++++++++++++++++++++++---- pr/src/md/mac/mdcriticalregion.c | 169 +++++++++++++++++++++ pr/src/md/mac/mdcriticalregion.h | 56 +++++++ pr/src/md/mac/mdmac.c | 3 + pr/src/md/unix/unix_errors.c | 5 + pr/src/misc/prnetdb.c | 2 +- pr/src/misc/prtime.c | 7 +- pr/src/pthreads/ptsynch.c | 41 +++--- 17 files changed, 774 insertions(+), 236 deletions(-) create mode 100644 pr/src/md/mac/mdcriticalregion.c create mode 100644 pr/src/md/mac/mdcriticalregion.h diff --git a/config/nspr-config.in b/config/nspr-config.in index d6776558..daae782d 100755 --- a/config/nspr-config.in +++ b/config/nspr-config.in @@ -107,7 +107,7 @@ if test "$echo_libs" = "yes"; then fi os_ldflags="@LDFLAGS@" for i in $os_ldflags ; do - if echo $i | grep ^-L >/dev/null; then + if echo $i | grep \^-L >/dev/null; then libdirs="$libdirs $i" fi done diff --git a/config/prdepend.h b/config/prdepend.h index 8bebd56b..28c1b139 100644 --- a/config/prdepend.h +++ b/config/prdepend.h @@ -39,3 +39,4 @@ */ #error "Do not include this header file." + diff --git a/macbuild/NSPR20PPC.mcp b/macbuild/NSPR20PPC.mcp index a0a17543..b305f756 100644 Binary files a/macbuild/NSPR20PPC.mcp and b/macbuild/NSPR20PPC.mcp differ diff --git a/pr/include/md/_macos.h b/pr/include/md/_macos.h index 2cc737d7..caf73cb9 100644 --- a/pr/include/md/_macos.h +++ b/pr/include/md/_macos.h @@ -120,11 +120,10 @@ struct _MDFileDesc { /* Server sockets: listen bit tells the notifier func what to do */ PRBool doListen; - + _MDSocketCallerInfo misc; _MDSocketCallerInfo read; _MDSocketCallerInfo write; - _MDSocketCallerInfo poll; }; /* @@ -138,6 +137,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 +254,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 +266,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 +646,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/md/_pth.h b/pr/include/md/_pth.h index 997f32f7..be65ab74 100644 --- a/pr/include/md/_pth.h +++ b/pr/include/md/_pth.h @@ -96,6 +96,52 @@ #define _PT_PTHREAD_COND_INIT(m, a) pthread_cond_init(&(m), &(a)) #endif +/* The pthreads standard does not specify an invalid value for the + * pthread_t handle. (0 is usually an invalid pthread identifier + * but there are exceptions, for example, DG/UX.) These macros + * define a way to set the handle to or compare the handle with an + * invalid identifier. These macros are not portable and may be + * more of a problem as we adapt to more pthreads implementations. + * They are only used in the PRMonitor functions. Do not use them + * in new code. + * + * Unfortunately some of our clients depend on certain properties + * of our PRMonitor implementation, preventing us from replacing + * it by a portable implementation. + * - High-performance servers like the fact that PR_EnterMonitor + * only calls PR_Lock and PR_ExitMonitor only calls PR_Unlock. + * (A portable implementation would use a PRLock and a PRCondVar + * to implement the recursive lock in a monitor and call both + * PR_Lock and PR_Unlock in PR_EnterMonitor and PR_ExitMonitor.) + * Unfortunately this forces us to read the monitor owner field + * without holding a lock. + * - One way to make it safe to read the monitor owner field + * without holding a lock is to make that field a PRThread* + * (one should be able to read a pointer with a single machine + * instruction). However, PR_GetCurrentThread calls calloc if + * it is called by a thread that was not created by NSPR. The + * malloc tracing tools in the Mozilla client use PRMonitor for + * locking in their malloc, calloc, and free functions. If + * PR_EnterMonitor calls any of these functions, infinite + * recursion ensues. + */ +#if defined(_PR_DCETHREADS) +#define _PT_PTHREAD_INVALIDATE_THR_HANDLE(t) \ + memset(&(t), 0, sizeof(pthread_t)) +#define _PT_PTHREAD_THR_HANDLE_IS_INVALID(t) \ + (!memcmp(&(t), &pt_zero_tid, sizeof(pthread_t))) +#define _PT_PTHREAD_COPY_THR_HANDLE(st, dt) (dt) = (st) +#elif defined(IRIX) || defined(OSF1) || defined(AIX) || defined(SOLARIS) \ + || defined(HPUX) || defined(LINUX) || defined(FREEBSD) \ + || defined(NETBSD) || defined(OPENBSD) || defined(BSDI) \ + || defined(VMS) || defined(NTO) || defined(RHAPSODY) +#define _PT_PTHREAD_INVALIDATE_THR_HANDLE(t) (t) = 0 +#define _PT_PTHREAD_THR_HANDLE_IS_INVALID(t) (t) == 0 +#define _PT_PTHREAD_COPY_THR_HANDLE(st, dt) (dt) = (st) +#else +#error "pthreads is not supported for this architecture" +#endif + #if defined(_PR_DCETHREADS) #define _PT_PTHREAD_ATTR_INIT pthread_attr_create #define _PT_PTHREAD_ATTR_DESTROY pthread_attr_delete diff --git a/pr/include/private/primpl.h b/pr/include/private/primpl.h index eb1dd716..8c330883 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); \ @@ -1450,7 +1460,7 @@ struct PRMonitor { const char* name; /* monitor name for debugging */ #if defined(_PR_PTHREADS) PRLock lock; /* the lock structure */ - PRThread *owner; /* the owner of the lock or NULL */ + pthread_t owner; /* the owner of the lock or invalid */ PRCondVar *cvar; /* condition variable queue */ #else /* defined(_PR_PTHREADS) */ PRCondVar *cvar; /* associated lock and condition variable queue */ diff --git a/pr/src/md/mac/macio.c b/pr/src/md/mac/macio.c index 9bc408dd..d1d58b2e 100644 --- a/pr/src/md/mac/macio.c +++ b/pr/src/md/mac/macio.c @@ -91,20 +91,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) @@ -247,9 +248,9 @@ PRInt32 ReadWriteProc(PRFileDesc *fd, void *buf, PRUint32 bytes, IOOperation op) { PRInt32 refNum = fd->secret->md.osfd; OSErr err; - ExtendedParamBlock pbAsync; + ExtendedParamBlock pbAsync; PRThread *me = _PR_MD_CURRENT_THREAD(); - _PRCPU *cpu = _PR_MD_CURRENT_CPU(); + _PRCPU *cpu = _PR_MD_CURRENT_CPU(); /* quick hack to allow PR_fprintf, etc to work with stderr, stdin, stdout */ /* note, if a user chooses "seek" or the like as an operation in another function */ @@ -270,12 +271,13 @@ PRInt32 ReadWriteProc(PRFileDesc *fd, void *buf, PRUint32 bytes, IOOperation op) } return (bytes); - } else { static IOCompletionUPP sCompletionUPP = NULL; + PRBool doingAsync = PR_FALSE; + /* allocate the callback Universal Procedure Pointer (UPP). This actually allocates a 32 byte Ptr in the heap, so only do this once */ @@ -296,9 +298,11 @@ PRInt32 ReadWriteProc(PRFileDesc *fd, void *buf, PRUint32 bytes, IOOperation op) /* ** Issue the async read call and wait for the io semaphore associated ** with this thread. - ** Don't compute error code from async call. Bug in OS returns a garbage value. + ** Async file system calls *never* return error values, so ignore their + ** results (see ); + ** the completion routine is always called. */ - me->io_fd = refNum; + me->io_fd = refNum; me->md.osErrCode = noErr; if (op == READ_ASYNC) { @@ -308,39 +312,33 @@ PRInt32 ReadWriteProc(PRFileDesc *fd, void *buf, PRUint32 bytes, IOOperation op) */ if ( bytes > 20480L ) { - err = PBReadAsync(&pbAsync.pb); - if (err != noErr && err != eofErr) - goto ErrorExit; + doingAsync = PR_TRUE; + me->io_pending = PR_TRUE; - me->io_pending = PR_TRUE; /* Only mark thread io pending if async call worked */ + (void)PBReadAsync(&pbAsync.pb); } else { - (void) PBReadSync(&pbAsync.pb); - /* - ** This is probbaly redundant but want to make sure we indicate the read - ** is complete so we don't wander off into the Sargasso Sea of Mac - ** threading - */ - pbAsync.pb.ioParam.ioResult = 0; + pbAsync.pb.ioParam.ioCompletion = NULL; + me->io_pending = PR_FALSE; + + err = PBReadSync(&pbAsync.pb); + if (err != noErr && err != eofErr) + goto ErrorExit; } } else { + doingAsync = PR_TRUE; + me->io_pending = PR_TRUE; + /* writes are currently always async */ - err = PBWriteAsync(&pbAsync.pb); - if (err != noErr) - goto ErrorExit; - - /* Didn't get an error on the asyn call so mark thread io pending */ - me->io_pending = PR_TRUE; + (void)PBWriteAsync(&pbAsync.pb); } - /* See if the i/o call is still pending before we actually yield */ - if (pbAsync.pb.ioParam.ioResult == 1) + if (doingAsync) { WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT); - else - me->io_pending = PR_FALSE; /* io completed so don't mark thread io pending */ + } } err = me->md.osErrCode; @@ -350,13 +348,13 @@ PRInt32 ReadWriteProc(PRFileDesc *fd, void *buf, PRUint32 bytes, IOOperation op) err = pbAsync.pb.ioParam.ioResult; if (err != noErr && err != eofErr) goto ErrorExit; - else - return pbAsync.pb.ioParam.ioActCount; + + return pbAsync.pb.ioParam.ioActCount; ErrorExit: me->md.osErrCode = err; _MD_SetError(err); - return -1; + return -1; } /* diff --git a/pr/src/md/mac/macrng.c b/pr/src/md/mac/macrng.c index 332c375e..d9e18d1e 100644 --- a/pr/src/md/mac/macrng.c +++ b/pr/src/md/mac/macrng.c @@ -33,13 +33,6 @@ */ -#if 0 /* XXX what the flip is this all about? No MAC Wizards here. */ -#ifdef notdef -#include "xp_core.h" -#include "xp_file.h" -#endif -#endif /* 0 */ - /* XXX are all these headers required for a call to TickCount()? */ #include #include diff --git a/pr/src/md/mac/macsockotpt.c b/pr/src/md/mac/macsockotpt.c index 00cc9022..ef15a56b 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); @@ -305,92 +305,89 @@ WakeUpNotifiedThread(PRThread *thread, OTResult result) // Notification routine // Async callback routine. // A5 is OK. Cannot allocate memory here +// Ref: http://gemma.apple.com/techpubs/mac/NetworkingOT/NetworkingWOT-100.html +// static pascal void NotifierRoutine(void * contextPtr, OTEventCode code, OTResult result, void * cookie) { - PRFilePrivate *secret = (PRFilePrivate *) contextPtr; - _MDFileDesc * md = &(secret->md); - EndpointRef endpoint = (EndpointRef)secret->md.osfd; - PRThread * thread = NULL; - PRThread * pollThread = md->poll.thread; - OSStatus err; - OTResult resultOT; - TDiscon discon; + PRFilePrivate *secret = (PRFilePrivate *) contextPtr; + _MDFileDesc * md = &(secret->md); + EndpointRef endpoint = (EndpointRef)secret->md.osfd; + PRThread * readThread = NULL; // also used for 'misc' + PRThread * writeThread = NULL; + OSStatus err; + OTResult resultOT; + TDiscon discon; switch (code) { // OTLook Events - case T_LISTEN: // A connection request is available - // If md->doListen is true, then PR_Listen has been - // called on this endpoint; therefore, we're ready to - // accept connections. But we'll do that with PR_Accept - // (which calls OTListen, OTAccept, etc) instead of - // doing it here. - if (md->doListen) { - thread = secret->md.misc.thread; - secret->md.misc.thread = NULL; - secret->md.misc.cookie = cookie; - break; - } else { - // Reject the connection, we're not listening - OTSndDisconnect(endpoint, NULL); - } + // If md->doListen is true, then PR_Listen has been + // called on this endpoint; therefore, we're ready to + // accept connections. But we'll do that with PR_Accept + // (which calls OTListen, OTAccept, etc) instead of + // doing it here. + if (md->doListen) { + readThread = secret->md.misc.thread; + secret->md.misc.thread = NULL; + secret->md.misc.cookie = cookie; + break; + } else { + // Reject the connection, we're not listening + OTSndDisconnect(endpoint, NULL); + } break; - + case T_CONNECT: // Confirmation of a connect request - // cookie = sndCall parameter from OTConnect() + // cookie = sndCall parameter from OTConnect() err = OTRcvConnect(endpoint, NULL); PR_ASSERT(err == kOTNoError); - // wake up waiting thread, if any - thread = secret->md.write.thread; + // wake up waiting thread, if any. + writeThread = secret->md.write.thread; secret->md.write.thread = NULL; - secret->md.write.cookie = cookie; + secret->md.write.cookie = cookie; break; case T_DATA: // Standard data is available - // Mark this socket as readable. - secret->md.readReady = PR_TRUE; + // Mark this socket as readable. + secret->md.readReady = PR_TRUE; - // wake up waiting thread, if any - thread = secret->md.read.thread; + // wake up waiting thread, if any + readThread = secret->md.read.thread; secret->md.read.thread = NULL; secret->md.read.cookie = cookie; - break; + break; case T_EXDATA: // Expedited data is available PR_ASSERT(!"T_EXDATA Not implemented"); - return; + return; case T_DISCONNECT: // A disconnect is available discon.udata.len = 0; err = OTRcvDisconnect(endpoint, &discon); PR_ASSERT(err == kOTNoError); - secret->md.exceptReady = PR_TRUE; + secret->md.exceptReady = PR_TRUE; // XXX Check this - // wake up waiting threads, if any - result = -3199 - discon.reason; // obtain the negative error code + // wake up waiting threads, if any + result = -3199 - discon.reason; // obtain the negative error code + if ((readThread = secret->md.read.thread) != NULL) { + secret->md.read.thread = NULL; + secret->md.read.cookie = cookie; + } - if ((thread = secret->md.read.thread) != NULL) { - secret->md.read.thread = NULL; - secret->md.read.cookie = cookie; - WakeUpNotifiedThread(thread, result); - } - - if ((thread = secret->md.write.thread) != NULL) { - secret->md.write.thread = NULL; - secret->md.write.cookie = cookie; - WakeUpNotifiedThread(thread, result); - } - - thread = NULL; // already took care of notification here + if ((writeThread = secret->md.write.thread) != NULL) { + secret->md.write.thread = NULL; + secret->md.write.cookie = cookie; + } break; - + case T_ERROR: // obsolete/unused in library PR_ASSERT(!"T_ERROR Not implemented"); - return; - + return; + case T_UDERR: // UDP Send error; clear the error - (void) OTRcvUDErr((EndpointRef) cookie, NULL); + (void) OTRcvUDErr((EndpointRef) cookie, NULL); break; case T_ORDREL: // An orderly release is available @@ -398,28 +395,27 @@ static pascal void NotifierRoutine(void * contextPtr, OTEventCode code, OTResul PR_ASSERT(err == kOTNoError); secret->md.readReady = PR_TRUE; // mark readable (to emulate bsd sockets) // remember connection is closed, so we can return 0 on read or receive - secret->md.orderlyDisconnect = PR_TRUE; - - thread = secret->md.read.thread; - secret->md.read.thread = NULL; - secret->md.read.cookie = cookie; - + secret->md.orderlyDisconnect = PR_TRUE; + + readThread = secret->md.read.thread; + secret->md.read.thread = NULL; + secret->md.read.cookie = cookie; break; case T_GODATA: // Flow control lifted on standard data secret->md.writeReady = PR_TRUE; - resultOT = OTLook(endpoint); // clear T_GODATA event - PR_ASSERT(resultOT == T_GODATA); - - // wake up waiting thread, if any - thread = secret->md.write.thread; + resultOT = OTLook(endpoint); // clear T_GODATA event + PR_ASSERT(resultOT == T_GODATA); + + // wake up waiting thread, if any + writeThread = secret->md.write.thread; secret->md.write.thread = NULL; secret->md.write.cookie = cookie; break; case T_GOEXDATA: // Flow control lifted on expedited data PR_ASSERT(!"T_GOEXDATA Not implemented"); - return; + return; case T_REQUEST: // An Incoming request is available PR_ASSERT(!"T_REQUEST Not implemented"); @@ -430,13 +426,13 @@ static pascal void NotifierRoutine(void * contextPtr, OTEventCode code, OTResul return; case T_PASSCON: // State is now T_DATAXFER - // OTAccept() complete, receiving endpoint in T_DATAXFER state - // cookie = OTAccept() resRef parameter - break; + // OTAccept() complete, receiving endpoint in T_DATAXFER state + // cookie = OTAccept() resRef parameter + break; case T_RESET: // Protocol has been reset PR_ASSERT(!"T_RESET Not implemented"); - return; + return; // Async Completion Events case T_BINDCOMPLETE: @@ -444,39 +440,39 @@ static pascal void NotifierRoutine(void * contextPtr, OTEventCode code, OTResul case T_ACCEPTCOMPLETE: case T_OPTMGMTCOMPLETE: case T_GETPROTADDRCOMPLETE: - thread = secret->md.misc.thread; + readThread = secret->md.misc.thread; secret->md.misc.thread = NULL; secret->md.misc.cookie = cookie; break; -// case T_OPENCOMPLETE: // we open endpoints in synchronous mode +// case T_OPENCOMPLETE: // we open endpoints in synchronous mode // case T_REPLYCOMPLETE: -// case T_DISCONNECTCOMPLETE: // we don't call OTSndDisconnect() +// case T_DISCONNECTCOMPLETE: // we don't call OTSndDisconnect() // case T_RESOLVEADDRCOMPLETE: // case T_GETINFOCOMPLETE: // case T_SYNCCOMPLETE: -// case T_MEMORYRELEASED: // only if OTAckSends() called on endpoint +// case T_MEMORYRELEASED: // only if OTAckSends() called on endpoint // case T_REGNAMECOMPLETE: // case T_DELNAMECOMPLETE: // case T_LKUPNAMECOMPLETE: // case T_LKUPNAMERESULT: - // OpenTptInternet.h -// case T_DNRSTRINGTOADDRCOMPLETE: // DNS is handled by dnsContext in DNSNotifierRoutine() + // OpenTptInternet.h +// case T_DNRSTRINGTOADDRCOMPLETE: // DNS is handled by dnsContext in DNSNotifierRoutine() // case T_DNRADDRTONAMECOMPLETE: // case T_DNRSYSINFOCOMPLETE: // case T_DNRMAILEXCHANGECOMPLETE: // case T_DNRQUERYCOMPLETE: default: - // we should probably have a bit more sophisticated handling of kOTSystemSleep, etc. - // PR_ASSERT(code != 0); + // we should probably have a bit more sophisticated handling of kOTSystemSleep, etc. + // PR_ASSERT(code != 0); return; } - if (pollThread) - WakeUpNotifiedThread(pollThread, kOTNoError); + if (readThread) + WakeUpNotifiedThread(readThread, result); - if (thread && (thread != pollThread)) - WakeUpNotifiedThread(thread, result); + if (writeThread && (writeThread != readThread)) + WakeUpNotifiedThread(writeThread, result); } @@ -488,8 +484,8 @@ static OSErr CreateSocket(int type, EndpointRef *endpoint) OTConfiguration *config; EndpointRef ep; - // for now we just create the endpoint - // we'll make it asynchronous and give it a notifier routine in _MD_makenonblock() + // for now we just create the endpoint + // we'll make it asynchronous and give it a notifier routine in _MD_makenonblock() switch (type){ case SOCK_STREAM: configName = kTCPName; break; @@ -519,7 +515,7 @@ PRInt32 _MD_socket(int domain, int type, int protocol) OSStatus err; EndpointRef endpoint; - _MD_FinishInitNetAccess(); + _MD_FinishInitNetAccess(); // We only deal with internet domain if (domain != AF_INET) { @@ -1171,8 +1167,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); @@ -1349,7 +1345,8 @@ PRInt32 _MD_connect(PRFileDesc *fd, PRNetAddr *addr, PRUint32 addrlen, PRInterva sndCall.addr.buf = (UInt8*) addr; if (!fd->secret->nonblocking) { - PrepareForAsyncCompletion(me, fd->secret->md.osfd); + PrepareForAsyncCompletion(me, fd->secret->md.osfd); + PR_ASSERT(fd->secret->md.write.thread == NULL); fd->secret->md.write.thread = me; } @@ -1407,7 +1404,10 @@ static PRInt32 SendReceiveStream(PRFileDesc *fd, void *buf, PRInt32 amount, err = kEFAULTErr; goto ErrorExit; } - + + PR_ASSERT(opCode == kSTREAM_SEND ? fd->secret->md.write.thread == NULL : + fd->secret->md.read.thread == NULL); + while (bytesLeft > 0) { Boolean disabledNotifications = OTEnterNotifier(endpoint); @@ -1416,7 +1416,6 @@ static PRInt32 SendReceiveStream(PRFileDesc *fd, void *buf, PRInt32 amount, if (opCode == kSTREAM_SEND) { do { - fd->secret->md.write.thread = me; fd->secret->md.writeReady = PR_FALSE; // expect the worst result = OTSnd(endpoint, buf, bytesLeft, NULL); @@ -1500,8 +1499,10 @@ static PRInt32 SendReceiveStream(PRFileDesc *fd, void *buf, PRInt32 amount, if (result > 0) { buf = (void *) ( (UInt32) buf + (UInt32)result ); bytesLeft -= result; - if (opCode == kSTREAM_RECEIVE) - return result; + if (opCode == kSTREAM_RECEIVE) { + amount = result; + goto NormalExit; + } } else { switch (result) { case kOTLookErr: @@ -1513,8 +1514,15 @@ static PRInt32 SendReceiveStream(PRFileDesc *fd, void *buf, PRInt32 amount, case kEAGAINErr: case kEWOULDBLOCKErr: if (fd->secret->nonblocking) { - err = result; - goto ErrorExit; + + if (bytesLeft == amount) { // no data was sent + err = result; + goto ErrorExit; + } + + // some data was sent + amount -= bytesLeft; + goto NormalExit; } WaitOnThisThread(me, timeout); @@ -1524,8 +1532,11 @@ static PRInt32 SendReceiveStream(PRFileDesc *fd, void *buf, PRInt32 amount, break; case kOTOutStateErr: // if provider already closed, fall through to handle error - if (fd->secret->md.orderlyDisconnect) - return 0; + if (fd->secret->md.orderlyDisconnect) { + amount = 0; + goto NormalExit; + } + // else fall through default: err = result; goto ErrorExit; @@ -1533,30 +1544,31 @@ static PRInt32 SendReceiveStream(PRFileDesc *fd, void *buf, PRInt32 amount, } } - PR_ASSERT(opCode == kSTREAM_SEND ? fd->secret->md.write.thread == nil : - fd->secret->md.read.thread == nil); +NormalExit: + PR_ASSERT(opCode == kSTREAM_SEND ? fd->secret->md.write.thread == NULL : + fd->secret->md.read.thread == NULL); return amount; ErrorExit: - PR_ASSERT(opCode == kSTREAM_SEND ? fd->secret->md.write.thread == nil : - fd->secret->md.read.thread == nil); + PR_ASSERT(opCode == kSTREAM_SEND ? fd->secret->md.write.thread == NULL : + fd->secret->md.read.thread == NULL); macsock_map_error(err); return -1; -} +} PRInt32 _MD_recv(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout) { return (SendReceiveStream(fd, buf, amount, flags, timeout, kSTREAM_RECEIVE)); -} +} PRInt32 _MD_send(PRFileDesc *fd,const void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout) { return (SendReceiveStream(fd, (void *)buf, amount, flags, timeout, kSTREAM_SEND)); -} +} // Errors: @@ -1638,7 +1650,7 @@ static PRInt32 SendReceiveDgram(PRFileDesc *fd, void *buf, PRInt32 amount, ErrorExit: macsock_map_error(err); return -1; -} +} PRInt32 _MD_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount, @@ -1647,7 +1659,7 @@ PRInt32 _MD_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount, { return (SendReceiveDgram(fd, buf, amount, flags, addr, addrlen, timeout, kDGRAM_RECEIVE)); -} +} PRInt32 _MD_sendto(PRFileDesc *fd,const void *buf, PRInt32 amount, @@ -1656,7 +1668,7 @@ PRInt32 _MD_sendto(PRFileDesc *fd,const void *buf, PRInt32 amount, { return (SendReceiveDgram(fd, (void *)buf, amount, flags, addr, &addrlen, timeout, kDGRAM_SEND)); -} +} PRInt32 _MD_closesocket(PRInt32 osfd) @@ -1683,7 +1695,7 @@ PRInt32 _MD_closesocket(PRInt32 osfd) ErrorExit: macsock_map_error(err); return -1; -} +} PRInt32 _MD_writev(PRFileDesc *fd, const struct PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout) @@ -1693,9 +1705,11 @@ PRInt32 _MD_writev(PRFileDesc *fd, const struct PRIOVec *iov, PRInt32 iov_size, PR_ASSERT(0); _PR_MD_CURRENT_THREAD()->md.osErrCode = unimpErr; return -1; -} - +} +// OT endpoint states are documented here: +// http://gemma.apple.com/techpubs/mac/NetworkingOT/NetworkingWOT-27.html#MARKER-9-65 +// static PRBool GetState(PRFileDesc *fd, PRBool *readReady, PRBool *writeReady, PRBool *exceptReady) { OTResult resultOT; @@ -1706,14 +1720,32 @@ static PRBool GetState(PRFileDesc *fd, PRBool *readReady, PRBool *writeReady, PR OTCountDataBytes((EndpointRef)fd->secret->md.osfd, &availableData); *readReady = fd->secret->md.readReady && (availableData > 0); - *exceptReady = fd->secret->md.exceptReady; + *exceptReady = fd->secret->md.exceptReady; resultOT = OTGetEndpointState((EndpointRef)fd->secret->md.osfd); - switch (resultOT) { - case T_DATAXFER: - case T_INREL: - *writeReady = PR_TRUE; + switch (resultOT) { + case T_IDLE: + case T_UNBND: + // the socket is not connected. Emulating BSD sockets, + // we mark it readable and writable. The next PR_Read + // or PR_Write will then fail. Usually, in this situation, + // fd->secret->md.exceptReady is also set, and returned if + // anyone is polling for it. + *readReady = PR_FALSE; + *writeReady = PR_FALSE; break; + + case T_DATAXFER: // data transfer + *writeReady = fd->secret->md.writeReady; + break; + + case T_INREL: // incoming orderly release + *writeReady = fd->secret->md.writeReady; + break; + + case T_OUTCON: // outgoing connection pending + case T_INCON: // incoming connection pending + case T_OUTREL: // outgoing orderly release default: *writeReady = PR_FALSE; } @@ -1811,7 +1843,24 @@ static void SetDescPollThread(PRPollDesc *pds, PRIntn npds, PRThread* thread) PRFileDesc *bottomFD = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); if (bottomFD && (_PR_FILEDESC_OPEN == bottomFD->secret->state)) { - bottomFD->secret->md.poll.thread = thread; + if (pd->in_flags & PR_POLL_READ) { + PR_ASSERT(thread == NULL || bottomFD->secret->md.read.thread == NULL); + bottomFD->secret->md.read.thread = thread; + } + + if (pd->in_flags & PR_POLL_WRITE) { + // it's possible for the writing thread to be non-null during + // a non-blocking connect, so we assert that we're on + // the same thread, or the thread is null. + // Note that it's strictly possible for the connect and poll + // to be on different threads, so ideally we need to assert + // that if md.write.thread is non-null, there is a non-blocking + // connect in progress. + PR_ASSERT(thread == NULL || + (bottomFD->secret->md.write.thread == NULL || + bottomFD->secret->md.write.thread == thread)); + bottomFD->secret->md.write.thread = thread; + } } } } @@ -1822,9 +1871,8 @@ PRInt32 _MD_poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) PRThread *thread = _PR_MD_CURRENT_THREAD(); intn is; PRInt32 ready; - OSErr result; - if (timeout == PR_INTERVAL_NO_WAIT) { + if (timeout == PR_INTERVAL_NO_WAIT) { return CheckPollDescs(pds, npds); } @@ -1835,7 +1883,7 @@ PRInt32 _MD_poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) // need to set up the thread PrepareForAsyncCompletion(thread, 0); - SetDescPollThread(pds, npds, thread); + SetDescPollThread(pds, npds, thread); ready = CheckPollDescs(pds, npds); PR_Unlock(thread->md.asyncIOLock); @@ -1843,13 +1891,8 @@ PRInt32 _MD_poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) if (ready == 0) { WaitOnThisThread(thread, timeout); - result = thread->md.osErrCode; - if (result != noErr && result != kETIMEDOUTErr) { - PR_ASSERT(0); /* debug: catch unexpected errors */ - ready = -1; - } else { - ready = CheckPollDescs(pds, npds); - } + ready = CheckPollDescs(pds, npds); + } else { thread->io_pending = PR_FALSE; } @@ -1937,7 +1980,7 @@ PR_IMPLEMENT(PRInt32) _MD_shutdown(PRFileDesc *fd, PRIntn how) /* Just succeed silently!!! */ return (0); -} +} PR_IMPLEMENT(PRStatus) @@ -1987,7 +2030,7 @@ _MD_getpeername(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen) ErrorExit: macsock_map_error(err); return PR_FAILURE; -} +} PR_IMPLEMENT(unsigned long) inet_addr(const char *cp) @@ -1995,7 +2038,7 @@ PR_IMPLEMENT(unsigned long) inet_addr(const char *cp) OSStatus err; InetHost host; - _MD_FinishInitNetAccess(); + _MD_FinishInitNetAccess(); err = OTInetStringToHost((char*) cp, &host); if (err != kOTNoError) @@ -2067,7 +2110,7 @@ PR_IMPLEMENT(struct hostent *) gethostbyaddr(const void *addr, int addrlen, int PR_IMPLEMENT(char *) inet_ntoa(struct in_addr addr) { - _MD_FinishInitNetAccess(); + _MD_FinishInitNetAccess(); OTInetHostToString((InetHost)addr.s_addr, sHostInfo.name); @@ -2080,7 +2123,7 @@ PRStatus _MD_gethostname(char *name, int namelen) OSStatus err; InetInterfaceInfo info; - _MD_FinishInitNetAccess(); + _MD_FinishInitNetAccess(); /* * On a Macintosh, we don't have the concept of a local host name. @@ -2164,8 +2207,8 @@ int _MD_mac_get_nonblocking_connect_error(PRInt32 osfd) case T_IDLE: return -1; case T_INREL: - macsock_map_error(ENOTCONN); - return -1; + macsock_map_error(ENOTCONN); + return -1; default: PR_ASSERT(0); return -1; 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 -#include +#include #include #include #include +#include +#include +#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 + * + * 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 + * + * 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 +#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(); } diff --git a/pr/src/md/unix/unix_errors.c b/pr/src/md/unix/unix_errors.c index 7164675b..1ce52e68 100644 --- a/pr/src/md/unix/unix_errors.c +++ b/pr/src/md/unix/unix_errors.c @@ -383,9 +383,14 @@ void _MD_unix_map_rmdir_error(int err) PRErrorCode prError; switch (err) { + /* + * On AIX 4.3, ENOTEMPTY is defined as EEXIST. + */ +#if ENOTEMPTY != EEXIST case ENOTEMPTY: prError = PR_DIRECTORY_NOT_EMPTY_ERROR; break; +#endif case EEXIST: prError = PR_DIRECTORY_NOT_EMPTY_ERROR; break; diff --git a/pr/src/misc/prnetdb.c b/pr/src/misc/prnetdb.c index 22a15dae..c8d33900 100644 --- a/pr/src/misc/prnetdb.c +++ b/pr/src/misc/prnetdb.c @@ -84,7 +84,7 @@ PRLock *_pr_dnsLock = NULL; * an int. */ -#if defined(SOLARIS) \ +#if defined(SOLARIS) || (defined(BSDI) && defined(_REENTRANT)) \ || (defined(LINUX) && defined(_REENTRANT) \ && !(defined(__GLIBC__) && __GLIBC__ >= 2)) #define _PR_HAVE_GETPROTO_R diff --git a/pr/src/misc/prtime.c b/pr/src/misc/prtime.c index 33df013f..e31ba74b 100644 --- a/pr/src/misc/prtime.c +++ b/pr/src/misc/prtime.c @@ -1583,12 +1583,13 @@ PR_ParseTimeString( secs = mktime(&localTime); if (secs != (time_t) -1) { -#if defined(XP_MAC) +#if defined(XP_MAC) && (__MSL__ < 0x6000) /* * The mktime() routine in MetroWerks MSL C * Runtime library returns seconds since midnight, - * 1 Jan. 1900, not 1970. So we need to adjust - * its return value to the NSPR epoch. + * 1 Jan. 1900, not 1970 - in versions of MSL (Metrowerks Standard + * Library) prior to version 6. Only for older versions of + * MSL do we adjust the value of secs to the NSPR epoch */ secs -= ((365 * 70UL) + 17) * 24 * 60 * 60; #endif diff --git a/pr/src/pthreads/ptsynch.c b/pr/src/pthreads/ptsynch.c index ecf69a1a..4400e8df 100644 --- a/pr/src/pthreads/ptsynch.c +++ b/pr/src/pthreads/ptsynch.c @@ -444,6 +444,8 @@ PR_IMPLEMENT(PRMonitor*) PR_NewMonitor(void) rv = _PT_PTHREAD_MUTEX_INIT(mon->lock.mutex, _pt_mattr); PR_ASSERT(0 == rv); + _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner); + mon->cvar = cvar; rv = _PT_PTHREAD_COND_INIT(mon->cvar->cv, _pt_cvar_attr); PR_ASSERT(0 == rv); @@ -484,42 +486,43 @@ PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon) */ PR_IMPLEMENT(PRInt32) PR_GetMonitorEntryCount(PRMonitor *mon) { - PRThread *self = PR_GetCurrentThread(); - if (mon->owner == self) + pthread_t self = pthread_self(); + if (pthread_equal(mon->owner, self)) return mon->entryCount; return 0; } PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon) { - PRThread *self = PR_GetCurrentThread(); + pthread_t self = pthread_self(); PR_ASSERT(mon != NULL); /* - * This is safe only if mon->owner (a PRThread*) can be - * read in one instruction. + * This is safe only if mon->owner (a pthread_t) can be + * read in one instruction. Perhaps mon->owner should be + * a "PRThread *"? */ - if (mon->owner != self) + if (!pthread_equal(mon->owner, self)) { PR_Lock(&mon->lock); /* and now I have the lock */ PR_ASSERT(0 == mon->entryCount); - PR_ASSERT(NULL == mon->owner); - mon->owner = self; + PR_ASSERT(_PT_PTHREAD_THR_HANDLE_IS_INVALID(mon->owner)); + _PT_PTHREAD_COPY_THR_HANDLE(self, mon->owner); } mon->entryCount += 1; } /* PR_EnterMonitor */ PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon) { - PRThread *self = PR_GetCurrentThread(); + pthread_t self = pthread_self(); PR_ASSERT(mon != NULL); /* The lock better be that - locked */ PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex)); /* we'd better be the owner */ - PR_ASSERT(mon->owner == self); - if (mon->owner != self) + PR_ASSERT(pthread_equal(mon->owner, self)); + if (!pthread_equal(mon->owner, self)) return PR_FAILURE; /* if it's locked and we have it, then the entries should be > 0 */ @@ -528,7 +531,7 @@ PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon) if (mon->entryCount == 0) { /* and if it transitioned to zero - unlock */ - mon->owner = NULL; /* make the owner unknown */ + _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner); /* make the owner unknown */ PR_Unlock(&mon->lock); } return PR_SUCCESS; @@ -538,7 +541,7 @@ PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime timeout) { PRStatus rv; PRInt16 saved_entries; - PRThread *saved_owner; + pthread_t saved_owner; PR_ASSERT(mon != NULL); /* we'd better be locked */ @@ -546,19 +549,19 @@ PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime timeout) /* and the entries better be positive */ PR_ASSERT(mon->entryCount > 0); /* and it better be by us */ - PR_ASSERT(mon->owner == PR_GetCurrentThread()); + PR_ASSERT(pthread_equal(mon->owner, pthread_self())); /* tuck these away 'till later */ saved_entries = mon->entryCount; mon->entryCount = 0; - saved_owner = mon->owner; - mon->owner = NULL; + _PT_PTHREAD_COPY_THR_HANDLE(mon->owner, saved_owner); + _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner); rv = PR_WaitCondVar(mon->cvar, timeout); /* reinstate the intresting information */ mon->entryCount = saved_entries; - mon->owner = saved_owner; + _PT_PTHREAD_COPY_THR_HANDLE(saved_owner, mon->owner); return rv; } /* PR_Wait */ @@ -571,7 +574,7 @@ PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor *mon) /* and the entries better be positive */ PR_ASSERT(mon->entryCount > 0); /* and it better be by us */ - PR_ASSERT(mon->owner == PR_GetCurrentThread()); + PR_ASSERT(pthread_equal(mon->owner, pthread_self())); pt_PostNotifyToCvar(mon->cvar, PR_FALSE); @@ -586,7 +589,7 @@ PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor *mon) /* and the entries better be positive */ PR_ASSERT(mon->entryCount > 0); /* and it better be by us */ - PR_ASSERT(mon->owner == PR_GetCurrentThread()); + PR_ASSERT(pthread_equal(mon->owner, pthread_self())); pt_PostNotifyToCvar(mon->cvar, PR_TRUE); -- cgit v1.2.1