*** empty log message ***
[bpt/guile.git] / libguile / coop.c
index 832df6e..f676ea0 100644 (file)
@@ -1,4 +1,4 @@
-/*     Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+/*     Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000 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
@@ -40,7 +40,7 @@
  * If you do not wish that, delete this exception notice.  */
 \f
 
-/* $Id: coop.c,v 1.15 1998-11-19 08:15:22 mdj Exp $ */
+/* $Id: coop.c,v 1.18 2000-03-12 01:48:04 mdj Exp $ */
 
 /* Cooperative thread library, based on QuickThreads */
 
@@ -48,6 +48,8 @@
 #include <unistd.h>
 #endif
 
+#include <errno.h>
+
 #include <qt.h>
 #include "eval.h"
 
 
 /* Queue access functions. */
 
-#ifdef __STDC__
 static void
 coop_qinit (coop_q_t *q)
-#else
-static void
-coop_qinit (q)
-     coop_q_t *q;
-#endif
 {
   q->t.next = q->tail = &q->t;
 
@@ -85,14 +81,8 @@ coop_qinit (q)
 }
 
 
-#ifdef __STDC__
 coop_t *
 coop_qget (coop_q_t *q)
-#else
-coop_t *
-coop_qget (q)
-     coop_q_t *q;
-#endif
 {
   coop_t *t;
 
@@ -108,30 +98,16 @@ coop_qget (q)
 }
 
 
-#ifdef __STDC__
 void
 coop_qput (coop_q_t *q, coop_t *t)
-#else
-void
-coop_qput (q, t)
-     coop_q_t *q;
-     coop_t *t;
-#endif
 {
   q->tail->next = t;
   t->next = &q->t;
   q->tail = t;
 }
 
-#ifdef __STDC__
 static void
 coop_all_qput (coop_q_t *q, coop_t *t)
-#else
-static void
-coop_all_qput (q, t)
-     coop_q_t *q;
-     coop_t *t;
-#endif
 {
   if (q->t.all_next)
     q->t.all_next->all_prev = t;
@@ -140,15 +116,8 @@ coop_all_qput (q, t)
   q->t.all_next = t;
 }
 
-#ifdef __STDC__
 static void
 coop_all_qremove (coop_q_t *q, coop_t *t)
-#else
-static void
-coop_all_qremove (q, t)
-     coop_q_t *q;
-     coop_t *t;
-#endif
 {
   if (t->all_prev)
     t->all_prev->all_next = t->all_next;
@@ -158,6 +127,29 @@ coop_all_qremove (q, 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. */
+void
+coop_timeout_qinsert (coop_q_t *q, coop_t *t)
+{
+  coop_t *pred = &q->t;
+  int sec = t->wakeup_time.tv_sec;
+  int usec = t->wakeup_time.tv_usec;
+  while (pred->next != &q->t
+        && pred->next->timeoutp
+        && (pred->next->wakeup_time.tv_sec < sec
+            || (pred->next->wakeup_time.tv_sec == sec
+                && pred->next->wakeup_time.tv_usec < usec)))
+    pred = pred->next;
+  t->next = pred->next;
+  pred->next = t;
+  if (t->next == &q->t)
+    q->tail = t;
+}
+#endif
+
 
 \f/* Thread routines. */
 
@@ -174,13 +166,8 @@ static void *coop_aborthelp (qt_t *sp, void *old, void *null);
 static void *coop_yieldhelp (qt_t *sp, void *old, void *blockq);
 
 
-#ifdef __STDC__
-void
-coop_init()
-#else
 void
 coop_init()
-#endif
 {
   coop_qinit (&coop_global_runq);
   coop_qinit (&coop_global_sleepq);
@@ -195,13 +182,8 @@ coop_init()
    return NULL. */
 
 #ifndef GUILE_ISELECT
-#ifdef __STDC__
 coop_t *
 coop_next_runnable_thread()
-#else
-coop_t *
-coop_next_runnable_thread()
-#endif
 {
   int sleepers;
   coop_t *t;
@@ -231,13 +213,8 @@ coop_next_runnable_thread()
 }
 #endif
 
-#ifdef __STDC__
 void
 coop_start()
-#else
-void
-coop_start()
-#endif
 {
   coop_t *next;
 
@@ -248,16 +225,8 @@ coop_start()
 }
 
 
-#ifdef __STDC__
 static void *
 coop_starthelp (qt_t *old, void *ignore0, void *ignore1)
-#else
-static void *
-coop_starthelp (old, ignore0, ignore1)
-     qt_t *old;
-     void *ignore0;
-     void *ignore1;
-#endif
 {
   coop_global_main.sp = old;
   coop_global_main.joining = NULL;
@@ -265,28 +234,34 @@ coop_starthelp (old, ignore0, ignore1)
   return NULL; /* not used, but keeps compiler happy */
 }
 
-#ifdef __STDC__
 int
 coop_mutex_init (coop_m *m)
-#else
+{
+  return coop_new_mutex_init (m, NULL);
+}
+
 int
-coop_mutex_init (m)
-     coop_m *m;
-#endif
+coop_new_mutex_init (coop_m *m, coop_mattr *attr)
 {
   m->owner = NULL;
   coop_qinit(&(m->waiting));
   return 0;
 }
 
-#ifdef __STDC__
+int
+coop_mutex_trylock (coop_m *m)
+{
+  if (m->owner == NULL)
+    {
+      m->owner = coop_global_curr;
+      return 0;
+    }
+  else
+    return EBUSY;
+}
+
 int
 coop_mutex_lock (coop_m *m)
-#else
-int 
-coop_mutex_lock ()
-     coop_m *m;
-#endif
 {
   if (m->owner == NULL)
     {
@@ -314,14 +289,8 @@ coop_mutex_lock ()
 }
 
 
-#ifdef __STDC__
 int 
 coop_mutex_unlock (coop_m *m)
-#else
-int 
-coop_mutex_unlock (m)
-     coop_m *m;
-#endif
 {
   coop_t *old, *newthread;
   
@@ -346,41 +315,28 @@ coop_mutex_unlock (m)
 }
 
 
-#ifdef __STDC__
 int 
 coop_mutex_destroy (coop_m *m)
-#else
-int 
-coop_mutex_destroy (m)
-     coop_m *m;
-#endif
 {
   return 0;
 }
 
 
-#ifdef __STDC__
 int 
 coop_condition_variable_init (coop_c *c)
-#else
-int 
-coop_condition_variable_init (c)
-     coop_c *c;
-#endif
+{
+  return coop_new_condition_variable_init (c, NULL);
+}
+
+int
+coop_new_condition_variable_init (coop_c *c, coop_cattr *a)
 {
   coop_qinit(&(c->waiting));
   return 0;
 }
 
-#ifdef __STDC__
 int 
 coop_condition_variable_wait_mutex (coop_c *c, coop_m *m)
-#else
-int 
-coop_condition_variable_wait_mutex (c, m)
-     coop_c *c;
-     coop_m *m;
-#endif
 {
   coop_t *old, *newthread;
 
@@ -393,6 +349,7 @@ coop_condition_variable_wait_mutex (c, m)
   else
     {
       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)
@@ -410,15 +367,57 @@ coop_condition_variable_wait_mutex (c, m)
   return 0;
 }
 
-
-#ifdef __STDC__
 int 
-coop_condition_variable_signal (coop_c *c)
+coop_condition_variable_timed_wait_mutex (coop_c *c,
+                                         coop_m *m,
+                                         const struct timespec *abstime)
+{
+  coop_t *old, *t;
+  int res = ETIMEDOUT;
+
+  /* coop_mutex_unlock (m); */
+  t = coop_qget (&(m->waiting));
+  if (t != NULL)
+    {
+      m->owner = t;
+    }
+  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
-int 
-coop_condition_variable_signal (c)
-     coop_c *c;
+      /*fixme* Implement!*/
+      t = coop_next_runnable_thread();
 #endif
+    }
+  if (t != coop_global_curr)
+    {
+      coop_global_curr->top = &old;
+      old = coop_global_curr;
+      coop_global_curr = t;
+      QT_BLOCK (coop_yieldhelp, old, &(c->waiting), t->sp);
+
+      /* Are we still in the sleep queue? */
+      old = &coop_global_sleepq.t;
+      for (t = old->next; t != &coop_global_sleepq.t; old = t, t = t->next)
+       if (t == coop_global_curr)
+         {
+           old->next = t->next; /* unlink */
+           res = 0;
+           break;
+         }
+    }
+  coop_mutex_lock (m);
+  return res;
+}
+
+int 
+coop_condition_variable_signal (coop_c *c)
 {
   coop_t *newthread;
 
@@ -429,29 +428,95 @@ coop_condition_variable_signal (c)
   return 0;
 }
 
+/* {Keys}
+ */
+
+static int n_keys = 0;
+static int max_keys = 0;
+static void (**destructors) (void *) = 0;
+
+int
+coop_key_create (coop_k *keyp, void (*destructor) (void *value))
+{
+  if (n_keys >= max_keys)
+    {
+      int i;
+      max_keys = max_keys ? max_keys * 3 / 2 : 10;
+      destructors = realloc (destructors, sizeof (void *) * max_keys);
+      if (destructors == 0)
+       {
+         fprintf (stderr, "Virtual memory exceeded in coop_key_create\n");
+         exit (1);
+       }
+      for (i = n_keys; i < max_keys; ++i)
+       destructors[i] = NULL;
+    }
+  destructors[n_keys] = destructor;
+  *keyp = n_keys++;
+  return 0;
+}
+
+int
+coop_setspecific (coop_k key, const void *value)
+{
+  int n_keys = coop_global_curr->n_keys;
+  if (key >= n_keys)
+    {
+      int i;
+      coop_global_curr->n_keys = max_keys;
+      coop_global_curr->specific = realloc (n_keys
+                                           ? coop_global_curr->specific
+                                           : NULL,
+                                           sizeof (void *) * max_keys);
+      if (coop_global_curr->specific == 0)
+       {
+         fprintf (stderr, "Virtual memory exceeded in coop_setspecific\n");
+         exit (1);
+       }
+      for (i = n_keys; i < max_keys; ++i)
+       coop_global_curr->specific[i] = NULL;
+    }
+  coop_global_curr->specific[key] = (void *) value;
+  return 0;
+}
+
+void *
+coop_getspecific (coop_k key)
+{
+  return (key < coop_global_curr->n_keys
+         ? coop_global_curr->specific[key]
+         : NULL);
+}
+
+int
+coop_key_delete (coop_k key)
+{
+  return 0;
+}
+
 
-#ifdef __STDC__
 int 
 coop_condition_variable_destroy (coop_c *c)
-#else
-int 
-coop_condition_variable_destroy (c)
-     coop_c *c;
-#endif
 {
   return 0;
 }
 
+#ifdef GUILE_PTHREAD_COMPAT
+static void *
+dummy_start (void *coop_thread)
+{
+  coop_t *t = (coop_t *) coop_thread;
+  t->sto = &t + 1;
+  pthread_mutex_init (&t->dummy_mutex, NULL);
+  pthread_mutex_lock (&t->dummy_mutex);
+  pthread_cond_init (&t->dummy_cond, NULL);
+  pthread_cond_wait (&t->dummy_cond, &t->dummy_mutex);
+  return 0;
+}
+#endif
 
-#ifdef __STDC__
 coop_t *
 coop_create (coop_userf_t *f, void *pu)
-#else
-coop_t *
-coop_create (f, pu)
-     coop_userf_t *f;
-     void *pu;
-#endif
 {
   coop_t *t;
   void *sto;
@@ -459,7 +524,12 @@ coop_create (f, pu)
   t = malloc (sizeof(coop_t));
 
   t->data = NULL;
+  t->n_keys = 0;
+#ifdef GUILE_PTHREAD_COMPAT
+  pthread_create (&t->dummy_thread, NULL, dummy_start, t);
+#else
   t->sto = malloc (COOP_STKSIZE);
+#endif
   sto = COOP_STKALIGN (t->sto, QT_STKALIGN);
   t->sp = QT_SP (sto, COOP_STKSIZE - QT_STKALIGN);
   t->base = t->sp;
@@ -472,16 +542,8 @@ coop_create (f, pu)
 }
 
 
-#ifdef __STDC__
 static void
 coop_only (void *pu, void *pt, qt_userf_t *f)
-#else
-static void
-coop_only (pu. pt, f)
-     void *pu, 
-     void *pt, 
-     qt_userf_t *f;
-#endif
 {
   coop_global_curr = (coop_t *)pt;
   (*(coop_userf_t *)f)(pu);
@@ -490,13 +552,8 @@ coop_only (pu. pt, f)
 }
 
 
-#ifdef __STDC__
 void
 coop_abort ()
-#else
-void
-coop_abort ()
-#endif
 {
   coop_t *old, *newthread;
 
@@ -523,20 +580,15 @@ coop_abort ()
   coop_all_qremove(&coop_global_allq, coop_global_curr);
   old = coop_global_curr;
   coop_global_curr = newthread;
+#ifdef GUILE_PTHREAD_COMPAT
+  pthread_cond_signal (&old->dummy_cond);
+#endif
   QT_ABORT (coop_aborthelp, old, (void *)NULL, newthread->sp);
 }
 
 
-#ifdef __STDC__
 static void *
 coop_aborthelp (qt_t *sp, void *old, void *null)
-#else
-static void *
-coop_aborthelp (sp, old, null)
-     qt_t *sp;
-     void *old;
-     void *null;
-#endif
 {
   coop_t *oldthread = (coop_t *) old;
 
@@ -551,14 +603,8 @@ coop_aborthelp (sp, old, null)
 }
 
 
-#ifdef __STDC__
 void 
 coop_join(coop_t *t)
-#else
-void 
-coop_join()
-     coop_t *t;
-#endif
 {
   coop_t *old, *newthread;
   
@@ -585,13 +631,8 @@ coop_join()
   QT_BLOCK (coop_yieldhelp, old, (coop_q_t *) t->joining, newthread->sp);
 }
 
-#ifdef __STDC__
-void
-coop_yield()
-#else
 void
 coop_yield()
-#endif
 {
   coop_t *old = NULL;
   coop_t *newthread;
@@ -615,16 +656,8 @@ coop_yield()
 }
 
 
-#ifdef __STDC__
 static void *
 coop_yieldhelp (qt_t *sp, void *old, void *blockq)
-#else
-static void *
-coop_yieldhelp (sp, old, blockq)
-     qt_t *sp;
-     void *old;
-     void *blockq;
-#endif
 {
   ((coop_t *)old)->sp = sp;
   coop_qput ((coop_q_t *)blockq, (coop_t *)old);