[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]



I'm currently writing a software product using clisp that runs in a
batch-mode style in which no user interaction is necessary. But I'd
like to see the system stopping if CTRL-C (SIGINT) is pressed.
Normally, the system just tells me

	*** - Ctrl-C: User break

but continues running. This is a very unsatisfying behavior in an
UNIX environment. CLtL2 [Steele] tells something about in Chapter
`29.4.7. Establishing Restarts'

  Remark: Some readers may wonder what ought to be done by the
  ``abort'' key (or whatever the implementation's interrupt key
  is-Control-C or Control-G, for example). Such interrupts, whether
  synchronous or asynchronous in nature, are beyond the scope of this
  chapter and indeed are not currently addressed by Common Lisp at all.
  This may be a topic worth standardizing under separate cover. Here is
  some speculation about some possible things that might happen.

  An implementation might simply call abort or break directly without
  signaling any condition.

So far, there seems no portable way of handling this. The `impnotes'
also doesn't tell something about it.

After some investigation of the code I found that the function bound
to the variable *BREAK-DRIVER* is used to handle these kind of breaks.

The code of the function bound to this variable is BREAK-LOOP defined
in `user1.lsp'. This function is called from the interrupt-handler
`tast_break' which is responsible for handling keyboard-interruptions.
The last command in this function calls

      break_driver(T); # Break-Driver aufrufen

which in turn call calls the function bound to *BREAK-DRIVER* as one
can see from the following excerpt taken from `debug.d'

# Startet einen untergeordneten Driver (Read-Eval-Print-Loop)
# break_driver(continuable);
# > continuable: Flag, ob nach Beendigung des Drivers fortgefahren werden kann.
# kann GC auslösen
  global void break_driver (object continuable);
  global void break_driver(continuable)
    var reg3 object continuable;
    { pushSTACK(continuable);
     {var reg4 object driverfun = Symbol_value(S(break_driver));
						# Wert von *BREAK-DRIVER*
      if (!nullp(driverfun))
          #ifdef HAVE_NUM_STACK
          var reg2 uintD* old_NUM_STACK = NUM_STACK;
          var reg1 uintD* old_NUM_STACK_normal = NUM_STACK_normal;
          pushSTACK(STACK_0); funcall(driverfun,1);
---			      ^^^^^^^^^^^^^^^^^^^

My idea was now to bind a function like the following to

(defun *break-driver* (continuable
                       &optional (condition nil) (print-it nil)
                       &aux (may-continue
                             (or continuable
                                 (and condition
                                      (find-restart 'continue condition))
                                 ) )
                       (interactive-p (interactive-stream-p *debug-io*))
                       (commandsr '())
  (declare (ignore print-it may-continue interactive-p commandsr))
  (when condition
    (oops (simple-condition-format-string    condition)
          (simple-condition-format-arguments condition)))
  (format *ERROR-OUTPUT* "~&Bye.")

(setq *break-driver* #'*break-driver*)

The lambda-list is borrowed from the function BREAK-LOOP. This simply
ignores all kinds of breaks (even continuable ones) and exits. If a
condition was signaled it is printed before leaving (oops is a macro
hiding #'format).

Now my questions are (took a long time to come here).

1. Is this a good way of handling this situation?

   It seems to work quiet fine.

2. Is there a better way of handling this?

Thanx in advance.


Roger Kehr  	kehr@iti.informatik.th-darmstadt.de