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
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?
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
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
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