ecl/doc/signals.xmlf
Daniel Kochmański 1a3aecc2d6 doc: add documentation as doc subdirectory
Signed-off-by: Daniel Kochmański <daniel@turtleware.eu>
2015-08-04 21:55:36 +02:00

265 lines
13 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE book [
<!ENTITY % eclent SYSTEM "ecl.ent">
%eclent;
]>
<book xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
<chapter xml:id="ext.signals">
<title>Signals and interrupts</title>
<section xml:id="ext.signals.intro">
<title>Problems associated to signals</title>
<para>POSIX contemplates the notion of "signals", which are events that
cause a process or a thread to be interrupted. Windows uses the term
"exception", which includes also a more general kind of errors.</para>
<para>In both cases the consequence is that a thread or process may be
interrupted at any time, either by causes which are intrinsic to them
(synchronous signals), such as floating point exceptions, or extrinsic
(asynchronous signals), such as the process being aborted by the
user.</para>
<para>Of course, those interruptions are not always welcome. When the
interrupt is delivered and a handler is invoked, the thread or even the
whole program may be in an inconsistent state. For instance the thread may
have acquired a lock, or it may be in the process of filling the fields of
a structure. Furthermore, sometimes the signal that a process receives may
not even be related to it, as in the case when a user presses Cltr-C
and a SIGINT signal is delivered to an arbitrary thread, or when the
process receives the Windows exception CTRL_CLOSE_EVENT denoting
that the terminal window is being closed.</para>
<para>Understanding this, POSIX restricts severely what functions can be
called from a signal handler, thereby limiting its usefulness. However,
Common Lisp users expect to be able to handle floating point exceptions and
to gracefully manage user interrupts, program exits, etc. In an attempt to
solve this seemingly impossible problem, &ECL; has taken a pragmatic
approach that works, it is rather safe, but involves some work on the &ECL;
maintainers and also on users that want to embed &ECL; as a library.</para>
</section>
<section xml:id="ext.signals.kinds">
<title>Kinds of signals</title>
<section xml:id="ext.signals.synchronous">
<title>Synchronous signals</title>
<para>The name derives from POSIX and it denotes interrupts that occur due
to the code that a particular thread executes. They are largely equivalent
to C++ and Java exceptions, and in Windows they are called "unchecked
exceptions."</para>
<para>Common Lisp programs may generate mostly three kinds of synchronous
signals:</para>
<itemizedlist>
<listitem>
<para>Floating point exceptions, that result from overflows in
computations, division by zero, and so on.</para>
</listitem>
<listitem>
<para>Access violations, such as dereferencing NULL pointers,
writing into regions of memory that are protected, etc.</para>
</listitem>
<listitem>
<para>Process interrupts.</para>
</listitem>
</itemizedlist>
<para>The first family of signals are generated by the floating point
processing hardware in the computer, and they typically happen when code
is compiled with low security settings, performing mathematical operations
without checks.</para>
<para>The second family of signals may seem rare, but unfortunately they
still happen quite often. One scenario is wrong code that handles memory
directly via FFI. Another one is undetected stack overflows, which typically
result in access to protected memory regions. Finally, a very common cause
of these kind of exceptions is invoking a function that has been compiled
with very low security settings with arguments that are not of the expected
type -- for instance, passing a float when a structure is expected.</para>
<para>The third family is related to the multiprocessing capabilities in
Common Lisp systems and more precisely to the <xref
linkend="ref.mp.interrupt-process"/> function which is used to kill,
interrupt and inspect arbitrary threads. In POSIX systems &ECL; informs a
given thread about the need to interrupt its execution by sending a
particular signal from the set which is available to the user.</para>
<para>Note that in neither of these cases we should let the signal pass
unnoticed. Access violations and floating point exceptions may propagate
through the program causing more harm than expected, and without
process interrupts we will not be able to stop and cancel different
threads. The only question that remains, though, is whether such signals can
be handled by the thread in which they were generated and how.</para>
</section>
<section xml:id="ext.signals.asynchronous">
<title>Asynchronous signals</title>
<para>In addition to the set of synchronous signals or "exceptions", we
have a set of signals that denote "events", things that happen while the
program is being executed, and "requests". Some typical examples are:</para>
<itemizedlist>
<listitem>
<para>Request for program termination (SIGKILL, SIGTERM).</para>
</listitem>
<listitem>
<para>Indication that a child process has finished.</para>
</listitem>
<listitem>
<para>Request for program interruption (SIGINT), typically as a
consecuence of pressing a key combination, Ctrl-C.</para>
</listitem>
</itemizedlist>
<para>The important difference with synchronous signals is that we have no
thread that causes the interrupt and thus there is no preferred way of
handling them. Moreover, the operating system will typically dispatch these
signals to an arbitrary thread, unless we set up mechanisms to prevent
it. This can have nasty consequences if the incoming signal interrupt a
system call, or leaves the interrupted thread in an inconsistent
state.</para>
</section>
</section>
<section xml:id="ext.signals.implementation">
<title>Signals and interrupts in &ECL;</title>
<para>The signal handling facilities in &ECL; are constrained by two
needs. First of all, we can not ignore the synchronous signals mentioned in
<xref linkend="ext.signals.synchronous"/>. Second, all other signals should
cause the least harm to the running threads. Third, when a signal is handled
synchronously using a signal handler, the handler should do almost nothing
unless we are completely sure that we are in an interruptible region, that is
outside system calls, in code that &ECL; knows and controls.</para>
<para>The way in which this is solved is based on the existence of both
synchronous and asynchronous signal handling code, as explained in the
following two sections.</para>
<section xml:id="ext.signals.asynchronous-handler">
<title>Handling of asynchronous signals</title>
<para>In systems in which this is possible, &ECL; creates a signal handling
thread to detect and process asynchronous signals (See <xref
linkend="ext.signals.asynchronous"/>). This thread is a trivial one and does
not process the signals itself: it communicates with, or launches new signal
handling threads to act accordingly to the denoted events.</para>
<para>The use of a separate thread has some nice consequences. The first
one is that those signals will not interrupt any sensitive code. The
second one is that the signal handling thread will be able to execute
arbitrary lisp or C code, since it is not being executed in a sensitive
context. Most important, this style of signal handling is the recommended
one by the POSIX standards, and it is the one that Windows uses.</para>
<para>The installation of the signal handling thread is dictated by a boot
time option, <varname>ECL_OPT_SIGNAL_HANDLING_THREAD</varname>, and it will
only be possible in systems that support either POSIX or Windows
threads.</para>
<para>Systems which embed &ECL; as an extension language may wish to
deactivate the signal handling thread using the previously mentioned
option. If this is the case, then they should take appropriate measures to
avoid interrupting the code in &ECL; when such signals are delivered.</para>
<para>Systems which embed &ECL; and do not mind having a separate signal
handling thread can control the set of asynchronous signals which is handled
by this thread. This is done again using the appropriate boot options such
as <varname>ECL_OPT_TRAP_SIGINT</varname>,
<varname>ECL_OPT_TRAP_SIGTERM</varname>, etc. Note that in order to detect
and handle those signals, &ECL; must block them from delivery to any other
thread. This means changing the <function>sigprocmask()</function> in POSIX
systems or setting up a custom <function>SetConsoleCtrlHandler()</function>
in Windows.</para>
</section>
<section xml:id="ext.signals.synchronous-handler">
<title>Handling of synchronous signals</title>
<para>We have already mentioned that certain synchronous signals and
exceptions can not be ignored and yet the corresponding signal handlers are
not able to execute arbitrary code. To solve this seemingly impossible
contradiction, &ECL; uses a simple solution, which is to mark the sections of
code which are interruptible, and in which it is safe for the handler to run
arbitrary code. All other regions would be considered "unsafe" and would be
protected from signals and exceptions.</para>
<para>In principle this "marking" of safe areas can be done using POSIX
functions such as <function>pthread_sigmask()</function> or
<function>sigprocmask()</function>. However in practice this is slow, as it
involves at least a function call, resolving thread-local variables, etc,
etc, and it will not work in Windows.</para>
<para>Furthermore, sometimes we want signals to be detected but not to be
immediately processed. For instance, when reading from the terminal we want
to be able to interrupt the process, but we can not execute the code from
the handler, since the C function which is used to read from the terminal,
<function>read()</function>, may have left the input stream in an
inconsistent, or even locked state.</para>
<para>The approach in &ECL; is more lightweight: we install our own signal
handler and use a thread-local variable as a flag that determines whether
the thread is executing interrupt safe code or not. More precisely, if the
variable <code>ecl_process_env()->disable_interrupts</code> is set, signals
and exceptions will be postponed and then the information about the signal
is queued. Otherwise the appropriate code is executed: for instance invoking
the debugger, jumping to a condition handler, quitting, etc.</para>
<para>Systems that embed &ECL; may wish to deactivate completely these
signal handlers. This is done using the boot options,
<varname>ECL_OPT_TRAP_SIGFPE</varname>,
<varname>ECL_OPT_TRAP_SIGSEGV</varname>,
<varname>ECL_OPT_TRAP_SIGBUS</varname>,
<varname>ECL_OPT_TRAP_INTERRUPT_SIGNAL</varname>.</para>
<para>Systems that embed &ECL; and want to allow handling of synchronous
signals should take care to also trap the associated lisp conditions that
may arise. This is automatically taken care of by functions such as
<function>si_safe_eval()</function>, and in all other cases it can be solved
by enclosing the unsafe code in a <function>CL_CATCH_ALL_BEGIN</function>
frame (See <xref linkend="ref.embed.cl_catch_all"/>).</para>
</section>
</section>
<section xml:id="ext.signals.embedding">
<title>Considerations when embedding &ECL;</title>
<para>There are several approaches when handling signals and interrupts in
a program that uses &ECL;. One is to install your own signal handlers. This
is perfectly fine, but you should respect the same restrictions as &ECL;.
Namely, you may not execute arbitrary code from those signal handlers, and
in particular it will not always be safe to execute Common Lisp code from
there.</para>
<para>If you want to use your own signal handlers then you should set the
appropriate options before invoking <function>cl_boot()</function>, as
explained in <xref linkend="ref.embed.ecl_set_option"/>. Note that in this
case &ECL; will not always be able to detect floating point exceptions,
specially if your compiler does not support C99 and the corresponding
floating point flags.</para>
<para>The other option is to let &ECL; handle signals itself. This would be
safer when the dominant part of the code is Common Lisp, but you may need
to protect the code that embeds &ECL; from being interrupted using either
the macros <xref linkend="ref.embed.ecl_disable_interrupts"/> and <xref
linkend="ref.embed.ecl_enable_interrupts"/> or the POSIX functions
<function>pthread_sigmaks</function> and
<function>sigprocmask</function>.</para>
</section>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="ref_signals.xmlf" xpointer="ext.signals.dict"/>
</chapter>
</book>
<!-- Keep this comment at the end of the file
Local variables:
mode: nxml
sgml-parent-document: "ecl.xml"
sgml-indent-step: 1
nxml-child-indent: 1
nxml-outline-child-indent: 1
fill-column: 79
End:
-->