[Openmcl-devel] Catching Control C
Waldek Hebisch
hebisch at math.uni.wroc.pl
Sun Jan 31 14:42:20 PST 2010
Gary Byers wrote:
> ^C (SIGINT) is sent by the OS (the tty driver) to some thread
> in the target process. (POSIX doesn't say which thread, and
> the last time that I checked there was some variance in OS
> behavior in this regard: it might get sent to the initial
> thread in the process, or to some thread that's already
> scheduled.)
>
> A handler in the lisp kernel handles SIGINT (and SIGQUIT
> and SIGTERM) and just sets a bit in a global variable.
>
> The default initial function in CCL does something like:
>
> (progn
> (so-some-low-level-reinitialization)
> (start-listener-thread)
> (housekeeping-loop))
>
> where HOUSEKEEPING-LOOP is something like:
>
> (loop
> (sleep .33)
> (housekeeping)) ; do periodic things about 3 times per second
>
> CCL::HOUSEKEEPING is defined in "ccl:level-1;l1-events.lisp" and
> may be somewhat readable ... One of the things that it does is
> to see if the OS-level process has received any of SIGINT/SIGQUIT/
> SIGTERM recently and, if so, try to determine what lisp thread should
> handle the pending signal and, in some cases, how. (The actual
> thread-level handling is done via PROCESS-INTERRUPT.)
>
> Some of this code is ancient (originally from the days before MCL
> had cooperative threads); it used to provide the means by which GUI
> event processing took place, which explains the filename and the
> names of some of the functions involved. The most important things
> that happen in CCL::HOUSEKEEPING are:
>
> - handling of post-gc hooks for object termination/finalization
> - this deferred signal handling stuff
> - flushing (via FORCE-OUTPUT) of some interactive output streams
>
> This deferred handling scheme may seem like overkill for a single-threaded
> application, but it has some advantages in that situation as well: critical
> code sections that don't want to be interrupted can ensure that they aren't
> interrupted (via WITHOUT-INTERRUPTS) without having to manipulate the thread's
> signal mask.
>
> The scheme does mean that a single-threaded application either:
> - isn't interruptible via ^C
> - has to arrange to check for pending signals periodically, which may or
> may not fit into the application's structure.
> - fires off a thread to do CCL::HOUSEKEEPING-LOOP (or something like it),
> in which case it's not exactly a single-threaded application anymore.
Thanks for information. I do not care if Clozure CL needs some extra
threads for internal tasks. I am affraid I can not simply call
CCL::HOUSEKEEPING-LOOP because I want that after my main function
exits control should go to normal Lisp toplevel. Using your
information I modified my program so that now Control C gives me
Lisp debugger. However, how can handle it in my main thread?
I produce exutable by loading code below and doing
(CCL::save-application "./app3" :PREPEND-KERNEL t
:application-class 'fricas-application)
-------------<code below>--------------------
(defclass fricas-application (ccl::application) ())
(defvar *my-toplevel-function* nil)
(defclass fricas-development-system (ccl::lisp-development-system) ())
(defmethod repl-function-name ((a fricas-development-system))
#'(lambda ()
(funcall *my-toplevel-function*)
(ccl::listener-function)))
(defmethod ccl::toplevel-function ((app fricas-application) init-file)
(declare (ignore init-file))
(call-next-method) ; this is critical, but shouldn't be.
(let ((ap (make-instance 'fricas-development-system)))
(ccl::toplevel-function ap nil)))
(defun mini-repl()
(loop (print (eval (read)))))
(defun my-error-handler(c)
(format t "~a~&" c)
(quit))
(defun mini-repl2()
(let ((*debugger-hook*
(lambda (condition previous-handler)
(format t "~a~&" c)
(quit))
))
(handler-bind ((error #'my-error-handler))
(mini-repl))))
(setf *my-toplevel-function* #'mini-repl2)
--
Waldek Hebisch
hebisch at math.uni.wroc.pl
More information about the Openmcl-devel
mailing list