Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Handling errors in standard output statements with openmcl

10 views
Skip to first unread message

Kirk Job Sluder

unread,
Dec 5, 2005, 12:36:22 AM12/5/05
to

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.

Vladimir Zolotykh

unread,
Dec 5, 2005, 6:08:27 AM12/5/05
to
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

Kirk Job Sluder

unread,
Dec 5, 2005, 7:12:54 AM12/5/05
to
In article <dn1738$hm0$1...@dcs.eurocom.od.ua>,
Vladimir Zolotykh <gsm...@eurocom.od.ua> wrote:

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?

Rob Warnock

unread,
Dec 6, 2005, 8:10:19 AM12/6/05
to
Kirk Job Sluder <kirk-...@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 <rp...@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607

Kirk Job Sluder

unread,
Dec 7, 2005, 12:30:39 AM12/7/05
to
In article <VoGdneERIrM...@speakeasy.net>,
rp...@rpw3.org (Rob Warnock) wrote:

> Kirk Job Sluder <kirk-...@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

Rob Warnock

unread,
Dec 9, 2005, 11:53:50 PM12/9/05
to
Kirk Job Sluder <kirk-...@jobsluder.net> wrote:
+---------------
| rp...@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.

0 new messages