summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcvs2hg <devnull@localhost>2001-10-09 02:16:58 +0000
committercvs2hg <devnull@localhost>2001-10-09 02:16:58 +0000
commit58601f660e420b0aa1ece5d5228659eb25ce20b7 (patch)
treef3e0289d6db98db7b42570cc594cabaffaee4d33
parentf7b843b8dec17be6d612b30c52e39cfb35e62848 (diff)
downloadnspr-hg-NETSCAPE_6_2_3_BRANCH.tar.gz
fixup commit for branch 'NETSCAPE_6_2_3_BRANCH'NETSCAPE_6_2_3_BRANCH
-rwxr-xr-xconfig/nspr-config.in2
-rw-r--r--config/prdepend.h1
-rw-r--r--macbuild/NSPR20PPC.mcpbin167357 -> 163969 bytes
-rw-r--r--pr/include/md/_macos.h55
-rw-r--r--pr/include/md/_pth.h46
-rw-r--r--pr/include/private/primpl.h12
-rw-r--r--pr/src/md/mac/macio.c72
-rw-r--r--pr/src/md/mac/macrng.c7
-rw-r--r--pr/src/md/mac/macsockotpt.c309
-rw-r--r--pr/src/md/mac/macthr.c223
-rw-r--r--pr/src/md/mac/mdcriticalregion.c169
-rw-r--r--pr/src/md/mac/mdcriticalregion.h56
-rw-r--r--pr/src/md/mac/mdmac.c3
-rw-r--r--pr/src/md/unix/unix_errors.c5
-rw-r--r--pr/src/misc/prnetdb.c2
-rw-r--r--pr/src/misc/prtime.c7
-rw-r--r--pr/src/pthreads/ptsynch.c41
17 files changed, 774 insertions, 236 deletions
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
--- a/macbuild/NSPR20PPC.mcp
+++ b/macbuild/NSPR20PPC.mcp
Binary files differ
diff --git a/pr/include/md/_macos.h b/pr/include/md/_macos.h
index 2cc737d7..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 <http://developer.apple.com/technotes/fl/fl_515.html>);
+ ** 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 <Events.h>
#include <OSUtils.h>
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 <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();
}
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);