[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*)

(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)

(defun mini-repl2()
  (let ((*debugger-hook*
            (lambda (condition previous-handler)
                (format t "~a~&" c)
    (handler-bind ((error #'my-error-handler))

(setf *my-toplevel-function* #'mini-repl2)

                              Waldek Hebisch
hebisch at math.uni.wroc.pl 

More information about the Openmcl-devel mailing list