~ chicken-core (chicken-5) b7995839c0b481280bdeda117eb68bc0e78a40bf
commit b7995839c0b481280bdeda117eb68bc0e78a40bf Author: felix <felix@call-with-current-continuation.org> AuthorDate: Thu Oct 27 12:08:23 2011 +0200 Commit: Christian Kellermann <ckeen@pestilenz.org> CommitDate: Tue Nov 29 09:10:23 2011 +0100 Overhaul interrupt handling: - EINTR handling for all read-operations from tcp, file and process streams - signals are queued (up to a certain limit, with the usual restrictions given by UNIX) - added silly test-file - csi installs SIGINT handler directly (independent of the posix unit) - added setter for "signal-handler" - moved some more code into "posix-common" Squashed commit of the following (merges and fix commits omitted -- ck): fixed incorrect option when compiling signal-test.scm EINTR handling for process-I/O and read-line/read-string from FP disable failing numbers/string-conv test for windows added background threads to signal-test - moved low-level signal handling into library - establish SIGINT handler in csi (posix not needed) - added internal exn category #:memory-error (unused in the moment - this was intended for SIGSEGV handling, but ... not sure) - added setter for "signal-handler" - added note to manual about order of handling when signal-overrun occurs - "signal-handler" and setter moved to "posix-common.scm" - gave label in C_reclaim a more meaningful name - C_raise_interrupt drops interrupts if pending stack is full - C_i_pending_interrupts ignores timer interrupts - EINTR handling for tcp accept/connect - signal-tests fixes stack signals that arrive during handling; explicit EINTR handling in stream and tcp ports (Note: what about accept/connect?) do windows test in test-file, not in runtests.sh; added signal tests learning about EINTR - how could this ever have worked? use sigaction(3) instead of signal(3) where available. Patch by Alan Post, problem originally spotted by Joerg Wittenberger Signed-off-by: Christian Kellermann <ckeen@pestilenz.org> diff --git a/Makefile.bsd b/Makefile.bsd index 5eab203b..98e44fd9 100644 --- a/Makefile.bsd +++ b/Makefile.bsd @@ -83,6 +83,7 @@ chicken-config.h: chicken-defaults.h echo "#define HAVE_LONG_LONG 1" >>$@ echo "#define HAVE_MEMMOVE 1" >>$@ echo "#define HAVE_MEMORY_H 1" >>$@ + echo "#define HAVE_SIGACTION 1" >>$@ echo "#define HAVE_STDINT_H 1" >>$@ echo "#define HAVE_STDLIB_H 1" >>$@ echo "#define HAVE_STRERROR 1" >>$@ diff --git a/Makefile.cygwin b/Makefile.cygwin index f56bc291..cee6e741 100644 --- a/Makefile.cygwin +++ b/Makefile.cygwin @@ -95,6 +95,7 @@ chicken-config.h: chicken-defaults.h echo "#define HAVE_LONG_LONG 1" >>$@ echo "#define HAVE_MEMMOVE 1" >>$@ echo "#define HAVE_MEMORY_H 1" >>$@ + echo "#define HAVE_SIGACTION 1" >>$@ echo "#define HAVE_STDINT_H 1" >>$@ echo "#define HAVE_STDLIB_H 1" >>$@ echo "#define HAVE_STRERROR 1" >>$@ diff --git a/Makefile.haiku b/Makefile.haiku index 1f86bc39..54634a25 100644 --- a/Makefile.haiku +++ b/Makefile.haiku @@ -71,6 +71,7 @@ chicken-config.h: chicken-defaults.h echo "#define HAVE_LONG_LONG 1" >>$@ echo "#define HAVE_MEMMOVE 1" >>$@ echo "#define HAVE_MEMORY_H 1" >>$@ + echo "#define HAVE_SIGACTION 1" >>$@ echo "#define HAVE_STDINT_H 1" >>$@ echo "#define HAVE_STDLIB_H 1" >>$@ echo "#define HAVE_STRERROR 1" >>$@ diff --git a/Makefile.linux b/Makefile.linux index c713b456..6e5116ac 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -72,6 +72,7 @@ chicken-config.h: chicken-defaults.h echo "#define HAVE_LONG_LONG 1" >>$@ echo "#define HAVE_MEMMOVE 1" >>$@ echo "#define HAVE_MEMORY_H 1" >>$@ + echo "#define HAVE_SIGACTION 1" >>$@ echo "#define HAVE_STDINT_H 1" >>$@ echo "#define HAVE_STDLIB_H 1" >>$@ echo "#define HAVE_STRERROR 1" >>$@ diff --git a/Makefile.macosx b/Makefile.macosx index b4a44d97..da612a4a 100644 --- a/Makefile.macosx +++ b/Makefile.macosx @@ -96,6 +96,7 @@ chicken-config.h: chicken-defaults.h echo "#define HAVE_LONG_LONG 1" >>$@ echo "#define HAVE_MEMMOVE 1" >>$@ echo "#define HAVE_MEMORY_H 1" >>$@ + echo "#define HAVE_SIGACTION 1" >>$@ echo "#define HAVE_STDINT_H 1" >>$@ echo "#define HAVE_STDLIB_H 1" >>$@ echo "#define HAVE_STRERROR 1" >>$@ diff --git a/Makefile.solaris b/Makefile.solaris index f2d4deeb..84dc433c 100644 --- a/Makefile.solaris +++ b/Makefile.solaris @@ -102,6 +102,7 @@ chicken-config.h: chicken-defaults.h echo "#define HAVE_LONG_LONG 1" >>$@ echo "#define HAVE_MEMMOVE 1" >>$@ echo "#define HAVE_MEMORY_H 1" >>$@ + echo "#define HAVE_SIGACTION 1" >>$@ echo "#define HAVE_STDINT_H 1" >>$@ echo "#define HAVE_STDLIB_H 1" >>$@ echo "#define HAVE_STRERROR 1" >>$@ diff --git a/chicken.h b/chicken.h index af170154..8169f6d0 100644 --- a/chicken.h +++ b/chicken.h @@ -863,6 +863,9 @@ DECL_C_PROC_p0 (128, 1,0,0,0,0,0,0,0) # define C_isatty isatty # define C_fileno fileno # define C_select select +# if defined(HAVE_SIGACTION) +# define C_sigaction sigaction +# endif # define C_signal signal # define C_getrusage getrusage # define C_tolower tolower @@ -1848,6 +1851,7 @@ C_fctexport void C_ccall C_peek_unsigned_integer_32(C_word c, C_word closure, C_ #endif C_fctexport C_word C_fcall C_decode_literal(C_word **ptr, C_char *str) C_regparm; +C_fctexport C_word C_fcall C_i_pending_interrupt(C_word dummy) C_regparm; /* defined in eval.scm: */ C_fctexport void CHICKEN_get_error_message(char *buf,int bufsize); diff --git a/csi.scm b/csi.scm index fa95e2f5..8846dd47 100644 --- a/csi.scm +++ b/csi.scm @@ -31,6 +31,8 @@ (disable-interrupts) (compile-syntax) (foreign-declare #<<EOF +#include <signal.h> + #if defined(HAVE_DIRECT_H) # include <direct.h> #else @@ -906,6 +908,20 @@ EOF (##sys#void)))))) +;;; Handle some signals: + +(define-foreign-variable _sigint int "SIGINT") + +(define-syntax defhandler + (syntax-rules () + ((_ sig handler) + (begin + (##core#inline "C_establish_signal_handler" sig sig) + (##sys#setslot ##sys#signal-vector sig handler))))) + +(defhandler _sigint (lambda (n) (##sys#user-interrupt-hook))) + + ;;; Start interpreting: (define (member* keys set) diff --git a/distribution/manifest b/distribution/manifest index ba7001ab..134546d0 100644 --- a/distribution/manifest +++ b/distribution/manifest @@ -199,6 +199,7 @@ tests/reverser/tags/1.1/reverser.meta tests/reverser/tags/1.1/reverser.setup tests/reverser/tags/1.1/reverser.scm tests/rev-app.scm +tests/signal-tests.scm tweaks.scm utils.scm apply-hack.x86.S diff --git a/library.scm b/library.scm index 8075c2fd..9b65ad29 100644 --- a/library.scm +++ b/library.scm @@ -76,12 +76,18 @@ fast_read_line_from_file(C_word str, C_word port, C_word size) { C_FILEPTR fp = C_port_file(port); if ((c = C_getc(fp)) == EOF) - return C_SCHEME_END_OF_FILE; + return errno == EINTR ? C_fix(-1) : C_SCHEME_END_OF_FILE; C_ungetc(c, fp); for (i = 0; i < n; i++) { c = C_getc(fp); + + if(c == EOF && errno == EINTR) { + clearerr(fp); + return C_fix(-(i + 1)); + } + switch (c) { case '\r': if ((c = C_getc(fp)) != '\n') C_ungetc(c, fp); case EOF: clearerr(fp); @@ -101,7 +107,11 @@ fast_read_string_from_file(C_word dest, C_word port, C_word len, C_word pos) size_t m = fread (buf, sizeof (char), n, fp); - if (m < n) { + if(m == EOF && errno == EINTR) { + clearerr(fp); + return C_fix(-1); + } + else if (m < n) { if (feof (fp)) { clearerr (fp); if (0 == m) @@ -1736,9 +1746,17 @@ EOF (define ##sys#stream-port-class (vector (lambda (p) ; read-char - (##core#inline "C_read_char" p) ) + (let loop () + (let ((c (##core#inline "C_read_char" p))) + (if (eq? -1 c) ; EINTR + (##sys#dispatch-interrupt loop) + c)))) (lambda (p) ; peek-char - (##core#inline "C_peek_char" p) ) + (let loop () + (let ((c (##core#inline "C_peek_char" p))) + (if (eq? -1 c) ; EINTR + (##sys#dispatch-interrupt loop) + c)))) (lambda (p c) ; write-char (##core#inline "C_display_char" p c) ) (lambda (p s) ; write-string @@ -1756,6 +1774,11 @@ EOF (cond [(or (not len) ; error returns EOF (eof-object? len)) ; EOF returns 0 bytes read act] + ((fx< len 0) ; EINTR + (let ((len (fx< (fxneg len) 1))) + (##sys#dispatch-interrupt + (lambda () + (loop (fx- rem len) (fx+ act len) (fx+ start len)))))) [(fx< len rem) (loop (fx- rem len) (fx+ act len) (fx+ start len))] [else @@ -1781,6 +1804,11 @@ EOF (##sys#make-string (fx* len 2)) (##sys#string-append result buffer) #t)) ] + ((fx< n 0) ; EINTR + (let ((n (fx- (fxneg n) 1))) + (##sys#dispatch-interrupt + (lambda () + (loop len limit buffer result f))))) [f (##sys#setislot p 4 (fx+ (##sys#slot p 4) 1)) (##sys#string-append result (##sys#substring buffer 0 n))] [else @@ -3909,6 +3937,7 @@ EOF [(#:arity-error) '(exn arity)] [(#:access-error) '(exn access)] [(#:domain-error) '(exn domain)] + ((#:memory-error) '(exn memory)) [else '(exn)] ) (list '(exn . message) msg '(exn . arguments) args @@ -4344,10 +4373,23 @@ EOF (define ##sys#context-switch (##core#primitive "C_context_switch")) +(define ##sys#signal-vector (make-vector 256 #f)) + (define (##sys#interrupt-hook reason state) - (cond ((fx> (##sys#slot ##sys#pending-finalizers 0) 0) - (##sys#run-pending-finalizers state) ) - (else (##sys#context-switch state) ) ) ) + (let loop ((reason reason)) + (cond ((and reason (##sys#slot ##sys#signal-vector reason)) => + (lambda (handler) + (handler reason) + (loop (##core#inline "C_i_pending_interrupt" #f)))) + ((fx> (##sys#slot ##sys#pending-finalizers 0) 0) + (##sys#run-pending-finalizers state) ) + ((procedure? state) (state)) + (else (##sys#context-switch state) ) ) ) ) + +(define (##sys#dispatch-interrupt k) + (##sys#interrupt-hook + (##core#inline "C_i_pending_interrupt" #f) + k)) ;;; Accessing "errno": @@ -4568,19 +4610,20 @@ EOF (vector-fill! ##sys#pending-finalizers (##core#undefined)) (##sys#setislot ##sys#pending-finalizers 0 0) (set! working #f) ) ) - (when state (##sys#context-switch state) ) ) ) ) + (cond ((not state)) + ((procedure? state) (state)) + (state (##sys#context-switch state) ) ) ) )) (define (##sys#force-finalizers) (let loop () (let ([n (##sys#gc)]) - (if (fx> (##sys#slot ##sys#pending-finalizers 0) 0) - (begin - (##sys#run-pending-finalizers #f) - (loop) ) - n) ) ) ) + (cond ((fx> (##sys#slot ##sys#pending-finalizers 0) 0) + (##sys#run-pending-finalizers #f) + (loop) ) + (else n) ) ) )) (define (gc . arg) - (let ([a (and (pair? arg) (car arg))]) + (let ((a (and (pair? arg) (car arg)))) (if a (##sys#force-finalizers) (apply ##sys#gc arg) ) ) ) diff --git a/manual/Unit posix b/manual/Unit posix index 0f6d0a2d..a81b834e 100644 --- a/manual/Unit posix +++ b/manual/Unit posix @@ -848,6 +848,12 @@ after {{SECONDS}} are elapsed. You can use the ==== set-signal-handler! +==== signal-handler + +<procedure>(signal-handler SIGNUM)</procedure> + +Returns the signal handler for the code {{SIGNUM}} or {{#f}}. + <procedure>(set-signal-handler! SIGNUM PROC)</procedure> Establishes the procedure of one argument {{PROC}} as the handler @@ -855,13 +861,13 @@ for the signal with the code {{SIGNUM}}. {{PROC}} is called with the signal number as its sole argument. If the argument {{PROC}} is {{#f}} then any signal handler will be removed, and the corresponding signal set to {{SIG_IGN}}. -Note that is is unspecified in which thread of execution the signal handler will be invoked. +Notes -==== signal-handler +* it is unspecified in which thread of execution the signal handler will be invoked. -<procedure>(signal-handler SIGNUM)</procedure> +* when signals arrive in quick succession (specifically, before the handler for a signal has been started), then signals will be queued (up to a certain limit); the order in which the queued signals will be handled is not specified -Returns the signal handler for the code {{SIGNUM}} or {{#f}}. +* {{(set! (signal-handler SIG) PROC)}} can be used as an alternative to {{(set-signal-handler! SIG PROC)}} ==== set-signal-mask! diff --git a/posix-common.scm b/posix-common.scm index 89e87d34..20b5a7ae 100644 --- a/posix-common.scm +++ b/posix-common.scm @@ -488,6 +488,21 @@ EOF (##sys#error 'time->string "cannot convert time vector to string" tm) ) ) ) ) ) ) +;;; Signals + +(define (set-signal-handler! sig proc) + (##sys#check-exact sig 'set-signal-handler!) + (##core#inline "C_establish_signal_handler" sig (and proc sig)) + (vector-set! ##sys#signal-vector sig proc) ) + +(define signal-handler + (getter-with-setter + (lambda (sig) + (##sys#check-exact sig 'signal-handler) + (##sys#slot ##sys#signal-vector sig) ) + set-signal-handler!)) + + ;;; Processes (define current-process-id (foreign-lambda int "C_getpid")) @@ -501,4 +516,3 @@ EOF (if (fx= epid -1) (posix-error #:process-error 'process-wait "waiting for child process failed" pid) (values epid enorm ecode) ) ) ) ) ) ) - diff --git a/posixunix.scm b/posixunix.scm index ec3df0f7..f1af0926 100644 --- a/posixunix.scm +++ b/posixunix.scm @@ -950,26 +950,6 @@ EOF signal/tstp signal/pipe signal/xcpu signal/xfsz signal/usr1 signal/usr2 signal/winch)) -(let ([oldhook ##sys#interrupt-hook] - [sigvector (make-vector 256 #f)] ) - (set! signal-handler - (lambda (sig) - (##sys#check-exact sig 'signal-handler) - (##sys#slot sigvector sig) ) ) - (set! set-signal-handler! - (lambda (sig proc) - (##sys#check-exact sig 'set-signal-handler!) - (##core#inline "C_establish_signal_handler" sig (and proc sig)) - (vector-set! sigvector sig proc) ) ) - (set! ##sys#interrupt-hook - (lambda (reason state) - (let ([h (##sys#slot sigvector reason)]) - (if h - (begin - (h reason) - (##sys#context-switch state) ) - (oldhook reason state) ) ) ) ) ) - (define set-signal-mask! (lambda (sigs) (##sys#check-list sigs 'set-signal-mask!) @@ -1005,12 +985,6 @@ EOF (when (fx< (##core#inline "C_sigprocmask_unblock" 0) 0) (posix-error #:process-error 'signal-unmask! "cannot unblock signal") ) ) -;;; Set SIGINT handler: - -(set-signal-handler! - signal/int - (lambda (n) (##sys#user-interrupt-hook)) ) - ;;; Getting system-, group- and user-information: @@ -1338,13 +1312,15 @@ EOF (when (fx>= bufpos buflen) (let loop () (let ([cnt (##core#inline "C_read" fd buf bufsiz)]) - (cond [(fx= cnt -1) - (if (fx= _errno _ewouldblock) - (begin - (##sys#thread-block-for-i/o! ##sys#current-thread fd #:input) - (##sys#thread-yield!) - (loop) ) - (posix-error #:file-error loc "cannot read" fd nam) )] + (cond ((fx= cnt -1) + (select errno + ((_ewouldblock) + (##sys#thread-block-for-i/o! ##sys#current-thread fd #:input) + (##sys#thread-yield!) + (loop) ) + ((_eintr) + (##sys#dispatch-interrupt loop)) + (else (posix-error #:file-error loc "cannot read" fd nam) ))) [(and more? (fx= cnt 0)) ; When "more" keep trying, otherwise read once more ; to guard against race conditions @@ -1445,18 +1421,21 @@ EOF (define ##sys#custom-output-port (lambda (loc nam fd #!optional (nonblocking? #f) (bufi 0) (on-close void)) (when nonblocking? (##sys#file-nonblocking! fd) ) - (letrec ( - [poke + (letrec ([poke (lambda (str len) - (let ([cnt (##core#inline "C_write" fd str len)]) - (cond [(fx= -1 cnt) - (if (fx= _errno _ewouldblock) - (begin - (##sys#thread-yield!) - (poke str len) ) - (posix-error loc #:file-error "cannot write" fd nam) ) ] - [(fx< cnt len) - (poke (##sys#substring str cnt len) (fx- len cnt)) ] ) ) )] + (let loop () + (let ([cnt (##core#inline "C_write" fd str len)]) + (cond ((fx= -1 cnt) + (select _errno + ((_ewouldblock) + (##sys#thread-yield!) + (poke str len) ) + ((_eintr) + (##sys#dispatch-interrupt loop)) + (else + (posix-error loc #:file-error "cannot write" fd nam) ) ) ) + ((fx< cnt len) + (poke (##sys#substring str cnt len) (fx- len cnt)) ) ) ) ))] [store (let ([bufsiz (if (fixnum? bufi) bufi (##sys#size bufi))]) (if (fx= 0 bufsiz) @@ -1480,8 +1459,7 @@ EOF (set! bufpos (fx+ bufpos len))] ) ) (when (fx< 0 bufpos) (poke buf bufpos) ) ) ) ) ) )]) - (letrec ( - [this-port + (letrec ([this-port (make-output-port (lambda (str) ; write-string (store str) ) diff --git a/posixwin.scm b/posixwin.scm index 2dd5a30b..bc61b7e7 100644 --- a/posixwin.scm +++ b/posixwin.scm @@ -1262,25 +1262,6 @@ EOF signal/term signal/int signal/fpe signal/ill signal/segv signal/abrt signal/break)) -(let ([oldhook ##sys#interrupt-hook] - [sigvector (make-vector 256 #f)] ) - (set! signal-handler - (lambda (sig) - (##sys#check-exact sig 'signal-handler) - (##sys#slot sigvector sig) ) ) - (set! set-signal-handler! - (lambda (sig proc) - (##sys#check-exact sig 'set-signal-handler!) - (##core#inline "C_establish_signal_handler" sig (and proc sig)) - (vector-set! sigvector sig proc) ) ) - (set! ##sys#interrupt-hook - (lambda (reason state) - (let ([h (##sys#slot sigvector reason)]) - (if h - (begin - (h reason) - (##sys#context-switch state) ) - (oldhook reason state) ) ) ) ) ) ;;; More errno codes: diff --git a/runtime.c b/runtime.c index 09b1393b..d0e14258 100644 --- a/runtime.c +++ b/runtime.c @@ -32,6 +32,7 @@ #include <assert.h> #include <limits.h> #include <math.h> +#include <signal.h> #ifdef HAVE_SYSEXITS_H # include <sysexits.h> @@ -164,6 +165,8 @@ extern void _C_do_apply_hack(void *proc, C_word *args, int count) C_noret; #define FILE_INFO_SIZE 7 +#define MAX_PENDING_INTERRUPTS 100 + #ifdef C_DOUBLE_IS_32_BITS # define FLONUM_PRINT_PRECISION 7 #else @@ -446,6 +449,9 @@ static C_TLS FINALIZER_NODE static C_TLS void *current_module_handle; static C_TLS int flonum_print_precision = FLONUM_PRINT_PRECISION; static C_TLS HDUMP_BUCKET **hdump_table; +static C_TLS int + pending_interrupts[ MAX_PENDING_INTERRUPTS ], + pending_interrupts_count; /* Prototypes: */ @@ -695,6 +701,7 @@ int CHICKEN_initialize(int heap, int stack, int symbols, void *toplevel) C_clear_trace_buffer(); chicken_is_running = chicken_ran_once = 0; interrupt_reason = 0; + pending_interrupts_count = 0; last_interrupt_latency = 0; C_interrupts_enabled = 1; C_initial_timer_interrupt_period = INITIAL_TIMER_INTERRUPT_PERIOD; @@ -718,7 +725,7 @@ int CHICKEN_initialize(int heap, int stack, int symbols, void *toplevel) static C_PTABLE_ENTRY *create_initial_ptable() { - /* hardcoded table size - this must match the number of C_pte calls! */ + /* IMPORTANT: hardcoded table size - this must match the number of C_pte calls! */ C_PTABLE_ENTRY *pt = (C_PTABLE_ENTRY *)C_malloc(sizeof(C_PTABLE_ENTRY) * 60); int i = 0; @@ -750,6 +757,7 @@ static C_PTABLE_ENTRY *create_initial_ptable() C_pte(C_divide); C_pte(C_nequalp); C_pte(C_greaterp); + /* IMPORTANT: have you read the comments at the start and the end of this function? */ C_pte(C_lessp); C_pte(C_greater_or_equal_p); C_pte(C_less_or_equal_p); @@ -784,7 +792,7 @@ static C_PTABLE_ENTRY *create_initial_ptable() C_pte(C_filter_heap_objects); C_pte(C_get_argument); - /* did you remember the hardcoded pte table size? */ + /* IMPORTANT: did you remember the hardcoded pte table size? */ pt[ i ].id = NULL; return pt; } @@ -982,7 +990,9 @@ void initialize_symbol_table(void) void global_signal_handler(int signum) { C_raise_interrupt(signal_mapping_table[ signum ]); - signal(signum, global_signal_handler); +#ifndef HAVE_SIGACTION + C_signal(signum, global_signal_handler); +#endif } @@ -2650,7 +2660,7 @@ C_regparm void C_fcall C_reclaim(void *trampoline, void *proc) if(gc_mode == GC_REALLOC) { C_rereclaim2(percentage(heap_size, C_heap_growth), 0); gc_mode = GC_MAJOR; - goto never_mind_edsgar; + goto i_like_spaghetti; } heap_scan_top = (C_byte *)C_align((C_uword)tospace_top); @@ -2836,7 +2846,7 @@ C_regparm void C_fcall C_reclaim(void *trampoline, void *proc) tospace_limit = tmp; } - never_mind_edsgar: + i_like_spaghetti: ++gc_count_2; if(C_enable_gcweak) { @@ -3944,7 +3954,12 @@ C_regparm C_word C_fcall C_read_char(C_word port) { int c = C_getc(C_port_file(port)); - return c == EOF ? C_SCHEME_END_OF_FILE : C_make_character(c); + if(c == EOF) { + if(errno == EINTR) return C_fix(-1); + else return C_SCHEME_END_OF_FILE; + } + + return C_make_character(c); } @@ -3953,8 +3968,13 @@ C_regparm C_word C_fcall C_peek_char(C_word port) C_FILEPTR fp = C_port_file(port); int c = C_getc(fp); + if(c == EOF) { + if(errno == EINTR) return C_fix(-1); + else return C_SCHEME_END_OF_FILE; + } + C_ungetc(c, fp); - return c == EOF ? C_SCHEME_END_OF_FILE : C_make_character(c); + return C_make_character(c); } @@ -4202,16 +4222,25 @@ C_regparm void C_fcall C_paranoid_check_for_interrupt(void) C_regparm void C_fcall C_raise_interrupt(int reason) { if(C_interrupts_enabled) { - saved_stack_limit = C_stack_limit; + if(interrupt_reason) { + if(reason != C_TIMER_INTERRUPT_NUMBER) { + if(pending_interrupts_count < MAX_PENDING_INTERRUPTS) + /* drop signals if too many */ + pending_interrupts[ pending_interrupts_count++ ] = reason; + } + } + else { + saved_stack_limit = C_stack_limit; #if C_STACK_GROWS_DOWNWARD - C_stack_limit = C_stack_pointer + 1000; + C_stack_limit = C_stack_pointer + 1000; #else - C_stack_limit = C_stack_pointer - 1000; + C_stack_limit = C_stack_pointer - 1000; #endif - interrupt_reason = reason; - interrupt_time = C_cpu_milliseconds(); + interrupt_reason = reason; + interrupt_time = C_cpu_milliseconds(); + } } } @@ -4235,11 +4264,23 @@ C_regparm C_word C_fcall C_disable_interrupts(void) C_regparm C_word C_fcall C_establish_signal_handler(C_word signum, C_word reason) { int sig = C_unfix(signum); +#if defined(HAVE_SIGACTION) + struct sigaction new; + + new.sa_flags = 0; + sigemptyset(&new.sa_mask); +#endif if(reason == C_SCHEME_FALSE) C_signal(sig, SIG_IGN); else { signal_mapping_table[ sig ] = C_unfix(reason); +#if defined(HAVE_SIGACTION) + sigaddset(&new.sa_mask, sig); + new.sa_handler = global_signal_handler; + C_sigaction(sig, &new, NULL); +#else C_signal(sig, global_signal_handler); +#endif } return C_SCHEME_UNDEFINED; @@ -9173,3 +9214,19 @@ C_i_file_exists_p(C_word name, C_word file, C_word dir) } +C_regparm C_word C_fcall +C_i_pending_interrupt(C_word dummy) +{ + int i; + + if(interrupt_reason && interrupt_reason != C_TIMER_INTERRUPT_NUMBER) { + i = interrupt_reason; + interrupt_reason = 0; + return C_fix(i); + } + + if(pending_interrupts_count > 0) + return C_fix(pending_interrupts[ --pending_interrupts_count ]); + + return C_SCHEME_FALSE; +} diff --git a/tcp.scm b/tcp.scm index 4dfe579d..4731c01f 100644 --- a/tcp.scm +++ b/tcp.scm @@ -99,6 +99,7 @@ EOF (define-foreign-variable _ipproto_tcp int "IPPROTO_TCP") (define-foreign-variable _invalid_socket int "INVALID_SOCKET") (define-foreign-variable _ewouldblock int "EWOULDBLOCK") +(define-foreign-variable _eintr int "EINTR") (define-foreign-variable _einprogress int "EINPROGRESS") (define ##net#socket (foreign-lambda int "socket" int int int)) @@ -359,6 +360,8 @@ EOF #:network-timeout-error "read operation timed out" tmr fd) ) (loop) ) + ((eq? errno _eintr) + (##sys#dispatch-interrupt loop)) (else (##sys#update-errno) (##sys#signal-hook @@ -474,6 +477,9 @@ EOF #:network-timeout-error "write operation timed out" tmw fd) ) (loop len offset) ) + ((eq? errno _eintr) + (##sys#dispatch-interrupt + (cut loop len offset))) (else (##sys#update-errno) (##sys#signal-hook @@ -524,12 +530,16 @@ EOF (let loop () (if (eq? 1 (##net#select fd)) (let ((fd (##net#accept fd #f #f))) - (when (eq? -1 fd) - (##sys#update-errno) - (##sys#signal-hook - #:network-error 'tcp-accept (##sys#string-append "could not accept from listener - " strerror) - tcpl) ) - (##net#io-ports fd) ) + (cond ((not (eq? -1 fd)) (##net#io-ports fd)) + ((eq? errno _eintr) + (##sys#dispatch-interrupt loop)) + (else + (##sys#update-errno) + (##sys#signal-hook + #:network-error + 'tcp-accept + (##sys#string-append "could not accept from listener - " strerror) + tcpl)))) (begin (when tma (##sys#thread-block-for-timeout! @@ -559,7 +569,7 @@ EOF "int err, optlen;" "optlen = sizeof(err);" "if (typecorrect_getsockopt(socket, SOL_SOCKET, SO_ERROR, &err, (socklen_t *)&optlen) == -1)" - "C_return(-1);" + " C_return(-1);" "C_return(err);")) (define general-strerror (foreign-lambda c-string "strerror" int)) @@ -590,25 +600,28 @@ EOF (unless (##net#make-nonblocking s) (##sys#update-errno) (##sys#signal-hook #:network-error 'tcp-connect (##sys#string-append "fcntl() failed - " strerror)) ) - (when (eq? -1 (##net#connect s addr _sockaddr_in_size)) - (if (eq? errno _einprogress) - (let loop () - (let ((f (##net#select-write s))) - (when (eq? f -1) (fail)) - (unless (eq? f 1) - (when tmc - (##sys#thread-block-for-timeout! - ##sys#current-thread - (+ (current-milliseconds) tmc) ) ) - (##sys#thread-block-for-i/o! ##sys#current-thread s #:all) - (yield) - (when (##sys#slot ##sys#current-thread 13) - (##sys#signal-hook - #:network-timeout-error - 'tcp-connect - "connect operation timed out" tmc s) ) - (loop) ) ) ) - (fail) ) ) + (let loop () + (when (eq? -1 (##net#connect s addr _sockaddr_in_size)) + (cond ((eq? errno _einprogress) + (let loop2 () + (let ((f (##net#select-write s))) + (when (eq? f -1) (fail)) + (unless (eq? f 1) + (when tmc + (##sys#thread-block-for-timeout! + ##sys#current-thread + (+ (current-milliseconds) tmc) ) ) + (##sys#thread-block-for-i/o! ##sys#current-thread s #:all) + (yield) + (when (##sys#slot ##sys#current-thread 13) + (##sys#signal-hook + #:network-timeout-error + 'tcp-connect + "connect operation timed out" tmc s) ) + (loop2) ) ) )) + ((eq? errno _eintr) + (##sys#dispatch-interrupt loop)) + (else (fail) ) ))) (let ((err (get-socket-error s))) (cond ((fx= err -1) (##net#close s) diff --git a/tests/runtests.sh b/tests/runtests.sh index 9c3f7cf1..d54c9bca 100644 --- a/tests/runtests.sh +++ b/tests/runtests.sh @@ -153,10 +153,7 @@ $compile lolevel-tests.scm ./a.out echo "======================================== arithmetic tests ..." -if test -z "$MSYSTEM"; then - # the windows runtime library prints flonums differently - $interpret -D check -s arithmetic-test.scm -fi +$interpret -D check -s arithmetic-test.scm echo "======================================== pretty-printer tests ..." $interpret -s pp-test.scm @@ -316,6 +313,10 @@ fi $interpret -R posix -e '(delete-directory "tmpdir" #t)' +echo "======================================== signal tests ..." +$compile signal-tests.scm +./a.out + echo "======================================== lolevel tests ..." $interpret -s lolevel-tests.scm $compile lolevel-tests.scm diff --git a/tests/signal-tests.scm b/tests/signal-tests.scm new file mode 100644 index 00000000..6f004401 --- /dev/null +++ b/tests/signal-tests.scm @@ -0,0 +1,85 @@ +;;;; signal-tests.scm + + +#+windows +(begin + (print "this test can not be run on Windows") + (exit)) + + +;;XXX not tested yet + + +(use posix srfi-18 extras tcp) + + +(define received1 0) +(define received2 0) + +(define (tick c) + (write-char c) + (flush-output)) + +(define (handler sig) + (select sig + ((signal/usr1) + (tick #\1) + (set! received1 (add1 received1))) + ((signal/usr2) + (tick #\2) + (set! received2 (add1 received2))))) + +(define (fini _) + (printf "~%child terminating, received: ~a USR1, ~a USR2~%" + received1 received2) + (exit)) + +(define (child) + (print "child started") + (thread-start! + (lambda () + (let-values (((i o) (tcp-accept (tcp-listen 9999)))) + (tick #\!) + (assert (string=? "ok." (read-line i))) + (print "client connected.") + (close-input-port i) + (close-output-port o)))) + (thread-start! + (lambda () + (do () (#f) + (thread-sleep! 0.5) + (tick #\_)))) + (set-signal-handler! signal/usr1 handler) + (set-signal-handler! signal/usr2 handler) + (set-signal-handler! signal/term fini) + (do () (#f) + (thread-sleep! 1) + (tick #\.))) + +(let ((pid (process-fork child)) + (sent1 0) + (sent2 0)) + (sleep 1) + (print "sending signals to " pid) + (do ((i 1000 (sub1 i))) + ((zero? i)) + (thread-sleep! (/ (random 10) 1000)) + (do ((j (random 4) (sub1 j))) + ((zero? j)) + (case (random 2) + ((0) + (tick #\A) + (set! sent1 (add1 sent1)) + (process-signal pid signal/usr1)) + ((1) + (tick #\B) + (set! sent2 (add1 sent2)) + (process-signal pid signal/usr2))))) + (printf "~%signals sent: ~a USR1, ~a USR2~%" sent1 sent2) + (print "connecting ...") + (let-values (((i o) (tcp-connect "localhost" 9999))) + (display "ok.\n" o) + (close-input-port i) + (close-output-port o) + (sleep 1)) + (process-signal pid))Trap