drop extra 2006-02-06 heading
[bpt/guile.git] / libguile / coop.c
index 889276e..133c3d4 100644 (file)
@@ -1,49 +1,29 @@
-/*     Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+/*     Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2006 Free Software Foundation, Inc.
  * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this software; see the file COPYING.  If not, write to
- * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307 USA
- *
- * As a special exception, the Free Software Foundation gives permission
- * for additional uses of the text contained in its release of GUILE.
- *
- * The exception is that, if you link the GUILE library with other files
- * to produce an executable, this does not by itself cause the
- * resulting executable to be covered by the GNU General Public License.
- * Your use of that executable is in no way restricted on account of
- * linking the GUILE library code into it.
- *
- * This exception does not however invalidate any other reasons why
- * the executable file might be covered by the GNU General Public License.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
  *
- * This exception applies only to the code released by the
- * Free Software Foundation under the name GUILE.  If you copy
- * code from other Free Software Foundation releases into a copy of
- * GUILE, as the General Public License permits, the exception does
- * not apply to the code that you add in this way.  To avoid misleading
- * anyone as to the status of such modified files, you must delete
- * this exception notice from them.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
  *
- * If you write modifications of your own for GUILE, it is your choice
- * whether to permit this exception to apply to your modifications.
- * If you do not wish that, delete this exception notice.  */
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
 \f
 
-/* $Id: coop.c,v 1.31 2002-08-17 20:39:35 ghouston Exp $ */
+/* $Id: coop.c,v 1.39 2006-04-17 00:05:38 kryde Exp $ */
 
 /* Cooperative thread library, based on QuickThreads */
 
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
 #include <stdio.h>
 
 #ifdef HAVE_UNISTD_H 
@@ -73,13 +53,11 @@ coop_qinit (coop_q_t *q)
 
   q->t.all_prev = NULL;
   q->t.all_next = NULL;
-#ifdef GUILE_ISELECT
   q->t.nfds = 0;
   q->t.readfds = NULL;
   q->t.writefds = NULL;
   q->t.exceptfds = NULL;
   q->t.timeoutp = 0;
-#endif
 }
 
 
@@ -131,7 +109,6 @@ coop_all_qremove (coop_q_t *q, coop_t *t)
       t->all_next->all_prev = t->all_prev;
 }
 
-#ifdef GUILE_ISELECT
 /* Insert thread t into the ordered queue q.
    q is ordered after wakeup_time.  Threads which aren't sleeping but
    waiting for I/O go last into the queue. */
@@ -152,10 +129,10 @@ coop_timeout_qinsert (coop_q_t *q, coop_t *t)
   if (t->next == &q->t)
     q->tail = t;
 }
-#endif
 
+\f
 
-\f/* Thread routines. */
+/* Thread routines. */
 
 coop_q_t coop_global_runq;     /* A queue of runable threads. */
 coop_q_t coop_global_sleepq;   /* A queue of sleeping threads. */
@@ -226,42 +203,6 @@ coop_init ()
 #endif
 }
 
-/* Return the next runnable thread. If no threads are currently runnable,
-   and there are sleeping threads - wait until one wakes up. Otherwise,
-   return NULL. */
-
-#ifndef GUILE_ISELECT
-coop_t *
-coop_next_runnable_thread()
-{
-  int sleepers;
-  coop_t *t;
-  time_t now;
-
-  do {
-    sleepers = 0;
-    now = time(NULL);
-
-    /* Check the sleeping queue */
-    while ((t = coop_qget(&coop_global_sleepq)) != NULL)
-      {
-       sleepers++;
-       if (t->wakeup_time <= now)
-         coop_qput(&coop_global_runq, t);
-       else
-         coop_qput(&coop_tmp_queue, t);
-      }
-    while ((t = coop_qget(&coop_tmp_queue)) != NULL)
-      coop_qput(&coop_global_sleepq, t);
-    
-    t = coop_qget (&coop_global_runq);
-
-  } while ((t == NULL) && (sleepers > 0));
-
-  return t;
-}
-#endif
-
 void
 coop_start()
 {
@@ -293,6 +234,7 @@ int
 coop_new_mutex_init (coop_m *m, coop_mattr *attr)
 {
   m->owner = NULL;
+  m->level = 0;
   coop_qinit(&(m->waiting));
   return 0;
 }
@@ -305,6 +247,11 @@ coop_mutex_trylock (coop_m *m)
       m->owner = coop_global_curr;
       return 0;
     }
+  else if (m->owner == coop_global_curr)
+    {
+      m->level++;
+      return 0;
+    }
   else
     return EBUSY;
 }
@@ -316,6 +263,10 @@ coop_mutex_lock (coop_m *m)
     {
       m->owner = coop_global_curr;
     }
+  else if (m->owner == coop_global_curr)
+    {
+      m->level++;
+    }
   else
     {
       coop_t *old, *newthread;
@@ -323,13 +274,9 @@ coop_mutex_lock (coop_m *m)
       /* Record the current top-of-stack before going to sleep */
       coop_global_curr->top = &old;
 
-#ifdef GUILE_ISELECT
       newthread = coop_wait_for_runnable_thread();
       if (newthread == coop_global_curr)
        coop_abort ();
-#else
-      newthread = coop_next_runnable_thread();
-#endif
       old = coop_global_curr;
       coop_global_curr = newthread;
       QT_BLOCK (coop_yieldhelp, old, &(m->waiting), newthread->sp);
@@ -343,23 +290,31 @@ coop_mutex_unlock (coop_m *m)
 {
   coop_t *old, *newthread;
   
-  newthread = coop_qget (&(m->waiting));
-  if (newthread != NULL)
+  if (m->level == 0)
     {
-      /* Record the current top-of-stack before going to sleep */
-      coop_global_curr->top = &old;
-
-      old = coop_global_curr;
-      coop_global_curr = newthread;
-      /* The new thread came into m->waiting through a lock operation.
-        It now owns this mutex. */
-      m->owner = coop_global_curr;
-      QT_BLOCK (coop_yieldhelp, old, &coop_global_runq, newthread->sp);
+      newthread = coop_qget (&(m->waiting));
+      if (newthread != NULL)
+       {
+         /* Record the current top-of-stack before going to sleep */
+         coop_global_curr->top = &old;
+         
+         old = coop_global_curr;
+         coop_global_curr = newthread;
+         /* The new thread came into m->waiting through a lock operation.
+            It now owns this mutex. */
+         m->owner = coop_global_curr;
+         QT_BLOCK (coop_yieldhelp, old, &coop_global_runq, newthread->sp);
+       }
+      else
+       {
+         m->owner = NULL;
+       }
     }
+  else if (m->level > 0)
+    m->level--;
   else
-    {
-      m->owner = NULL;
-    }
+    abort (); /* XXX */
+
   return 0;
 }
 
@@ -399,13 +354,9 @@ coop_condition_variable_wait_mutex (coop_c *c, coop_m *m)
     {
       m->owner = NULL;
       /*fixme* Should we really wait here?  Isn't it OK just to proceed? */
-#ifdef GUILE_ISELECT
       newthread = coop_wait_for_runnable_thread();
       if (newthread == coop_global_curr)
        coop_abort ();
-#else
-      newthread = coop_next_runnable_thread();
-#endif
     }
   coop_global_curr->top = &old;
   old = coop_global_curr;
@@ -419,7 +370,7 @@ coop_condition_variable_wait_mutex (coop_c *c, coop_m *m)
 int 
 coop_condition_variable_timed_wait_mutex (coop_c *c,
                                          coop_m *m,
-                                         const struct timespec *abstime)
+                                         const scm_t_timespec *abstime)
 {
   coop_t *old, *t;
 #ifdef ETIMEDOUT
@@ -439,16 +390,11 @@ coop_condition_variable_timed_wait_mutex (coop_c *c,
   else
     {
       m->owner = NULL;
-#ifdef GUILE_ISELECT
       coop_global_curr->timeoutp = 1;
       coop_global_curr->wakeup_time.tv_sec = abstime->tv_sec;
       coop_global_curr->wakeup_time.tv_usec = abstime->tv_nsec / 1000;
       coop_timeout_qinsert (&coop_global_sleepq, coop_global_curr);
       t = coop_wait_for_runnable_thread();
-#else
-      /*fixme* Implement!*/
-      t = coop_next_runnable_thread();
-#endif
     }
   if (t != coop_global_curr)
     {
@@ -472,7 +418,7 @@ coop_condition_variable_timed_wait_mutex (coop_c *c,
 }
 
 int 
-coop_condition_variable_signal (coop_c *c)
+coop_condition_variable_broadcast (coop_c *c)
 {
   coop_t *newthread;
 
@@ -483,6 +429,13 @@ coop_condition_variable_signal (coop_c *c)
   return 0;
 }
 
+int 
+coop_condition_variable_signal (coop_c *c)
+{
+  return coop_condition_variable_broadcast (c);
+}
+
+
 /* {Keys}
  */
 
@@ -559,10 +512,10 @@ coop_condition_variable_destroy (coop_c *c)
 #ifdef GUILE_PTHREAD_COMPAT
 
 /* 1K room for the cond wait routine */
-#ifdef SCM_STACK_GROWS_UP
-#define COOP_STACK_ROOM (256)
+#if SCM_STACK_GROWS_UP
+# define COOP_STACK_ROOM (256)
 #else
-#define COOP_STACK_ROOM (-256)
+# define COOP_STACK_ROOM (-256)
 #endif
 
 static void *
@@ -687,15 +640,11 @@ coop_abort ()
       free (coop_global_curr->joining);
     }
 
-#ifdef GUILE_ISELECT
   scm_I_am_dead = 1;
   do {
     newthread = coop_wait_for_runnable_thread();
   } while (newthread == coop_global_curr);
   scm_I_am_dead = 0;
-#else
-  newthread = coop_next_runnable_thread();
-#endif
   coop_all_qremove (&coop_global_allq, coop_global_curr);
   old = coop_global_curr;
   coop_global_curr = newthread;
@@ -733,13 +682,9 @@ coop_join(coop_t *t)
       coop_qinit((coop_q_t *) t->joining);
     }
 
-#ifdef GUILE_ISELECT
   newthread = coop_wait_for_runnable_thread();
   if (newthread == coop_global_curr)
     return;
-#else
-  newthread = coop_next_runnable_thread();
-#endif
   old = coop_global_curr;
   coop_global_curr = newthread;
   QT_BLOCK (coop_yieldhelp, old, (coop_q_t *) t->joining, newthread->sp);
@@ -755,13 +700,8 @@ coop_yield()
 
   /* There may be no other runnable threads. Return if this is the 
      case. */
-#if GUILE_ISELECT
   if (newthread == coop_global_curr)
       return;
-#else
-  if (newthread == NULL)
-      return;
-#endif
 
   old = coop_global_curr;
 
@@ -790,8 +730,6 @@ coop_sleephelp (qt_t *sp, void *old, void *blockq)
   return NULL;
 }
 
-#ifdef GUILE_ISELECT
-
 unsigned long 
 scm_thread_usleep (unsigned long usec)
 {
@@ -816,43 +754,6 @@ scm_thread_sleep (unsigned long sec)
   return slept > sec ? 0 : sec - slept;
 }
 
-#else /* GUILE_ISELECT */
-
-unsigned long
-scm_thread_sleep (unsigned long s)
-{
-  coop_t *newthread, *old;
-  time_t now = time (NULL);
-  coop_global_curr->wakeup_time = now + s;
-
-  /* Put the current thread on the sleep queue */
-  coop_qput (&coop_global_sleepq, coop_global_curr);
-
-  newthread = coop_next_runnable_thread();
-
-  /* If newthread is the same as the sleeping thread, do nothing */
-  if (newthread == coop_global_curr)
-    return s;
-
-  old = coop_global_curr;
-
-  coop_global_curr = newthread;
-  QT_BLOCK (coop_sleephelp, old, NULL, newthread->sp);
-
-  return s;
-}
-
-unsigned long 
-scm_thread_usleep (unsigned long usec)
-{
-  /* We're so cheap.  */
-  scm_thread_sleep (usec / 1000000);
-  return 0;  /* Maybe we should calculate actual time slept,
-               but this is faster... :) */
-}
-
-#endif /* GUILE_ISELECT */
-
 /*
   Local Variables:
   c-file-style: "gnu"