Subject: Re: Handling errors in standard output statements with openmcl (learner questions)
From: rpw3@rpw3.org (Rob Warnock)
Date: Fri, 09 Dec 2005 22:53:50 -0600
Newsgroups: comp.lang.lisp
Message-ID: <YsCdnZN-SdXDwgfenZ2dnUVZ_v-dnZ2d@speakeasy.net>
Kirk Job Sluder  <kirk-nospam@jobsluder.net> wrote:
+---------------
| rpw3@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@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607