From: Kirk Job Sluder
Subject: Handling errors in standard output statements with openmcl
Date: 
Message-ID: <kirk-nospam-16A0E5.00363205122005@newsclstr01.news.prodigy.com>
I have a very simple error-checking script that processes text from 
standard input, looks up values in a database, and then prints the 
values to standard output.  The function that does all the work looks 
like this:

Basically, read a line.  Print a modified line.  Print out comments 
unmodified.  

(defun validate-chat-summary-file ()
  "Validate a summary file."
  (loop for line = (read-line *standard-input* nil nil)
   while line do
   (if (detect-bogo-line line) 
     (format t "~a~%" (validate-chat-summary-line line))
     (format t "~a~%" line)))
  (finish-output))

So far this script does its job, but I've noticed something odd if I 
pipe the output into less, and then quit less midway through the file.  
The program hangs, then gives me:

Unhandled exception 11 at 0x410be24, context->regs at #xbff9eee8
Write operation to unmapped address 0x75ff8
 While executing: #<Function WRITE-INTERNAL #x080d6d1e>
? for help
[25568] OpenMCL kernel debugger: [25568] OpenMCL kernel debugger: 
[25568] OpenMCL kernel debugger: [25568] OpenMCL kernel debugger: 
[25568] OpenMCL kernel debugger: [25568] OpenMCL kernel debugger: 
[25568] OpenMCL kernel debugger: [25568] OpenMCL kernel debugger:

I know that what is happening when I quit less, OpenMCL still tries to 
write to an output buffer.  My question is (after a modest amount of 
searching) primarily for future reference.  How can I make this output 
function "safe" so that if *standard-input* closes, the script exits 
gracefully.

From: Vladimir Zolotykh
Subject: Re: Handling errors in standard output statements with openmcl
Date: 
Message-ID: <dn1738$hm0$1@dcs.eurocom.od.ua>
Kirk Job Sluder wrote:

> [skip] How can I make this output 
> function "safe" so that if *standard-input* closes, the script exits 
> gracefully.
I think as in any other language, you might check the state of the
output stream (open-stream-p) and quit if it's closed or you might
use handler-case to catch the error and quit accordingly.


-- 
Vladimir Zolotykh
From: Kirk Job Sluder
Subject: Re: Handling errors in standard output statements with openmcl
Date: 
Message-ID: <kirk-nospam-EB3405.07125405122005@newsclstr01.news.prodigy.com>
In article <············@dcs.eurocom.od.ua>,
 Vladimir Zolotykh <······@eurocom.od.ua> wrote:

> Kirk Job Sluder wrote:
> 
> > [skip] How can I make this output 
> > function "safe" so that if *standard-input* closes, the script exits 
> > gracefully.
> I think as in any other language, you might check the state of the
> output stream (open-stream-p) and quit if it's closed or you might
> use handler-case to catch the error and quit accordingly.

After posting I discovered and tried open-stream-p and that didn't work.

Using handler-case is something I really could use some help with.  My 
main guide is Practical Common Lisp, and while it does a fairly good job 
explaining how to handle errors you create, I'm uncertain how to handle 
this particular error.  

I also ran the code through clisp and didn't get the same results. So 
could this be a bug in openmcl?
From: Rob Warnock
Subject: Re: Handling errors in standard output statements with openmcl
Date: 
Message-ID: <VoGdneERIrMmEAjeRVn-pQ@speakeasy.net>
Kirk Job Sluder  <···········@jobsluder.net> wrote:
+---------------
| ... if I pipe the output into less, and then quit less midway
| through the file.  The program hangs, then gives me:
| 
| Unhandled exception 11 at 0x410be24, context->regs at #xbff9eee8
| Write operation to unmapped address 0x75ff8
...
| I know that what is happening when I quit less, OpenMCL still tries to 
| write to an output buffer.  My question is (after a modest amount of 
| searching) primarily for future reference.  How can I make this output 
| function "safe" so that if *standard-input* closes, the script exits 
| gracefully.
+---------------

I had exactly the same problem when writing a CMUCL-based web
application server, when the browser user hit "Stop" or "Back"
or "Close" before a large request has been completely sent.
Under Unix/Linux, this causes a SIGPIPE signal to be sent to
the CMUCL process, which I tried to catch using CMUCL's signal-
handling code... which is unfortunately not completely reliable.

But Dan Barlow [thanks, Dan!!] pointed out a much simpler way:
Simply set the Lisp process to *ignore* SIGPIPE, and then you'll
get ordinary system call errors (with EPIPE) from any writes after
the pipe [or socket] is closed. In CMUCL, you can do that this way:

    ;;; Somewhere in your init code:
    (defvar *old-sigpipe-handler* (system:enable-interrupt :sigpipe :ignore))

Then wrap the following around your outputting code:

    (handler-case
	(send-output stream)		; the main work
      (error (condition)
	(cond
	  ((eql unix:unix-errno unix:EPIPE)
	   ;; Log it locally if you want to & can, then...
	   (ignore-errors (close stream :abort t)))
	  ;; <== Maybe insert other error cases here.
	  (t			; catch-all
	   ;; Log it locally if you want to & can, then...
           ;; If the stream is still open, try to print the error
           ;; onto the output stream, too.
           (when (open-stream-p stream)
             (ignore-errors     ; But might EPIPE here, too, so protect.
               (progn
                 (format stream "Internal server error!~%~a~%" condition)
                 (finish-output stream)  ; Either of these might EPIPE,
                 (close stream))))))))   ;  which is why the IGNORE-ERRORS.

As I said, this works fine for CMUCL. You'll need to find out how
to do the same SIGPIPE ignoring in OpenMCL, and also translate the
other UNIX package code above appropriately...


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Kirk Job Sluder
Subject: Re: Handling errors in standard output statements with openmcl (learner questions)
Date: 
Message-ID: <kirk-nospam-8FF54D.00303707122005@newsclstr01.news.prodigy.com>
In article <······················@speakeasy.net>,
 ····@rpw3.org (Rob Warnock) wrote:

> Kirk Job Sluder  <···········@jobsluder.net> wrote:
> +---------------
> | ... if I pipe the output into less, and then quit less midway
> | through the file.  The program hangs, then gives me:
> | 

> I had exactly the same problem when writing a CMUCL-based web
> application server, when the browser user hit "Stop" or "Back"
> or "Close" before a large request has been completely sent.
> Under Unix/Linux, this causes a SIGPIPE signal to be sent to
> the CMUCL process, which I tried to catch using CMUCL's signal-
> handling code... which is unfortunately not completely reliable.
> 
> But Dan Barlow [thanks, Dan!!] pointed out a much simpler way:
> Simply set the Lisp process to *ignore* SIGPIPE, and then you'll
> get ordinary system call errors (with EPIPE) from any writes after
> the pipe [or socket] is closed. In CMUCL, you can do that this way:
> 
>     ;;; Somewhere in your init code:
>     (defvar *old-sigpipe-handler* (system:enable-interrupt :sigpipe :ignore))
> 
> Then wrap the following around your outputting code:
> 
>     (handler-case
> 	(send-output stream)		; the main work
>       (error (condition)....; 
...rest of block snipped for space...

It turns out I didn't have a problem with openmcl SIGPIPE (at least not 
in this case.  This simplifies the problem, giving at least one desired 
result, a graceful exit to the program.  

 (defun main ()
  "Main entry point for program."
   (handler-case (validate-chat-summary-file)
     (error (condition) ;see question below.
       (progn 
    (format *error-output* "~a~%" condition) ;this fails to produce 
output.
    ;(force-output *error-output*) ;this hangs
    (quit))))
  (quit))


Still, I have some questions.  Why would the write to *error-output* 
here hang?  And since the big problem seems to be due to my ignorance of 
lisp error handling, does anyone know of a good tutorial to muddle my 
way through it?   This is one of those cases where PCL leaves a bit 
unsaid.

So for example, in the (error (condition) _form_) clause "error" is not 
invoking the function ERROR, but saying that the _form_ should be 
invoked for every condition of type ERROR.  If I wanted to catch all 
warnings, I could use (warning (condition) _form_).  Is that correct?

Also, why would the following NOT work? (Hangs on exit from less.)

(defun main2 ()
  (ignore-errors 
    (validate-chat-summary-file)
    (quit)))

> -Rob
From: Rob Warnock
Subject: Re: Handling errors in standard output statements with openmcl (learner questions)
Date: 
Message-ID: <YsCdnZN-SdXDwgfenZ2dnUVZ_v-dnZ2d@speakeasy.net>
Kirk Job Sluder  <···········@jobsluder.net> wrote:
+---------------
| ····@rpw3.org (Rob Warnock) wrote:
| >  ;;; Somewhere in your init code:
| >  (defvar *old-sigpipe-handler* (system:enable-interrupt :sigpipe :ignore))
| > Then wrap the following around your outputting code:
| >     (handler-case
| > 	(send-output stream)		; the main work
| >       (error (condition)....; 
...
| It turns out I didn't have a problem with openmcl SIGPIPE (at least not 
| in this case.  This simplifies the problem, giving at least one desired 
| result, a graceful exit to the program.  
| 
|  (defun main ()
|   "Main entry point for program."
|    (handler-case (validate-chat-summary-file)
|      (error (condition) ;see question below.
|        (progn 
|     (format *error-output* "~a~%" condition) ;this fails to produce 
| output.
|     ;(force-output *error-output*) ;this hangs
|     (quit))))
|   (quit))
| 
| 
| Still, I have some questions.  Why would the write to *error-output* 
| here hang?
+---------------

Hmmm... You might want to try running the OpenMCL equivalent of the
following and see what you get. In particular, see if *ERROR-OUTPUT*
is bound to your terminal or if by default *ERROR-OUTPUT* is bound
to the same thing as *STANDARD-OUTPUT*, which would explain the behavior
you're seeing. That is, what I guessing might be happening is that
by default OpenMCL maps *both* *ERROR-OUTPUT* & *STANDARD-OUTPUT* to
the process's standard output, so that when you pipe it into "less"
(say) and the pipe break, *both* *ERROR-OUTPUT* & *STANDARD-OUTPUT*
get jammed up. [If this is correct, there might be a different OpenMCL
stream variable you can use to get your error output, maybe *DEBUG-IO*
or *TERMINAL-IO*, see below.]

Start with just the "standardized stream variables" [CLHS 21.1.2
"Stream Variables"] listed just above the first comment below, and
then add more as you discover any implementation-dependent variables
they map to [are "synonym streams" for -- the ones below the comment].
Then when you get to "ground" stream objects, run DESCRIBE on them,
and see what the bottommost things pointed to by *STANDARD-OUTPUT*
and *ERROR-OUTPUT* really are. Here's what I ended up with for CMUCL:

    $ cat ./describe-streams
    #!/usr/local/bin/cmucl -script

    (dolist (name '(*debug-io*
		    *error-output*       
		    *query-io*        
		    *standard-input*    
		    *standard-output*    
		    *terminal-io*     
		    *trace-output*
		    ;; The following added after looking
		    ;; at the output from the above set.
		    system:*stdin*
		    system:*stdout*
		    system:*stderr*
		    system:*tty*
		    ))
      (format t "~s ==> ~s~%" name (symbol-value name)))

    (dolist (name '(system:*stdin*
		    system:*stdout*
		    system:*stderr*
		    system:*tty*
		    ))
      (format t "~%~%(describe '~s) ==>~%" name)
      (describe name))
    $ 

When run, this gives [trimmed for brevity] the following:

    $ ./describe-streams | cat
    *DEBUG-IO* ==> #<Synonym Stream to *TERMINAL-IO*>
    *ERROR-OUTPUT* ==> #<Synonym Stream to SYSTEM:*STDERR*>
    *QUERY-IO* ==> #<Synonym Stream to *TERMINAL-IO*>
    *STANDARD-INPUT* ==> #<Two-Way Stream, Input = #<Synonym Stream
	to SYSTEM:*STDIN*>, Output = #<Synonym Stream to SYSTEM:*STDOUT*>>
    *STANDARD-OUTPUT* ==> #<Synonym Stream to SYSTEM:*STDOUT*>
    *TERMINAL-IO* ==> #<Synonym Stream to SYSTEM:*TTY*>
    *TRACE-OUTPUT* ==> #<Synonym Stream to SYSTEM:*STDOUT*>
    SYSTEM:*STDIN* ==> #<Stream for Standard Input>
    SYSTEM:*STDOUT* ==> #<Stream for Standard Output>
    SYSTEM:*STDERR* ==> #<Stream for Standard Error>
    SYSTEM:*TTY* ==> #<Stream for the Terminal>

    (describe 'SYSTEM:*STDIN*) ==>
    *STDIN* is an external symbol in the SYSTEM package.
    It is a special variable; its value is #<Stream for Standard Input>.
       #<Stream for Standard Input> is a structure of type FD-STREAM.
    ...
       FD: 0.
    ...
       PATHNAME: NIL.

    Special documentation:
      The stream connected to the standard input (file descriptor 0).
    It is defined in:
    target:code/fd-stream.lisp

    (describe 'SYSTEM:*STDOUT*) ==>
    *STDOUT* is an external symbol in the SYSTEM package.
    It is a special variable; its value is #<Stream for Standard Output>.
       #<Stream for Standard Output> is a structure of type FD-STREAM.
    ...
       FD: 1.
    ...
       PATHNAME: NIL.

    Special documentation:
      The stream connected to the standard output (file descriptor 1).
    It is defined in:
    target:code/fd-stream.lisp

    (describe 'SYSTEM:*STDERR*) ==>
    *STDERR* is an external symbol in the SYSTEM package.
    It is a special variable; its value is #<Stream for Standard Error>.
       #<Stream for Standard Error> is a structure of type FD-STREAM.
    ...
       FD: 2.
    ...
       PATHNAME: NIL.

    Special documentation:
      The stream connected to the standard error output (file descriptor 2).
    It is defined in:
    target:code/fd-stream.lisp

    (describe 'SYSTEM:*TTY*) ==>
    *TTY* is an external symbol in the SYSTEM package.
    It is a special variable; its value is #<Stream for the Terminal>.
       #<Stream for the Terminal> is a structure of type FD-STREAM.
    ...
       FD: 4.
    ...
       PATHNAME: NIL.
    Special documentation:
      The stream connected to the controlling terminal or NIL if there is none.
    It is defined in:

Here the "ground" objects are the values of SYSTEM:*STDIN*,
SYSTEM:*STDOUT*, SYSTEM:*STDERR*, and SYSTEM:*TTY*, and they're
"FD-STREAMS" for the Unix file descriptors (FDs) 0, 1, 2, & 4,
respectively. [The latter is a CMUCL-specific thing, and is a
bi-directional stream opened on "/dev/tty" iff the image was
*not* started in "batch mode".]

+---------------
| And since the big problem seems to be due to my ignorance of lisp
| error handling, does anyone know of a good tutorial to muddle my 
| way through it?
+---------------

You might want to read this one:

    http://www.nhplace.com/kent/Papers/Condition-Handling-2001.html

+---------------
| Also, why would the following NOT work? (Hangs on exit from less.)
| (defun main2 ()
|   (ignore-errors 
|     (validate-chat-summary-file)
|     (quit)))
+---------------

See above. If OpenMCL maps both *ERROR-OUTPUT* & *STANDARD-OUTPUT*
to the process's standard output, that might be your problem.


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607