VM: Define ASM_MUL on ARM only if SMULL instruction is supported.
[bpt/guile.git] / libguile / scmsigs.c
index 989ddca..701beb5 100644 (file)
@@ -1,24 +1,25 @@
-/* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2002, 2004, 2006, 2007 Free Software Foundation, Inc.
+/* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2002, 2004, 2006, 2007, 2008, 2009, 2011, 2013 Free Software Foundation, Inc.
  *
  * 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.
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
  *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * 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.
  *
  * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
  */
 
 
 \f
 
-#if HAVE_CONFIG_H
+#ifdef HAVE_CONFIG_H
 #  include <config.h>
 #endif
 
 #include <stdio.h>
 #include <errno.h>
 
-#include "libguile/_scm.h"
-
-#include "libguile/async.h"
-#include "libguile/eval.h"
-#include "libguile/root.h"
-#include "libguile/vectors.h"
-
-#include "libguile/validate.h"
-#include "libguile/scmsigs.h"
-
-#ifdef HAVE_IO_H
-#include <io.h>  /* for mingw _pipe() */
-#endif
-
 #ifdef HAVE_PROCESS_H
 #include <process.h>    /* for mingw */
 #endif
 #include <sys/time.h>
 #endif
 
-#ifdef __MINGW32__
-#include <windows.h>
-#define alarm(sec) (0)
-/* This weird comma expression is because Sleep is void under Windows. */
-#define sleep(sec) (Sleep ((sec) * 1000), 0)
-#define usleep(usec) (Sleep ((usec) / 1000), 0)
-#define pipe(fd) _pipe (fd, 256, O_BINARY)
-#endif
+#include <full-write.h>
+
+#include "libguile/_scm.h"
+
+#include "libguile/async.h"
+#include "libguile/eval.h"
+#include "libguile/root.h"
+#include "libguile/vectors.h"
+#include "libguile/threads.h"
+
+#include "libguile/validate.h"
+#include "libguile/scmsigs.h"
+
 
 \f
 
@@ -99,6 +91,14 @@ static SCM *signal_handlers;
 static SCM signal_handler_asyncs;
 static SCM signal_handler_threads;
 
+/* The signal delivery thread.  */
+scm_i_thread *scm_i_signal_delivery_thread = NULL;
+
+/* The mutex held when launching the signal delivery thread.  */
+static scm_i_pthread_mutex_t signal_delivery_thread_mutex =
+  SCM_I_PTHREAD_MUTEX_INITIALIZER;
+
+
 /* saves the original C handlers, when a new handler is installed.
    set to SIG_ERR if the original handler is installed.  */
 #ifdef HAVE_SIGACTION
@@ -128,44 +128,35 @@ static SIGRETTYPE
 take_signal (int signum)
 {
   char sigbyte = signum;
-  write (signal_pipe[1], &sigbyte, 1);
+  full_write (signal_pipe[1], &sigbyte, 1);
 
 #ifndef HAVE_SIGACTION
   signal (signum, take_signal);
 #endif
 }
 
-typedef struct {
-  ssize_t res;
-  int fd;
-  char *buf;
-  size_t n;
-} read_without_guile_data;
-
-static void *
-do_read_without_guile (void *raw_data)
+struct signal_pipe_data
 {
-  read_without_guile_data *data = (read_without_guile_data *)raw_data;
-  data->res = read (data->fd, data->buf, data->n);
-  return NULL;
-}
+  char sigbyte;
+  ssize_t n;
+  int err;
+};
 
-static ssize_t
-read_without_guile (int fd, char *buf, size_t n)
+static void*
+read_signal_pipe_data (void * data)
 {
-  read_without_guile_data data;
-  data.fd = fd;
-  data.buf = buf;
-  data.n = n;
-  scm_without_guile (do_read_without_guile, &data);
-  return data.res;
-}
+  struct signal_pipe_data *sdata = data;
+  
+  sdata->n = read (signal_pipe[0], &sdata->sigbyte, 1);
+  sdata->err = errno;
 
+  return NULL;
+}
+  
 static SCM
 signal_delivery_thread (void *data)
 {
-  int n, sig;
-  char sigbyte;
+  int sig;
 #if HAVE_PTHREAD_SIGMASK  /* not on mingw, see notes above */
   sigset_t all_sigs;
   sigfillset (&all_sigs);
@@ -174,9 +165,12 @@ signal_delivery_thread (void *data)
 
   while (1)
     {
-      n = read_without_guile (signal_pipe[0], &sigbyte, 1);
-      sig = sigbyte;
-      if (n == 1 && sig >= 0 && sig < NSIG)
+      struct signal_pipe_data sigdata;
+
+      scm_without_guile (read_signal_pipe_data, &sigdata);
+      
+      sig = sigdata.sigbyte;
+      if (sigdata.n == 1 && sig >= 0 && sig < NSIG)
        {
          SCM h, t;
 
@@ -185,24 +179,34 @@ signal_delivery_thread (void *data)
          if (scm_is_true (h))
            scm_system_async_mark_for_thread (h, t);
        }
-      else if (n < 0 && errno != EINTR)
+      else if (sigdata.n == 0)
+       break; /* the signal pipe was closed. */
+      else if (sigdata.n < 0 && sigdata.err != EINTR)
        perror ("error in signal delivery thread");
     }
 
-  return SCM_UNSPECIFIED;      /* not reached */
+  return SCM_UNSPECIFIED; /* not reached unless all other threads exited */
 }
 
 static void
 start_signal_delivery_thread (void)
 {
-  if (pipe (signal_pipe) != 0)
+  SCM signal_thread;
+
+  scm_i_pthread_mutex_lock (&signal_delivery_thread_mutex);
+
+  if (pipe2 (signal_pipe, O_CLOEXEC) != 0)
     scm_syserror (NULL);
-  scm_spawn_thread (signal_delivery_thread, NULL,
-                   scm_handle_by_message, "signal delivery thread");
+  signal_thread = scm_spawn_thread (signal_delivery_thread, NULL,
+                                   scm_handle_by_message,
+                                   "signal delivery thread");
+  scm_i_signal_delivery_thread = SCM_I_THREAD_DATA (signal_thread);
+
+  scm_i_pthread_mutex_unlock (&signal_delivery_thread_mutex);
 }
 
-static void
-ensure_signal_delivery_thread ()
+void
+scm_i_ensure_signal_delivery_thread ()
 {
   static scm_i_pthread_once_t once = SCM_I_PTHREAD_ONCE_INIT;
   scm_i_pthread_once (&once, start_signal_delivery_thread);
@@ -228,8 +232,8 @@ take_signal (int signum)
 #endif
 }
 
-static void
-ensure_signal_delivery_thread ()
+void
+scm_i_ensure_signal_delivery_thread ()
 {
   return;
 }
@@ -283,10 +287,8 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
            "a scheme procedure has been specified, that procedure will run\n"
            "in the given @var{thread}.   When no thread has been given, the\n"
            "thread that made this call to @code{sigaction} is used.\n"
-           "Flags can "
-           "optionally be specified for the new handler (@code{SA_RESTART} will\n"
-           "always be added if it's available and the system is using restartable\n"
-           "system calls.)  The return value is a pair with information about the\n"
+           "Flags can optionally be specified for the new handler.\n"
+           "The return value is a pair with information about the\n"
            "old handler as described above.\n\n"
            "This interface does not provide access to the \"signal blocking\"\n"
            "facility.  Maybe this is not needed, since the thread support may\n"
@@ -310,14 +312,7 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
   csig = scm_to_signed_integer (signum, 0, NSIG-1);
 
 #if defined(HAVE_SIGACTION)
-#if defined(SA_RESTART) && defined(HAVE_RESTARTABLE_SYSCALLS)
-  /* don't allow SA_RESTART to be omitted if HAVE_RESTARTABLE_SYSCALLS
-     is defined, since libguile would be likely to produce spurious
-     EINTR errors.  */
-  action.sa_flags = SA_RESTART;
-#else
   action.sa_flags = 0;
-#endif
   if (!SCM_UNBNDP (flags))
     action.sa_flags |= scm_to_int (flags);
   sigemptyset (&action.sa_mask);
@@ -332,7 +327,7 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
        SCM_MISC_ERROR ("thread has already exited", SCM_EOL);
     }
 
-  ensure_signal_delivery_thread ();
+  scm_i_ensure_signal_delivery_thread ();
 
   SCM_CRITICAL_SECTION_START;
   old_handler = SCM_SIMPLE_VECTOR_REF (*signal_handlers, csig);
@@ -352,7 +347,10 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
          install_handler (csig, SCM_BOOL_F, SCM_BOOL_F);
        }
       else
-       SCM_OUT_OF_RANGE (2, handler);
+       {
+         SCM_CRITICAL_SECTION_END;
+         SCM_OUT_OF_RANGE (2, handler);
+       }
     }
   else if (scm_is_false (handler))
     {
@@ -493,6 +491,7 @@ SCM_DEFINE (scm_restore_signals, "restore-signals", 0, 0, 0,
 }
 #undef FUNC_NAME
 
+#if defined HAVE_ALARM && HAVE_DECL_ALARM
 SCM_DEFINE (scm_alarm, "alarm", 1, 0, 0,
            (SCM i),
            "Set a timer to raise a @code{SIGALRM} signal after the specified\n"
@@ -508,6 +507,7 @@ SCM_DEFINE (scm_alarm, "alarm", 1, 0, 0,
   return scm_from_uint (alarm (scm_to_uint (i)));
 }
 #undef FUNC_NAME
+#endif /* HAVE_ALARM */
 
 #ifdef HAVE_SETITIMER
 SCM_DEFINE (scm_setitimer, "setitimer", 5, 0, 0,
@@ -652,6 +652,23 @@ SCM_DEFINE (scm_raise, "raise", 1, 0, 0,
 
 \f
 
+void
+scm_i_close_signal_pipe()
+{
+  /* SIGNAL_DELIVERY_THREAD_MUTEX is only locked while the signal delivery
+     thread is being launched.  The thread that calls this function is
+     already holding the thread admin mutex, so if the delivery thread hasn't
+     been launched at this point, it never will be before shutdown.  */
+  scm_i_pthread_mutex_lock (&signal_delivery_thread_mutex);
+
+#if SCM_USE_PTHREAD_THREADS
+  if (scm_i_signal_delivery_thread != NULL)
+    close (signal_pipe[1]);
+#endif
+
+  scm_i_pthread_mutex_unlock (&signal_delivery_thread_mutex);
+}
+
 void
 scm_init_scmsigs ()
 {
@@ -660,10 +677,8 @@ scm_init_scmsigs ()
   signal_handlers =
     SCM_VARIABLE_LOC (scm_c_define ("signal-handlers",
                                  scm_c_make_vector (NSIG, SCM_BOOL_F)));
-  signal_handler_asyncs =
-    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));
+  signal_handler_asyncs = scm_c_make_vector (NSIG, SCM_BOOL_F);
+  signal_handler_threads = scm_c_make_vector (NSIG, SCM_BOOL_F);
 
   for (i = 0; i < NSIG; i++)
     {
@@ -673,29 +688,6 @@ scm_init_scmsigs ()
 #else
       orig_handlers[i] = SIG_ERR;
 #endif
-
-#ifdef HAVE_RESTARTABLE_SYSCALLS
-      /* If HAVE_RESTARTABLE_SYSCALLS is defined, it's important that
-        signals really are restartable.  don't rely on the same
-        run-time that configure got: reset the default for every signal.
-      */
-#ifdef HAVE_SIGINTERRUPT
-      siginterrupt (i, 0);
-#elif defined(SA_RESTART)
-      {
-       struct sigaction action;
-
-       sigaction (i, NULL, &action);
-       if (!(action.sa_flags & SA_RESTART))
-         {
-           action.sa_flags |= SA_RESTART;
-           sigaction (i, &action, NULL);
-         }
-      }
-#endif
-      /* if neither siginterrupt nor SA_RESTART are available we may
-        as well assume that signals are always restartable.  */
-#endif
     }
 
   scm_c_define ("NSIG", scm_from_long (NSIG));