Subject: [clisp-list] must not use signal handlers written in Lisp

From: "Hoehle, Joerg-Cyril" <[email-masked]>
Date: Tue Jan 21 08:36:09 2003

I'm playing Cassandra again.

One must currently not install a signal handler written in Lisp into CLISP.

Reasons are both from i) GC and ii) not all parts of CLISP being reentrant.

Although Peter Wood demo'ed one in March 2002, a signal handler in Lisp is completely unreliable and may crash at any GC or cause other trouble.

[5]> (defun wow (s) (format t "Wow! You zapped me with signal ~R~%" s))
WOW
[6]> (setf (sa-handler oldsigact) #'wow)
#<CLOSURE WOW (S) (DECLARE (SYSTEM::IN-DEFUN WOW)) (BLOCK WOW (FORMAT T "Wow! You zapped me with signal ~R~%" S))>
[7]> (signal-action-install linux:SIGINT oldsigact)
T

;;now Ctrl-C calls #'wow !!
[8]> Wow! You zapped me with signal two
Wow! You zapped me with signal two
Wow! You zapped me with signal two
Wow! You zapped me with signal two

	GC problems:
- Signal handlers are called between any two processor instructions (when out of the kernel). Therefore, GC roots hold in registers and not yet pushed onto the STACK would become lost.
- C code cannot cope with GC arbitrarily moving stuff while it's working on Lisp objects.

	Reentrancy problems:
E.g. foreign.d features foreign_layout() which modifies the variables data_size, data_alignment and data_splitable. This is used by FFI::FOREIGN-CALL-OUT, each time argument or return types are parsed, but also by FFI:SIZEOF.
That means that if some code is using these and a signal handlers jumps in and modifies them, anything can happen.

set/clr_break_sem_1-7 has been in CLISP for the last 10 years to protect various non-reentrant or non-interruptible areas (IIRC, Atari and DOS would not allow to use a directory control block twice during one traversal). However I wouldn't bet on
o whether all non-reentrant places are identified this way
o whether these (seemingly history) break semaphore protected places are still in good shape.
Their purpose is documented in spvw.d. lispbibl.d would be a better choice.

lispbibl.d says:
# Break-Semaphores
# As long as a Break-Semaphore is set, the Lisp-Program can not
# be interrupted. Purpose:
# - backup of Consistencies,
# - Non-reentrant Data-Structures (like e.g. DTA_buffer) can not
#   be used recursively.
Signal handlers written in Lisp would be subject to the same limitation.

		Solution path proposals:
A) Bruno Haible proposed running signal handlers in separate threads. We don't have threads right now and I wouldn't hold my breath for them (vapourware for the last 5 years).

B) I propose delaying them and invoking them in safe places. Typically in the bytecode evaluation loop, where ^C is also checked for. That should be often enough. That would avoid concern i) (GC).

The invocation of signal handlers cannot be thrown into the places where interruptp() lies, because code must be ready to continue from there, whereas current interruptp() places are IMHO only safe for abort (throwing to a REPL), but not for arbitrary GC inbetween.

Note that the current break_sem[] code already serves to delay SIGINT a little bit to ensure data structure consistency (but not reentrancy).

OTOH, if one has threads, signals (in other threads) become trivial. Can this be reason enough not to bother with signals and attempt to direct all effort into threads?

o Propose an API, e.g. CMUCL's
http://cvs2.cons.org/ftp-area/cmucl/doc/cmu-user/unix.html
system:enable-interrupt signal function

		Open issues:

o Also delay non-Lisp handlers? I'd say yes, in that they could call back into the FFI and this would be safe. People who don't want them to be delayed (and don'T call back or use Lisp) could still call the original UNIX function.

o Identify any non-reentrant places in CLISP and make them safe (e.g. foreign.d data_* variables) - somehow. That would also work towards threads.

o interaction with signal handlers that CLISP needs itself, e.g. SIGPIPE(?), SIGSEGV, SIGINT, SIGALRM, SIGWINCH.

Regards,
	Jörg Höhle.