implement a hacky version of timed waits on condition variables using another thread, and get the majority of the tests working. RHAPSODY_NSPR_BRANCH
authortoshok
Tue, 05 May 1998 17:00:31 +0000
branchRHAPSODY_NSPR_BRANCH
changeset 89 ee61d81e87b483776ed7d309dec9d4693ebf6ff5
parent 88 6a0906eecc8aad9c05eb4705aade5b11b0a0f809
child 95 c76cedb83a30687401992c6ef151dfee5d8cdae3
push idunknown
push userunknown
push dateunknown
implement a hacky version of timed waits on condition variables using another thread, and get the majority of the tests working.
pr/include/private/primpl.h
pr/src/Makefile
pr/src/cthreads/Makefile
pr/src/cthreads/ctmisc.c
pr/src/cthreads/ctsynch.c
pr/src/cthreads/ctthread.c
pr/src/cthreads/cttwait.c
--- a/pr/include/private/primpl.h
+++ b/pr/include/private/primpl.h
@@ -1376,20 +1376,16 @@ struct PRThread {
 #endif
 #elif defined(_PR_CTHREADS)
         cthread_t id;                   /* pthread identifier for the thread */
     PRBool okToDelete;              /* ok to delete the PRThread struct? */
         PRCondVar *waiting;             /* where the thread is waiting | NULL */
         void *sp;                                               /* recorded sp for garbage collection */
         PRThread *next, *prev;          /* simple linked list of all threads */
         PRUint32 suspend;                       /* used to store suspend and resume flags */
-#ifdef PT_NO_SIGTIMEDWAIT
-    mutex_t suspendResumeMutex;
-    condition_t suspendResumeCV;
-#endif
 #else /* defined(_PR_PTHREADS) || defined(_PR_CTHREADS) */
     _MDLock threadLock;             /* Lock to protect thread state variables.
                                     * Protects the following fields:
                                     *     state
                                     *     priority
                                     *     links
                                     *     wait
                                     *     cpu
--- a/pr/src/Makefile
+++ b/pr/src/Makefile
@@ -173,17 +173,18 @@ OBJS = \
     misc/$(OBJDIR)/prthinfo.$(OBJ_SUFFIX) \
     misc/$(OBJDIR)/prtime.$(OBJ_SUFFIX)
 
 ifdef USE_CTHREADS
 OBJS += \
     cthreads/$(OBJDIR)/ctsynch.$(OBJ_SUFFIX) \
     cthreads/$(OBJDIR)/ctio.$(OBJ_SUFFIX) \
     cthreads/$(OBJDIR)/ctthread.$(OBJ_SUFFIX) \
-    cthreads/$(OBJDIR)/ctmisc.$(OBJ_SUFFIX)
+    cthreads/$(OBJDIR)/ctmisc.$(OBJ_SUFFIX) \
+    cthreads/$(OBJDIR)/cttwait.$(OBJ_SUFFIX)
 else
 ifdef USE_PTHREADS
 OBJS += \
     pthreads/$(OBJDIR)/ptsynch.$(OBJ_SUFFIX) \
     pthreads/$(OBJDIR)/ptio.$(OBJ_SUFFIX) \
     pthreads/$(OBJDIR)/ptthread.$(OBJ_SUFFIX) \
     pthreads/$(OBJDIR)/ptmisc.$(OBJ_SUFFIX)
 else
--- a/pr/src/cthreads/Makefile
+++ b/pr/src/cthreads/Makefile
@@ -21,16 +21,17 @@ MOD_DEPTH = ../../..
 
 include $(MOD_DEPTH)/config/config.mk
 
 CSRCS = \
 	ctio.c \
 	ctsynch.c \
 	ctthread.c \
 	ctmisc.c \
+        cttwait.c \
 	$(NULL)
 
 TARGETS	= $(OBJS)
 
 INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
 
 include $(MOD_DEPTH)/config/rules.mk
 
--- a/pr/src/cthreads/ctmisc.c
+++ b/pr/src/cthreads/ctmisc.c
@@ -24,17 +24,17 @@
 #if defined(_PR_CTHREADS)
 
 #include "primpl.h"
 
 #include <stdio.h>
 
 #if defined(_PR_NO_CTHREAD_KEY_T)
 
-#define MAX_KEYS 15
+#define MAX_KEYS 255
 
 static int cur_key;
 
 typedef void (*destructor_t)(any_t);
 
 static destructor_t key_destructors[MAX_KEYS];
 static mutex_t key_mutex = NULL;
 
@@ -65,60 +65,63 @@ cthread_key_create(cthread_key_t *key, d
   key_destructors[cur_key] = destructor;
 
   cur_key ++;
 
   mutex_unlock(key_mutex);
   return 0;
 }
 
-int
-cthread_setspecific(cthread_key_t key, any_t value)
+static any_t *
+get_tpd_array()
 {
   any_t *key_values = cthread_data(cthread_self());
 
   if (!key_values)
   {
     key_values = (any_t*)calloc(MAX_KEYS, sizeof(any_t));
 
     if (!key_values) return 1;
 
     cthread_set_data(cthread_self(), key_values);
   }
 
+  return key_values;
+}
+
+int
+cthread_setspecific(cthread_key_t key, any_t value)
+{
+  any_t *key_values = get_tpd_array();
+
+  if (key_destructors[key] && key_values[key])
+    (*key_destructors[key])(key_values[key]);
+
   key_values[key] = value;
+
   return 0;
 }
 
 int
 cthread_getspecific(cthread_key_t key, any_t value)
 {
-  any_t *key_values = cthread_data(cthread_self());
-
-  if (!key_values)
-  {
-    key_values = (any_t*)calloc(MAX_KEYS, sizeof(any_t));
-
-    if (!key_values) return 1;
-
-    cthread_set_data(cthread_self(), key_values);
-  }
+  any_t *key_values = get_tpd_array();
 
   *((any_t*)value) = key_values[key];
   return 0;
 }
 
 void
 _cthread_callkeydestructors()
 {
   any_t *key_values = cthread_data(cthread_self());
   int i;
 
   for (i = 0; i < MAX_KEYS; i ++)
-    if (key_destructors[i]) (*key_destructors[i])(key_values[i]);
+    if (key_destructors[i] && key_values[i]) (*key_destructors[i])(key_values[i]);
 
   free(key_values);
 }
 
 #endif /* _PR_CTHREAD_NO_CTHREAD_KEY_T */
 
 #if 0 && defined(DEBUG)
 #define CT_LOG(f) \
--- a/pr/src/cthreads/ctsynch.c
+++ b/pr/src/cthreads/ctsynch.c
@@ -37,17 +37,17 @@
 /**************************************************************/
 /**************************************************************/
 
 void _PR_InitLocks(void)
 {
     _PR_MD_INIT_LOCKS();
 }
 
-static void pt_PostNotifies(PRLock *lock, PRBool unlock)
+static void ct_PostNotifies(PRLock *lock, PRBool unlock)
 {
     PRIntn index, rv;
     _CT_Notified post;
     _CT_Notified *notified, *prev = NULL;
     /*
      * Time to actually notify any conditions that were affected
      * while the lock was held. Get a copy of the list that's in
      * the lock structure and then zero the original. If it's
@@ -87,17 +87,17 @@ static void pt_PostNotifies(PRLock *lock
                     condition_signal(notified->cv[index].cv->cv);
                 }
             }
         }
         prev = notified;
         notified = notified->link;
         if (&post != prev) PR_DELETE(prev);
     } while (NULL != notified);
-}  /* pt_PostNotifies */
+}  /* ct_PostNotifies */
 
 PR_IMPLEMENT(PRLock*) PR_NewLock(void)
 {
     PRLock *lock;
 
     if (!_pr_initialized) _PR_ImplicitInitialization();
 
     lock = PR_NEWZAP(PRLock);
@@ -146,40 +146,41 @@ PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock 
     if (!lock->owner == cthread_self())
         return PR_FAILURE;
 
     CTHREAD_ZERO_THR_HANDLE(lock->owner);
     if (0 == lock->notified.length)  /* shortcut */
     {
         mutex_unlock(lock->mutex);
     }
-    else pt_PostNotifies(lock, PR_TRUE);
+    else ct_PostNotifies(lock, PR_TRUE);
 
     return PR_SUCCESS;
 }  /* PR_Unlock */
 
 
 /**************************************************************/
 /**************************************************************/
 /***************************CONDITIONS*************************/
 /**************************************************************/
 /**************************************************************/
 
-#if 0
 /*
  * This code is used to compute the absolute time for the wakeup.
  * It's moderately ugly, so it's defined here and called in a
  * couple of places.
  */
 #define CT_NANOPERMICRO 1000UL
 #define CT_BILLION 1000000000UL
 
+#if 0
 static PRStatus ct_TimedWait(
     condition_t cv, mutex_t ml, PRIntervalTime timeout)
 {
+#if 0
     int rv;
     struct timeval now;
     struct timespec tmo;
     PRUint32 ticks = PR_TicksPerSecond();
 
     tmo.tv_sec = timeout / ticks;
     tmo.tv_nsec = timeout - (tmo.tv_sec * ticks);
     tmo.tv_nsec = PR_IntervalToMicroseconds(CT_NANOPERMICRO * tmo.tv_nsec);
@@ -199,22 +200,22 @@ static PRStatus ct_TimedWait(
     rv = pthread_cond_timedwait(cv, ml, &tmo);
 
     /* NSPR doesn't report timeouts */
 #ifdef _PR_DCETHREADS
     return (rv == -1 && errno == EAGAIN) ? 0 : rv;
 #else
     return (rv == ETIMEDOUT) ? 0 : rv;
 #endif
+#endif
 }  /* ct_TimedWait */
 #endif
 
-
 /*
- * Notifies just get posted to the to the protecting mutex. The
+ * Notifies just get posted to the protecting mutex. The
  * actual notification is done when the lock is released so that
  * MP systems don't contend for a lock that they can't have.
  */
 static void ct_PostNotifyToCvar(PRCondVar *cvar, PRBool broadcast)
 {
     PRIntn index = 0;
     _CT_Notified *notified = &cvar->lock->notified;
 
@@ -276,19 +277,17 @@ PR_IMPLEMENT(void) PR_DestroyCondVar(PRC
 #if defined(DEBUG)
         memset(cvar, 0xaf, sizeof(PRCondVar));
 #endif
     PR_DELETE(cvar);
 }  /* PR_DestroyCondVar */
 
 PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout)
 {
-#if 0
     PRStatus rv;
-#endif
     PRThread *thred = PR_CurrentThread();
 
     PR_ASSERT(cvar != NULL);
     /* We'd better be locked */
     PR_ASSERT(CTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex));
     /* and it better be by us */
     PR_ASSERT(cvar->lock->owner == cthread_self());
 
@@ -309,49 +308,44 @@ PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PR
      * If we have pending notifies, post them now.
      *
      * This is not optimal. We're going to post these notifies
      * while we're holding the lock. That means on MP systems
      * that they are going to collide for the lock that we will
      * hold until we actually wait.
      */
     if (0 != cvar->lock->notified.length)
-        pt_PostNotifies(cvar->lock, PR_FALSE);
+        ct_PostNotifies(cvar->lock, PR_FALSE);
 
     /*
      * We're surrendering the lock, so clear out the owner field.
      */
     CTHREAD_ZERO_THR_HANDLE(cvar->lock->owner);
 
-#if 0 /* XXX FIX ME */
     if (timeout == PR_INTERVAL_NO_TIMEOUT)
-#endif
+    {
         condition_wait(cvar->cv, cvar->lock->mutex);
-#if 0
+        rv = 0;
+    }
     else
-        rv = ct_TimedWait(&cvar->cv, &cvar->lock->mutex, timeout);
-#endif
+        rv = ct_TimedWait(cvar->cv, cvar->lock->mutex, timeout);
 
     /* We just got the lock back - this better be empty */
     PR_ASSERT(CTHREAD_THR_HANDLE_IS_ZERO(cvar->lock->owner));
     CTHREAD_COPY_THR_HANDLE(cthread_self(), cvar->lock->owner);
 
     PR_ASSERT(0 == cvar->lock->notified.length);
     thred->waiting = NULL;  /* and now we're not */
     if (thred->state & CT_THREAD_ABORTED)
     {
         PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
         thred->state &= ~CT_THREAD_ABORTED;
         return PR_FAILURE;
     }
-#if 0
     return (rv == 0) ? PR_SUCCESS : PR_FAILURE;
-#else
-    return PR_SUCCESS;
-#endif
 }  /* PR_WaitCondVar */
 
 PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar *cvar)
 {
     PR_ASSERT(cvar != NULL);   
     ct_PostNotifyToCvar(cvar, PR_FALSE);
     return PR_SUCCESS;
 }  /* PR_NotifyCondVar */
@@ -656,35 +650,28 @@ PR_IMPLEMENT(void) PRP_DestroyNakedCondV
         memset(cvar, 0xaf, sizeof(PRCondVar));
 #endif
     PR_DELETE(cvar);
 }  /* PRP_DestroyNakedCondVar */
 
 PR_IMPLEMENT(PRStatus) PRP_NakedWait(
     PRCondVar *cvar, PRLock *ml, PRIntervalTime timeout)
 {
-#if 0
     PRStatus rv;
-#endif
     PR_ASSERT(cvar != NULL);
     /* XXX do we really want to assert this in a naked wait? */
     PR_ASSERT(CTHREAD_MUTEX_IS_LOCKED(ml->mutex));
-#if 0 /* XXX fix me -- toshok */
     if (timeout == PR_INTERVAL_NO_TIMEOUT)
-#endif
+    {
         condition_wait(cvar->cv, ml->mutex);
-#if 0
+        rv = 0;
+    }
     else
-        rv = pt_TimedWait(&cvar->cv, &ml->mutex, timeout);
-#endif
-#if 0
+        rv = ct_TimedWait(&cvar->cv, &ml->mutex, timeout);
     return (rv == 0) ? PR_SUCCESS : PR_FAILURE;
-#else
-    return PR_SUCCESS;
-#endif
 }  /* PRP_NakedWait */
 
 PR_IMPLEMENT(PRStatus) PRP_NakedNotify(PRCondVar *cvar)
 {
     PR_ASSERT(cvar != NULL);
     condition_signal(cvar->cv);
     return PR_SUCCESS;
 }  /* PRP_NakedNotify */
--- a/pr/src/cthreads/ctthread.c
+++ b/pr/src/cthreads/ctthread.c
@@ -207,29 +207,16 @@ static PRThread* _PR_CreateThread(
         if (thred->stack == NULL) {
             PR_DELETE(thred);  /* all that work ... poof! */
             thred = NULL;  /* and for what? */
             goto done;
         }
         thred->stack->stackSize = stackSize;
         thred->stack->thr = thred;
 
-#ifdef CT_NO_SIGTIMEDWAIT
-      thred->suspendResumeMutex = mutex_alloc();
-      if (thred->suspendResumeMutex)
-	{
-	  mutex_init(thred->suspendResumeMutex);
-	}
-      thred->suspendResumeCV = condition_alloc();
-      if (thred->suspendResumeCV)
-	{
-	  condition_init(thred->suspendResumeCV);
-	}
-#endif
-
 	    /* make the thread counted to the rest of the runtime */
 	    PR_Lock(ct_book.ml);
 	    if (thred->state & CT_THREAD_SYSTEM)
 	        ct_book.system += 1;
 	    else ct_book.user += 1;
 	    PR_Unlock(ct_book.ml);
 
         if ((thred->id = cthread_fork(_ct_root, thred)) == 0)
@@ -623,16 +610,17 @@ PR_IMPLEMENT(void) _PR_InitThreads(
     rv = cthread_key_create(&ct_book.key, NULL);
     PR_ASSERT(0 == rv);
     rv = cthread_setspecific(ct_book.key, thred);
     PR_ASSERT(0 == rv);
     
     PR_SetThreadPriority(thred, priority);
 
     init_cthread_gc_support();
+    ct_InitTimedWaitThread();
 
 }  /* _PR_InitThreads */
 
 PR_IMPLEMENT(PRStatus) PR_Cleanup()
 {
     PRThread *me = PR_CurrentThread();
     PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR"));
     PR_ASSERT(me->state & CT_THREAD_PRIMORD);
@@ -784,160 +772,60 @@ PR_IMPLEMENT(PRStatus) PR_EnumerateThrea
         thred = next;
     }
     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
 	   ("End PR_EnumerateThreads count = %d \n", count));
     return rv;
 }  /* PR_EnumerateThreads */
 
 /*
- * PR_SuspendAll and PR_ResumeAll are called during garbage collection.  The strategy 
- * we use is to send a SIGUSR2 signal to every gc able thread that we intend to suspend.
- * The signal handler will record the stack pointer and will block until resumed by
- * the resume call.  Since the signal handler is the last routine called for the
- * suspended thread, the stack pointer will also serve as a place where all the
- * registers have been saved on the stack for the previously executing routines.
- *
- * Through global variables, we also make sure that PR_Suspend and PR_Resume does not
- * proceed until the thread is suspended or resumed.
+ * PR_SuspendAll and PR_ResumeAll are called during garbage collection.
  */
 
-/*
- * In the signal handler, we can not use condition variable notify or wait.
- * This does not work consistently across all pthread platforms.  We also can not 
- * use locking since that does not seem to work reliably across platforms.
- * Only thing we can do is yielding while testing for a global condition
- * to change.  This does work on pthread supported platforms.  We may have
- * to play with priortities if there are any problems detected.
- */
-
- /* 
-  * In AIX, you cannot use ANY pthread calls in the signal handler except perhaps
-  * pthread_yield. But that is horribly inefficient. Hence we use only sigwait, no
-  * sigtimedwait is available. We need to use another user signal, SIGUSR1. Actually
-  * SIGUSR1 is also used by exec in Java. So our usage here breaks the exec in Java,
-  * for AIX. You cannot use pthread_cond_wait or pthread_delay_np in the signal
-  * handler as all synchronization mechanisms just break down. 
-  */
-
 static void PR_SuspendSet(PRThread *thred)
 {
-#if 0
-    PRIntn rv;
+    kern_return_t rv;
 
     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
 	   ("PR_SuspendSet thred %X thread id = %X\n", thred, thred->id));
 
 
     /*
      * Check the thread state and signal the thread to suspend
      */
 
     PR_ASSERT((thred->suspend & CT_THREAD_SUSPENDED) == 0);
 
     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
-	   ("doing pthread_kill in PR_SuspendSet thred %X tid = %X\n",
+	   ("doing thread_syspend in PR_SuspendSet thred %X tid = %X\n",
 	   thred, thred->id));
-    rv = pthread_kill (thred->id, SIGUSR2);
-    PR_ASSERT(0 == rv);
-#endif
-}
-
-static void PR_SuspendTest(PRThread *thred)
-{
-#if 0
-    PRIntn rv;
-
-    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
-	   ("Begin PR_SuspendTest thred %X thread id = %X\n", thred, thred->id));
-
+    rv = thread_suspend(cthread_thread(thred->id));
 
-    /*
-     * Wait for the thread to be really suspended. This happens when the
-     * suspend signal handler stores the stack pointer and sets the state
-     * to suspended. 
-     */
+    thred->suspend |= CT_THREAD_SUSPENDED;
 
-#if defined(CT_NO_SIGTIMEDWAIT)
-    pthread_mutex_lock(&thred->suspendResumeMutex);
-    while ((thred->suspend & CT_THREAD_SUSPENDED) == 0)
-    {
-	    pthread_cond_timedwait(
-	        &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec);
-	}
-	pthread_mutex_unlock(&thred->suspendResumeMutex);
-#else
-    while ((thred->suspend & CT_THREAD_SUSPENDED) == 0)
-    {
-		rv = sigtimedwait(&sigwait_set, NULL, &onemillisec);
-    	PR_ASSERT(-1 == rv);
-	}
-#endif
-
-    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
-        ("End PR_SuspendTest thred %X tid %X\n", thred, thred->id));
-#endif
-}  /* PR_SuspendTest */
+    PR_ASSERT(KERN_SUCCESS == rv);
+}
 
 PR_IMPLEMENT(void) PR_ResumeSet(PRThread *thred)
 {
-#if 0
     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
 	   ("PR_ResumeSet thred %X thread id = %X\n", thred, thred->id));
 
     /*
      * Clear the global state and set the thread state so that it will
      * continue past yield loop in the suspend signal handler
      */
 
     PR_ASSERT(thred->suspend & CT_THREAD_SUSPENDED);
 
-
     thred->suspend &= ~CT_THREAD_SUSPENDED;
 
-#if defined(CT_NO_SIGTIMEDWAIT)
-	pthread_kill(thred->id, SIGUSR1);
-#endif
-#endif
+    thread_resume(cthread_thread(thred->id));
 }  /* PR_ResumeSet */
 
-PR_IMPLEMENT(void) PR_ResumeTest(PRThread *thred)
-{
-#if 0
-    PRIntn rv;
-
-    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
-	   ("Begin PR_ResumeTest thred %X thread id = %X\n", thred, thred->id));
-
-    /*
-     * Wait for the threads resume state to change
-     * to indicate it is really resumed 
-     */
-#if defined(CT_NO_SIGTIMEDWAIT)
-    pthread_mutex_lock(&thred->suspendResumeMutex);
-    while ((thred->suspend & CT_THREAD_RESUMED) == 0)
-    {
-	    pthread_cond_timedwait(
-	        &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec);
-    }
-    pthread_mutex_unlock(&thred->suspendResumeMutex);
-#else
-    while ((thred->suspend & CT_THREAD_RESUMED) == 0) {
-		rv = sigtimedwait(&sigwait_set, NULL, &onemillisec);
-    	PR_ASSERT(-1 == rv);
-	}
-#endif
-
-    thred->suspend &= ~CT_THREAD_RESUMED;
-
-    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, (
-        "End PR_ResumeTest thred %X tid %X\n", thred, thred->id));
-#endif
-}  /* PR_ResumeTest */
-
 PR_IMPLEMENT(void) PR_SuspendAll()
 {
 #ifdef DEBUG
     PRIntervalTime stime, etime;
 #endif
     PRThread* thred = ct_book.first;
     PRThread *me = PR_CurrentThread();
     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n"));
@@ -951,25 +839,16 @@ PR_IMPLEMENT(void) PR_SuspendAll()
 #endif
     while (thred != NULL)
     {
 	    if ((thred != me) && (thred->state & CT_THREAD_GCABLE))
     		PR_SuspendSet(thred);
         thred = thred->next;
     }
 
-    /* Wait till they are really suspended */
-    thred = ct_book.first;
-    while (thred != NULL)
-    {
-	    if ((thred != me) && (thred->state & CT_THREAD_GCABLE))
-            PR_SuspendTest(thred);
-        thred = thred->next;
-    }
-
     suspendAllSuspended = PR_TRUE;
 
 #ifdef DEBUG
     etime = PR_IntervalNow();
     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,\
         ("End PR_SuspendAll (time %dms)\n", etime - stime));
 #endif
 }  /* PR_SuspendAll */
@@ -992,24 +871,16 @@ PR_IMPLEMENT(void) PR_ResumeAll()
 
     while (thred != NULL)
     {
 	    if ((thred != me) && (thred->state & CT_THREAD_GCABLE))
     	    PR_ResumeSet(thred);
         thred = thred->next;
     }
 
-    thred = ct_book.first;
-    while (thred != NULL)
-    {
-	    if ((thred != me) && (thred->state & CT_THREAD_GCABLE))
-    	    PR_ResumeTest(thred);
-        thred = thred->next;
-    }
-
     suspendAllOn = 0;
     PR_Unlock(ct_book.ml);
 #ifdef DEBUG
     etime = PR_IntervalNow();
     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
         ("End PR_ResumeAll (time %dms)\n", etime - stime));
 #endif
 }  /* PR_ResumeAll */
new file mode 100644
--- /dev/null
+++ b/pr/src/cthreads/cttwait.c
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL.  You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation.  Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
+ * Reserved.
+ */
+
+/*
+** File:            cttwait.c
+** Descritpion:        Implemenation for timed waits on condition variables.
+*/
+
+#if defined (_PR_CTHREADS) && defined(_PR_CTHREADS_NO_CONDITION_TIMEDWAIT)
+
+#include "primpl.h"
+#include <mach/cthreads.h>
+#include <mach/thread_switch.h>
+
+static PRThread *__ct_timed_wait_thread;
+static mutex_t __ct_timed_wait_ml;
+static condition_t __ct_timed_wait_cv;
+
+typedef struct timed_waiter {
+  PRThread *thread;
+
+  condition_t real_cv; /* the cv the thread is waiting on */
+  PRIntervalTime timeout;
+
+  /* our internal cv and mutex */
+  condition_t _cv;
+  mutex_t _ml;
+
+  struct timed_waiter *link;
+} timed_waiter;
+
+static timed_waiter *waiters;
+
+/* the number of nanoseconds we pause between wakeups */
+#define PAUSE_TIME 10000
+#define CT_NANOPERMICRO 1000UL
+
+static void PR_CALLBACK
+_timed_wait_thread_fn(void *arg)
+{
+  while (1)
+  {
+    mutex_lock(__ct_timed_wait_ml);
+
+    if (waiters)
+    {
+      PRIntervalTime interval = PAUSE_TIME;
+
+      while (waiters && waiters->timeout <= interval)
+      {
+        timed_waiter *old_head = waiters;
+  
+        condition_free(old_head->_cv);
+        mutex_free(old_head->_ml);
+  
+        interval -= old_head->timeout;
+
+        waiters = waiters->link;
+
+        PR_DELETE(old_head);
+      }
+    }
+    else 
+    {
+      condition_wait(__ct_timed_wait_cv, __ct_timed_wait_ml);
+    }
+
+    mutex_unlock(__ct_timed_wait_ml);
+
+    thread_switch(cthread_thread(cthread_self()), SWITCH_OPTION_WAIT, PAUSE_TIME / CT_NANOPERMICRO);
+
+  }
+}
+
+void ct_InitTimedWaitThread()
+{
+  /* this should only be called once. */
+  PR_ASSERT(NULL == __ct_timed_wait_thread);
+
+  __ct_timed_wait_cv = condition_alloc();
+  condition_init(__ct_timed_wait_cv);
+  __ct_timed_wait_ml = mutex_alloc();
+  mutex_init(__ct_timed_wait_ml);
+
+  __ct_timed_wait_thread = PR_CreateThread(PR_SYSTEM_THREAD,
+					_timed_wait_thread_fn,
+					NULL,
+					PR_PRIORITY_LOW,
+					PR_LOCAL_THREAD,
+					PR_UNJOINABLE_THREAD,
+					0);
+}
+
+PRStatus ct_TimedWait(condition_t cv, mutex_t ml, PRIntervalTime timeout)
+{
+  /* insert an entry into the timed_waiters list. */
+  timed_waiter *new_entry = PR_NEWZAP(timed_waiter);
+  timed_waiter *cur, *prev;
+
+  if(new_entry == NULL)
+  {
+    return PR_FAILURE;
+  }
+
+  new_entry->thread = PR_CurrentThread();
+  new_entry->real_cv = cv;
+  new_entry->timeout = timeout;
+
+  new_entry->_cv = condition_alloc();
+  condition_init(new_entry->_cv);
+  new_entry->_ml = mutex_alloc();
+  mutex_init(new_entry->_ml);
+
+  mutex_lock(__ct_timed_wait_ml);
+
+  cur = waiters;
+  prev = 0;
+
+  /* find the proper place to insert the new node. */
+  while (cur && new_entry->timeout > cur->timeout)
+  {
+    prev = cur;
+    cur = cur->link;
+  }
+
+  /* if we're inserting it into the list somewhere other than the head,
+     we need to adjust its timeout accordingly. */
+  if (prev) new_entry->timeout -= waiters->timeout;
+
+  /* now we adjust all the timeouts for the entries later than it in the
+     list */
+  if (cur)
+  {
+    PRIntervalTime delta = cur->timeout - new_entry->timeout;
+
+    for (; cur != NULL; cur = cur->link)
+      cur->timeout += delta;
+  }
+
+  if (prev)
+  {
+    new_entry->link = prev->link;
+    prev->link = new_entry;
+  }
+  else
+  {
+    new_entry->link = waiters;
+    waiters = new_entry;
+  }
+
+  condition_broadcast(__ct_timed_wait_cv);
+  mutex_unlock(__ct_timed_wait_ml);
+
+  /* race condition here?  we wake up the timed_wait thread, and then it goes
+     back to sleep since it has nothing to do, before we wait on our cv.
+     something to worry about?  XXX */
+  condition_wait(new_entry->_cv, new_entry->_ml);
+
+  return PR_SUCCESS;
+}
+
+#endif