diff options
Diffstat (limited to 'libguile/async.c')
-rw-r--r-- | libguile/async.c | 737 |
1 files changed, 737 insertions, 0 deletions
diff --git a/libguile/async.c b/libguile/async.c new file mode 100644 index 000000000..7a942270e --- /dev/null +++ b/libguile/async.c @@ -0,0 +1,737 @@ +/* Copyright (C) 1995,1996 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, 675 Mass Ave, Cambridge, MA 02139, 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. + */ + + +#include <stdio.h> +#include <signal.h> +#include "_scm.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + + + +/* {Asynchronous Events} + * + * + * Async == thunk + mark. + * + * Setting the mark guarantees future execution of the thunk. More + * than one set may be satisfied by a single execution. + * + * scm_tick_clock decremented once per SCM_ALLOW_INTS. + * Async execution triggered by SCM_ALLOW_INTS when scm_tick_clock drops to 0. + * Async execution prevented by scm_mask_ints != 0. + * + * If the clock reaches 0 when scm_mask_ints != 0, then reset the clock + * to 1. + * + * If the clock reaches 0 any other time, run marked asyncs. + * + * From a unix signal handler, mark a corresponding async and set the clock + * to 1. Do SCM_REDEFER_INTS;/SCM_REALLOW_INTS so that if the signal handler is not + * called in the dynamic scope of a critical section, it is excecuted immediately. + * + * Overall, closely timed signals of a particular sort may be combined. Pending signals + * are delivered in a fixed priority order, regardless of arrival order. + * + */ + + +#define min(A,B) ((A) < (B) ? (A) : (B)) + + +unsigned int scm_async_clock = 20; +static unsigned int scm_async_rate = 20; +unsigned int scm_mask_ints = 1; + +static unsigned int scm_tick_clock = 0; +static unsigned int scm_tick_rate = 0; +static unsigned int scm_desired_tick_rate = 0; +static unsigned int scm_switch_clock = 0; +static unsigned int scm_switch_rate = 0; +static unsigned int scm_desired_switch_rate = 0; + +static SCM system_signal_asyncs[SCM_NUM_SIGS]; +static SCM handler_var; +static SCM symbol_signal; + + +struct scm_async +{ + int got_it; /* needs to be delivered? */ + SCM thunk; /* the handler. */ +}; + + +static long scm_tc16_async; + +#define SCM_ASYNCP(X) (scm_tc16_async == SCM_GCTYP16 (X)) +#define SCM_ASYNC(X) ((struct scm_async *)SCM_CDR (X)) + + + + + + +#ifdef __STDC__ +static int +asyncs_pending (void) +#else +static int +asyncs_pending () +#endif +{ + SCM pos; + pos = scm_asyncs; + while (pos != SCM_EOL) + { + SCM a; + struct scm_async * it; + a = SCM_CAR (pos); + it = SCM_ASYNC (a); + if (it->got_it) + return 1; + pos = SCM_CDR (pos); + } + return 0; +} + + +#ifdef __STDC__ +void +scm_async_click (void) +#else +void +scm_async_click () +#endif +{ + int owe_switch; + int owe_tick; + + if (!scm_switch_rate) + { + owe_switch = 0; + scm_switch_clock = scm_switch_rate = scm_desired_switch_rate; + scm_desired_switch_rate = 0; + } + else + { + owe_switch = (scm_async_rate >= scm_switch_clock); + if (owe_switch) + { + if (scm_desired_switch_rate) + { + scm_switch_clock = scm_switch_rate = scm_desired_switch_rate; + scm_desired_switch_rate = 0; + } + else + scm_switch_clock = scm_switch_rate; + } + else + { + if (scm_desired_switch_rate) + { + scm_switch_clock = scm_switch_rate = scm_desired_switch_rate; + scm_desired_switch_rate = 0; + } + else + scm_switch_clock -= scm_async_rate; + } + } + + if (scm_mask_ints) + { + if (owe_switch) + scm_switch (); + scm_async_clock = 1; + return;; + } + + if (!scm_tick_rate) + { + unsigned int r; + owe_tick = 0; + r = scm_desired_tick_rate; + if (r) + { + scm_desired_tick_rate = 0; + scm_tick_rate = r; + scm_tick_clock = r; + } + } + else + { + owe_tick = (scm_async_rate >= scm_tick_clock); + if (owe_tick) + { + scm_tick_clock = scm_tick_rate = scm_desired_tick_rate; + scm_desired_tick_rate = 0; + } + else + { + if (scm_desired_tick_rate) + { + scm_tick_clock = scm_tick_rate = scm_desired_tick_rate; + scm_desired_tick_rate = 0; + } + else + scm_tick_clock -= scm_async_rate; + } + } + + if (owe_tick) + scm_async_mark (system_signal_asyncs[SCM_SIG_ORD(SCM_TICK_SIGNAL)]); + + SCM_DEFER_INTS; + if (scm_tick_rate && scm_switch_rate) + { + scm_async_rate = min (scm_tick_clock, scm_switch_clock); + scm_async_clock = scm_async_rate; + } + else if (scm_tick_rate) + { + scm_async_clock = scm_async_rate = scm_tick_clock; + } + else if (scm_switch_rate) + { + scm_async_clock = scm_async_rate = scm_switch_clock; + } + else + scm_async_clock = scm_async_rate = 1 << 16; + SCM_ALLOW_INTS_ONLY; + + tail: + scm_run_asyncs (scm_asyncs); + + SCM_DEFER_INTS; + if (asyncs_pending ()) + { + SCM_ALLOW_INTS_ONLY; + goto tail; + } + SCM_ALLOW_INTS; + + if (owe_switch) + scm_switch (); +} + + + + +#ifdef __STDC__ +void +scm_switch (void) +#else +void +scm_switch () +#endif +{} + + +#ifdef __STDC__ +static void +scm_deliver_signal (int num) +#else +static void +scm_deliver_signal (num) + int num; +#endif +{ + SCM handler; + handler = SCM_CDR (handler_var); + if (handler != SCM_BOOL_F) + scm_apply (handler, SCM_MAKINUM (num), scm_listofnull); + else + { + scm_mask_ints = 0; + scm_throw (symbol_signal, + scm_listify (SCM_MAKINUM (num), SCM_UNDEFINED)); + } +} + + + + +#ifdef __STDC__ +static int +print_async (SCM exp, SCM port, int writing) +#else +static int +print_async (exp, port, writing) + SCM exp; + SCM port; + int writing; +#endif +{ + scm_gen_puts (scm_regular_string, "#<async ", port); + scm_intprint(exp, 16, port); + scm_gen_putc('>', port); + return 1; +} + +#ifdef __STDC__ +static SCM +mark_async (SCM obj) +#else +static SCM +mark_async (obj) + SCM obj; +#endif +{ + struct scm_async * it; + if (SCM_GC8MARKP (obj)) + return SCM_BOOL_F; + SCM_SETGC8MARK (obj); + it = SCM_ASYNC (obj); + return it->thunk; +} + +#ifdef __STDC__ +static scm_sizet +free_async (SCM obj) +#else +static scm_sizet +free_async (SCM obj) + SCM obj; +#endif +{ + struct scm_async * it; + it = SCM_ASYNC (obj); + scm_must_free ((char *)it); + return (sizeof (*it)); +} + + +static scm_smobfuns async_smob = +{ + mark_async, + free_async, + print_async, + 0 +}; + + + + +SCM_PROC(s_async, "async", 1, 0, 0, scm_async); +#ifdef __STDC__ +SCM +scm_async (SCM thunk) +#else +SCM +scm_async (thunk) + SCM thunk; +#endif +{ + SCM it; + struct scm_async * async; + + SCM_NEWCELL (it); + SCM_DEFER_INTS; + SCM_SETCDR (it, SCM_EOL); + async = (struct scm_async *)scm_must_malloc (sizeof (*async), s_async); + async->got_it = 0; + async->thunk = thunk; + SCM_SETCDR (it, (SCM)async); + SCM_SETCAR (it, (SCM)scm_tc16_async); + SCM_ALLOW_INTS; + return it; +} + +SCM_PROC(s_system_async, "system-async", 1, 0, 0, scm_system_async); +#ifdef __STDC__ +SCM +scm_system_async (SCM thunk) +#else +SCM +scm_system_async (thunk) + SCM thunk; +#endif +{ + SCM it; + SCM list; + + it = scm_async (thunk); + SCM_NEWCELL (list); + SCM_DEFER_INTS; + SCM_SETCAR (list, it); + SCM_SETCDR (list, scm_asyncs); + scm_asyncs = list; + SCM_ALLOW_INTS; + return it; +} + +SCM_PROC(s_async_mark, "async-mark", 1, 0, 0, scm_async_mark); +#ifdef __STDC__ +SCM +scm_async_mark (SCM a) +#else +SCM +scm_async_mark (a) + SCM a; +#endif +{ + struct scm_async * it; + SCM_ASSERT (SCM_NIMP (a) && SCM_ASYNCP (a), a, SCM_ARG1, s_async_mark); + it = SCM_ASYNC (a); + it->got_it = 1; + return SCM_UNSPECIFIED; +} + + +SCM_PROC(s_system_async_mark, "system-async-mark", 1, 0, 0, scm_system_async_mark); +#ifdef __STDC__ +SCM +scm_system_async_mark (SCM a) +#else +SCM +scm_system_async_mark (a) + SCM a; +#endif +{ + struct scm_async * it; + SCM_ASSERT (SCM_NIMP (a) && SCM_ASYNCP (a), a, SCM_ARG1, s_async_mark); + it = SCM_ASYNC (a); + SCM_REDEFER_INTS; + it->got_it = 1; + scm_async_rate = 1 + scm_async_rate - scm_async_clock; + scm_async_clock = 1; + SCM_REALLOW_INTS; + return SCM_UNSPECIFIED; +} + + +SCM_PROC(s_run_asyncs, "run-asyncs", 1, 0, 0, scm_run_asyncs); +#ifdef __STDC__ +SCM +scm_run_asyncs (SCM list_of_a) +#else +SCM +scm_run_asyncs (list_of_a) + SCM list_of_a; +#endif +{ + SCM pos; + + if (scm_mask_ints) + return SCM_BOOL_F; + pos = list_of_a; + while (pos != SCM_EOL) + { + SCM a; + struct scm_async * it; + SCM_ASSERT (SCM_NIMP (pos) && SCM_CONSP (pos), pos, SCM_ARG1, s_run_asyncs); + a = SCM_CAR (pos); + SCM_ASSERT (SCM_NIMP (a) && SCM_ASYNCP (a), a, SCM_ARG1, s_run_asyncs); + it = SCM_ASYNC (a); + scm_mask_ints = 1; + if (it->got_it) + { + it->got_it = 0; + scm_apply (it->thunk, SCM_EOL, SCM_EOL); + } + scm_mask_ints = 0; + pos = SCM_CDR (pos); + } + return SCM_BOOL_T; +} + + + + +SCM_PROC(s_noop, "noop", 0, 0, 1, scm_noop); +#ifdef __STDC__ +SCM +scm_noop (SCM args) +#else +SCM +scm_noop (args) + SCM args; +#endif +{ + return (SCM_NULLP (args) + ? SCM_BOOL_F + : SCM_CAR (args)); +} + + + + +SCM_PROC(s_set_tick_rate, "set-tick-rate", 1, 0, 0, scm_set_tick_rate); +#ifdef __STDC__ +SCM +scm_set_tick_rate (SCM n) +#else +SCM +scm_set_tick_rate (n) + SCM n; +#endif +{ + unsigned int old_n; + SCM_ASSERT (SCM_INUMP (n), n, SCM_ARG1, s_set_tick_rate); + old_n = scm_tick_rate; + scm_desired_tick_rate = SCM_INUM (n); + scm_async_rate = 1 + scm_async_rate - scm_async_clock; + scm_async_clock = 1; + return SCM_MAKINUM (old_n); +} + + + + +SCM_PROC(s_set_switch_rate, "set-switch-rate", 1, 0, 0, scm_set_switch_rate); +#ifdef __STDC__ +SCM +scm_set_switch_rate (SCM n) +#else +SCM +scm_set_switch_rate (n) + SCM n; +#endif +{ + unsigned int old_n; + SCM_ASSERT (SCM_INUMP (n), n, SCM_ARG1, s_set_switch_rate); + old_n = scm_switch_rate; + scm_desired_switch_rate = SCM_INUM (n); + scm_async_rate = 1 + scm_async_rate - scm_async_clock; + scm_async_clock = 1; + return SCM_MAKINUM (old_n); +} + + + +#ifdef __STDC__ +static SCM +scm_sys_hup_async_thunk (void) +#else +static SCM +scm_sys_hup_async_thunk () +#endif +{ + scm_deliver_signal (SCM_HUP_SIGNAL); + return SCM_BOOL_F; +} + +#ifdef __STDC__ +static SCM +scm_sys_int_async_thunk (void) +#else +static SCM +scm_sys_int_async_thunk () +#endif +{ + scm_deliver_signal (SCM_INT_SIGNAL); + return SCM_BOOL_F; +} + +#ifdef __STDC__ +static SCM +scm_sys_fpe_async_thunk (void) +#else +static SCM +scm_sys_fpe_async_thunk () +#endif +{ + scm_deliver_signal (SCM_FPE_SIGNAL); + return SCM_BOOL_F; +} + +#ifdef __STDC__ +static SCM +scm_sys_bus_async_thunk (void) +#else +static SCM +scm_sys_bus_async_thunk () +#endif +{ + scm_deliver_signal (SCM_BUS_SIGNAL); + return SCM_BOOL_F; +} + +#ifdef __STDC__ +static SCM +scm_sys_segv_async_thunk (void) +#else +static SCM +scm_sys_segv_async_thunk () +#endif +{ + scm_deliver_signal (SCM_SEGV_SIGNAL); + return SCM_BOOL_F; +} + +#ifdef __STDC__ +static SCM +scm_sys_alrm_async_thunk (void) +#else +static SCM +scm_sys_alrm_async_thunk () +#endif +{ + scm_deliver_signal (SCM_ALRM_SIGNAL); + return SCM_BOOL_F; +} + +#ifdef __STDC__ +static SCM +scm_sys_gc_async_thunk (void) +#else +static SCM +scm_sys_gc_async_thunk () +#endif +{ + scm_deliver_signal (SCM_GC_SIGNAL); + return SCM_BOOL_F; +} + +#ifdef __STDC__ +static SCM +scm_sys_tick_async_thunk (void) +#else +static SCM +scm_sys_tick_async_thunk () +#endif +{ + scm_deliver_signal (SCM_TICK_SIGNAL); + return SCM_BOOL_F; +} + + + + + +#ifdef __STDC__ +SCM +scm_take_signal (int n) +#else +SCM +scm_take_signal (n) + int n; +#endif +{ + SCM ignored; + if (!scm_ints_disabled) + { + SCM_NEWCELL (ignored); /* In case we interrupted SCM_NEWCELL, + * throw out the possibly already allocated + * free cell. + */ + } + scm_system_async_mark (system_signal_asyncs[SCM_SIG_ORD(n)]); + return SCM_BOOL_F; +} + + + +SCM_PROC(s_unmask_signals, "unmask-signals", 0, 0, 0, scm_unmask_signals); +#ifdef __STDC__ +SCM +scm_unmask_signals (void) +#else +SCM +scm_unmask_signals () +#endif +{ + scm_mask_ints = 0; + return SCM_UNSPECIFIED; +} + + +SCM_PROC(s_mask_signals, "mask-signals", 0, 0, 0, scm_mask_signals); +#ifdef __STDC__ +SCM +scm_mask_signals (void) +#else +SCM +scm_mask_signals () +#endif +{ + scm_mask_ints = 1; + return SCM_UNSPECIFIED; +} + + + +#ifdef __STDC__ +void +scm_init_async (void) +#else +void +scm_init_async () +#endif +{ + SCM a_thunk; + scm_tc16_async = scm_newsmob (&async_smob); + symbol_signal = SCM_CAR (scm_sysintern ("signal", strlen ("signal"))); + scm_permanent_object (symbol_signal); + + /* These are in the opposite order of delivery priortity. + * + * Error conditions are given low priority: + */ + a_thunk = scm_make_gsubr ("%hup-thunk", 0, 0, 0, scm_sys_hup_async_thunk); + system_signal_asyncs[SCM_SIG_ORD(SCM_HUP_SIGNAL)] = scm_system_async (a_thunk); + a_thunk = scm_make_gsubr ("%int-thunk", 0, 0, 0, scm_sys_int_async_thunk); + system_signal_asyncs[SCM_SIG_ORD(SCM_INT_SIGNAL)] = scm_system_async (a_thunk); + a_thunk = scm_make_gsubr ("%fpe-thunk", 0, 0, 0, scm_sys_fpe_async_thunk); + system_signal_asyncs[SCM_SIG_ORD(SCM_FPE_SIGNAL)] = scm_system_async (a_thunk); + a_thunk = scm_make_gsubr ("%bus-thunk", 0, 0, 0, scm_sys_bus_async_thunk); + system_signal_asyncs[SCM_SIG_ORD(SCM_BUS_SIGNAL)] = scm_system_async (a_thunk); + a_thunk = scm_make_gsubr ("%segv-thunk", 0, 0, 0, scm_sys_segv_async_thunk); + system_signal_asyncs[SCM_SIG_ORD(SCM_SEGV_SIGNAL)] = scm_system_async (a_thunk); + + + a_thunk = scm_make_gsubr ("%gc-thunk", 0, 0, 0, scm_sys_gc_async_thunk); + system_signal_asyncs[SCM_SIG_ORD(SCM_GC_SIGNAL)] = scm_system_async (a_thunk); + + /* Clock and PC driven conditions are given highest priority. */ + a_thunk = scm_make_gsubr ("%tick-thunk", 0, 0, 0, scm_sys_tick_async_thunk); + system_signal_asyncs[SCM_SIG_ORD(SCM_TICK_SIGNAL)] = scm_system_async (a_thunk); + a_thunk = scm_make_gsubr ("%alrm-thunk", 0, 0, 0, scm_sys_alrm_async_thunk); + system_signal_asyncs[SCM_SIG_ORD(SCM_ALRM_SIGNAL)] = scm_system_async (a_thunk); + + handler_var = scm_sysintern ("signal-handler", strlen ("signal")); + SCM_SETCDR (handler_var, SCM_BOOL_F); + scm_permanent_object (handler_var); +#include "async.x" +} |