/* 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)
/* 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.
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
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),
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))
#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);
{
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)
{
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
}
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'
{
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),
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));