*** empty log message ***
[bpt/guile.git] / libguile / scmsigs.c
index c77fad0..a4a21c5 100644 (file)
@@ -1,47 +1,27 @@
 /* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2002 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 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 program is distributed in the hope that it will be useful,
+ * 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 General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser 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 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.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
 
 
 \f
 
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
 #include <signal.h>
 #include <errno.h>
 
 #include <sys/time.h>
 #endif
 
-/* The thread system has its own sleep and usleep functions.  */
-#ifndef USE_THREADS
-
-#if defined(MISSING_SLEEP_DECL)
-int sleep ();
-#endif
-
-#if defined(HAVE_USLEEP) && defined(MISSING_USLEEP_DECL)
-int usleep ();
-#endif
-
-#endif
-
 #ifdef __MINGW32__
 #include <windows.h>
 #define alarm(sec) (0)
@@ -108,12 +75,15 @@ int usleep ();
 
 /* Scheme vectors with information about a signal.  signal_handlers
    contains the handler procedure or #f, signal_handler_cells contains
-   preallocated cells for queuing the handler in take_signal since we
-   can't allocate during signal delivery, signal_handler_threads
-   points to the thread that a signal should be delivered to.
+   pre-queued cells for the handler (since we can't do fancy things
+   during signal delivery), signal_cell_handlers contains the SCM
+   value to be stuffed into the pre-queued cell upon delivery, and
+   signal_handler_threads points to the thread that a signal should be
+   delivered to.
 */
 static SCM *signal_handlers;
 static SCM signal_handler_cells;
+static SCM signal_cell_handlers;
 static SCM signal_handler_threads;
 
 /* saves the original C handlers, when a new handler is installed.
@@ -124,15 +94,23 @@ static struct sigaction orig_handlers[NSIG];
 static SIGRETTYPE (*orig_handlers[NSIG])(int);
 #endif
 
+
 static SIGRETTYPE
 take_signal (int signum)
 {
   if (signum >= 0 && signum < NSIG)
     {
-      SCM thread = SCM_VECTOR_REF (signal_handler_threads, signum);
-      scm_i_queue_async_cell (SCM_VECTOR_REF(signal_handler_cells, signum),
-                             scm_i_thread_root (thread));
+      SCM cell = SCM_VECTOR_REF(signal_handler_cells, signum);
+      SCM handler = SCM_VECTOR_REF(signal_cell_handlers, signum);
+      SCM thread = SCM_VECTOR_REF(signal_handler_threads, signum);
+      scm_root_state *root = scm_i_thread_root (thread);
+      if (SCM_CONSP (cell))
+       {
+         SCM_SETCAR (cell, handler);
+         root->pending_asyncs = 1;
+       }
     }
+  
 #ifndef HAVE_SIGACTION
   signal (signum, take_signal);
 #endif
@@ -151,6 +129,131 @@ close_1 (SCM proc, SCM arg)
                                           scm_list_2 (proc, arg)));
 }
 
+/* Make sure that signal SIGNUM can be delivered to THREAD, using
+   HANDLER.  THREAD and HANDLER must either both be non-#f (which
+   means install the handler), or both #f (which means deinstall an
+   existing handler).
+*/
+
+struct install_handler_data {
+  int signum;
+  SCM thread;
+  SCM handler;
+};
+
+static SCM
+scm_delq_spine_x (SCM cell, SCM list)
+{
+  SCM s = list, prev = SCM_BOOL_F;
+  
+  while (!SCM_EQ_P (cell, s))
+    {
+      if (SCM_NULLP (s))
+       return list;
+      prev = s;
+      s = SCM_CDR (s);
+    }
+  if (SCM_FALSEP (prev))
+    return SCM_CDR (cell);
+  else
+    {
+      SCM_SETCDR (prev, SCM_CDR (cell));
+      return list;
+    }
+}
+
+static void *
+really_install_handler (void *data)
+{
+  struct install_handler_data *args = data;
+  int signum = args->signum;
+  SCM thread = args->thread;
+  SCM handler = args->handler;
+  SCM cell;
+  SCM old_thread;
+
+  /* The following modifications are done while signals can be
+     delivered.  That is not a real problem since the signal handler
+     will only touch the car of the handler cell and set the
+     pending_asyncs trigger of a thread.  While the data structures
+     are in flux, the signal handler might store the wrong handler in
+     the cell, or set pending_asyncs of the wrong thread.  We fix this
+     at the end by making sure that the cell has the right handler in
+     it, if any, and that pending_asyncs is set for the new thread.
+  */
+
+  /* Make sure we have a cell. */
+  cell = SCM_VECTOR_REF (signal_handler_cells, signum);
+  if (SCM_FALSEP (cell))
+    {
+      cell = scm_cons (SCM_BOOL_F, SCM_EOL);
+      SCM_VECTOR_SET (signal_handler_cells, signum, cell);
+    }
+
+  /* Make sure it is queued for the right thread. */
+  old_thread = SCM_VECTOR_REF (signal_handler_threads, signum);
+  if (!SCM_EQ_P (thread, old_thread))
+    {
+      scm_root_state *r;
+      if (!SCM_FALSEP (old_thread))
+       {
+         r = scm_i_thread_root (old_thread);
+         r->signal_asyncs = scm_delq_spine_x (cell, r->signal_asyncs);
+       }
+      if (!SCM_FALSEP (thread))
+       {
+         r = scm_i_thread_root (thread);
+         SCM_SETCDR (cell, r->signal_asyncs);
+         r->signal_asyncs = cell;
+         /* Set pending_asyncs just in case.  A signal that is
+            delivered while we modify the data structures here might set
+            pending_asyncs of old_thread. */
+         r->pending_asyncs = 1; 
+       }
+      SCM_VECTOR_SET (signal_handler_threads, signum, thread); 
+    }
+
+  /* Set the new handler. */
+  if (SCM_FALSEP (handler))
+    {
+      SCM_VECTOR_SET (*signal_handlers, signum, SCM_BOOL_F);
+      SCM_VECTOR_SET (signal_cell_handlers, signum, SCM_BOOL_F);
+    }
+  else
+    {
+      SCM_VECTOR_SET (*signal_handlers, signum, handler);
+      SCM_VECTOR_SET (signal_cell_handlers, signum,
+                     close_1 (handler, scm_int2num (signum)));
+    }
+
+  /* Now fix up the cell.  It might contain the old handler but since
+     it is now queued for the new thread, we must make sure that the
+     new handler is run.  Any signal that is delivered during the
+     following code will install the new handler, so we have no
+     problem.
+  */
+  if (!SCM_FALSEP (SCM_CAR (cell)))
+    SCM_SETCAR (cell, SCM_VECTOR_REF (signal_cell_handlers, signum));
+
+  /* Phfew.  That should be it. */
+  return NULL;
+}
+
+static void
+install_handler (int signum, SCM thread, SCM handler)
+{
+  /* We block asyncs while installing the handler.  It would be safe
+     to leave them on, but we might run the wrong handler should a
+     signal be delivered. 
+  */
+
+  struct install_handler_data args;
+  args.signum = signum;
+  args.thread = thread;
+  args.handler = handler;
+  scm_c_call_with_blocked_asyncs (really_install_handler, &args);
+}
+
 /* user interface for installation of signal handlers.  */
 SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
            (SCM signum, SCM handler, SCM flags, SCM thread),
@@ -213,11 +316,17 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
       action.sa_flags |= SCM_INUM (flags);
     }
   sigemptyset (&action.sa_mask);
+#endif
+
   if (SCM_UNBNDP (thread))
     thread = scm_current_thread ();
   else
-    SCM_VALIDATE_THREAD (4, thread);
-#endif
+    {
+      SCM_VALIDATE_THREAD (4, thread);
+      if (scm_c_thread_exited_p (thread))
+       SCM_MISC_ERROR ("thread has already exited", SCM_EOL);
+    }
+
   SCM_DEFER_INTS;
   old_handler = SCM_VECTOR_REF(*signal_handlers, csig);
   if (SCM_UNBNDP (handler))
@@ -232,7 +341,7 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
 #else
          chandler = (SIGRETTYPE (*) (int)) SCM_INUM (handler);
 #endif
-         SCM_VECTOR_SET (*signal_handlers, csig, SCM_BOOL_F);
+         install_handler (csig, SCM_BOOL_F, SCM_BOOL_F);
        }
       else
        SCM_OUT_OF_RANGE (2, handler);
@@ -247,8 +356,7 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
        {
          action = orig_handlers[csig];
          orig_handlers[csig].sa_handler = SIG_ERR;
-         SCM_VECTOR_SET (*signal_handlers, csig, SCM_BOOL_F);
-
+         install_handler (csig, SCM_BOOL_F, SCM_BOOL_F);
        }
 #else
       if (orig_handlers[csig] == SIG_ERR)
@@ -257,7 +365,7 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
        {
          chandler = orig_handlers[csig];
          orig_handlers[csig] = SIG_ERR;
-         SCM_VECTOR_SET (*signal_handlers, csig, SCM_BOOL_F);
+         install_handler (csig, SCM_BOOL_F, SCM_BOOL_F);
        }
 #endif
     }
@@ -273,11 +381,7 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
       if (orig_handlers[csig] == SIG_ERR)
        save_handler = 1;
 #endif
-      handler = close_1 (handler, signum);
-      SCM_VECTOR_SET (*signal_handlers, csig, handler);
-      SCM_VECTOR_SET (signal_handler_cells, csig,
-                     scm_cons (handler, SCM_EOL));
-      SCM_VECTOR_SET (signal_handler_threads, csig, thread);
+      install_handler (csig, thread, handler);
     }
 
   /* XXX - Silently ignore setting handlers for `program error signals'
@@ -506,44 +610,23 @@ SCM_DEFINE (scm_sleep, "sleep", 1, 0, 0,
 {
   unsigned long j;
   SCM_VALIDATE_INUM_MIN (1, i,0);
-#ifdef USE_THREADS
   j = scm_thread_sleep (SCM_INUM(i));
-#else
-  j = sleep (SCM_INUM(i));
-#endif
   return scm_ulong2num (j);
 }
 #undef FUNC_NAME
 
-#if defined(USE_THREADS) || defined(HAVE_USLEEP) || defined(__MINGW32__)
 SCM_DEFINE (scm_usleep, "usleep", 1, 0, 0,
            (SCM i),
            "Sleep for I microseconds.  @code{usleep} is not available on\n"
            "all platforms.")
 #define FUNC_NAME s_scm_usleep
 {
+  unsigned long j;
   SCM_VALIDATE_INUM_MIN (1, i,0);
-
-#ifdef USE_THREADS
-  /* If we have threads, we use the thread system's sleep function.  */
-  {
-    unsigned long j = scm_thread_usleep (SCM_INUM (i));
-    return scm_ulong2num (j);
-  }
-#else
-#ifdef USLEEP_RETURNS_VOID
-  usleep (SCM_INUM (i));
-  return SCM_INUM0;
-#else
-  {
-    int j = usleep (SCM_INUM (i));
-    return SCM_MAKINUM (j);
-  }
-#endif
-#endif
+  j = scm_thread_usleep (SCM_INUM (i));
+  return scm_ulong2num (j);
 }
 #undef FUNC_NAME
-#endif /* USE_THREADS || HAVE_USLEEP || __MINGW32__ */
 
 SCM_DEFINE (scm_raise, "raise", 1, 0, 0,
            (SCM sig),
@@ -572,6 +655,8 @@ scm_init_scmsigs ()
                                  scm_c_make_vector (NSIG, SCM_BOOL_F)));
   signal_handler_cells =
     scm_permanent_object (scm_c_make_vector (NSIG, SCM_BOOL_F));
+  signal_cell_handlers =
+    scm_permanent_object (scm_c_make_vector (NSIG, SCM_BOOL_F));
   signal_handler_threads =
     scm_permanent_object (scm_c_make_vector (NSIG, SCM_BOOL_F));