Infinite loop on broken pipe

Bug #252221 reported by Gwen Weinholt
2
Affects Status Importance Assigned to Milestone
Ikarus Scheme
Fix Committed
Low
Abdulaziz Ghuloum

Bug Description

I was piping the output of my program to less, and exited less before the program was finished. Ikarus went into a (seemingly) infinite loop, according to strace:
write(1, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"..., 16384) = -1 EPIPE (Broken pipe)
write(1, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"..., 16384) = -1 EPIPE (Broken pipe)
write(1, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"..., 16384) = -1 EPIPE (Broken pipe)
write(1, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"..., 16384) = -1 EPIPE (Broken pipe)
write(1, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"..., 16384) = -1 EPIPE (Broken pipe)

This bash command line triggers the bug:
echo | ikarus | (read;read)

Related branches

Revision history for this message
Abdulaziz Ghuloum (aghuloum) wrote :

I tried the command line that you mention but nothing happens. I tried it on Mac OS X and Debian and it was terminating as usual:

$ echo | ikarus | (read;read)
$ echo | ikarus | (read;read)
$ echo | ikarus | (read;read)

Can you list what happens when you issue that command line. Also, can you provide more information about your operating system/environment. Thanks.

Revision history for this message
Derick Eddington (derick-eddington) wrote : Re: [Bug 252221] Re: Infinite loop on broken pipe

On Sun, 2008-07-27 at 07:02 +0000, Abdulaziz Ghuloum wrote:
> I tried the command line that you mention but nothing happens.

The problem happens to me on Ubuntu 8.04.

$ strace ikarus 2>/tmp/ikarus-read-read.strace | (read;read)

ikarus-read-read.strace:

execve("/home/d/bin/ikarus", ["ikarus"], [/* 45 vars */]) = 0
[...all the start-up stuff...]
write(1, "Ikarus Scheme version 0.0.3+ (re"..., 62) = 62
write(1, "\n", 1) = 1
write(1, "Copyright (c) 2006-2008 Abdulazi"..., 43) = 43
write(1, ">", 1) = -1 EPIPE (Broken pipe)
write(1, ">", 1) = -1 EPIPE (Broken pipe)
write(1, ">>", 2) = -1 EPIPE (Broken pipe)
write(1, ">>", 2) = -1 EPIPE (Broken pipe)
write(1, ">>>", 3) = -1 EPIPE (Broken pipe)
write(1, ">>>", 3) = -1 EPIPE (Broken pipe)
write(1, ">>>>", 4) = -1 EPIPE (Broken pipe)
write(1, ">>>>", 4) = -1 EPIPE (Broken pipe)
write(1, ">>>>>", 5) = -1 EPIPE (Broken pipe)
write(1, ">>>>>", 5) = -1 EPIPE (Broken pipe)
write(1, ">>>>>>", 6) = -1 EPIPE (Broken pipe)
write(1, ">>>>>>", 6) = -1 EPIPE (Broken pipe)
write(1, ">>>>>>>", 7) = -1 EPIPE (Broken pipe)
write(1, ">>>>>>>", 7) = -1 EPIPE (Broken pipe)
write(1, ">>>>>>>>", 8) = -1 EPIPE (Broken pipe)
write(1, ">>>>>>>>", 8) = -1 EPIPE (Broken pipe)
[...]
write(1, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"..., 4775) = -1 EPIPE (Broken pipe)
write(1, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"..., 4775) = -1 EPIPE (Broken pipe)
write(1, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"..., 4776) = -1 EPIPE (Broken pipe)
write(1, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"..., 4776) = -1 EPIPE (Broken pipe)

The cause is in ikarus.cafe.ss. The (read;read) consumes the first two
lines of the banner, then when (display-prompt 0) is attempted it raises
a broken pipe exception, the handler then attempts (flush-output-port
(console-output-port)) which raises a broken pipe exception, the next
handler invokes the reset continuation which loops back to attempting
another (display-prompt 0).

Revision history for this message
Derick Eddington (derick-eddington) wrote :

On Sun, 2008-07-27 at 03:30 -0700, Derick Eddington wrote:
> The cause is in ikarus.cafe.ss.

Do you think the handlers which do the reset behavior should only be
around the (eval-proc x)? That way any bizarre exceptions raised by
non- user code propagate to the base handler which just exits.

Revision history for this message
Derick Eddington (derick-eddington) wrote :

On Sun, 2008-07-27 at 04:07 -0700, Derick Eddington wrote:
> Do you think the handlers which do the reset behavior should only be
> around the (eval-proc x)?

No. Reader exceptions need to reset the REPL too.

Revision history for this message
Abdulaziz Ghuloum (aghuloum) wrote :

There are two other situations that make the cafe go berserk. Try:
  (close-output-port (current-output-port))
and
  (close-input-port (current-input-port))

I think that the cafe handler around "read" should only handle &lexical and &interrupted conditions and propagate the rest upwards while the handler around eval should trap all conditions. Need to try it out.

Revision history for this message
Derick Eddington (derick-eddington) wrote :
Download full text (8.0 KiB)

On Sun, 2008-07-27 at 16:39 +0000, Abdulaziz Ghuloum wrote:
> There are two other situations that make the cafe go berserk. Try:
> (close-output-port (current-output-port))
> and
> (close-input-port (current-input-port))

Hehe :)

> I think that the cafe handler around "read" should only handle &lexical
> and &interrupted conditions and propagate the rest upwards while the
> handler around eval should trap all conditions. Need to try it out.

I've tried this:

=== modified file 'scheme/ikarus.cafe.ss'
--- scheme/ikarus.cafe.ss 2008-07-13 03:05:45 +0000
+++ scheme/ikarus.cafe.ss 2008-07-28 02:34:31 +0000
@@ -53,48 +53,58 @@
             (display ">" (console-output-port))
             (display-prompt (fx+ i 1))))))

- (define my-read
- (lambda (k)
- (parameterize ([interrupt-handler
- (lambda ()
- (flush-output-port (console-output-port))
- (reset-input-port! (console-input-port))
- (newline (console-output-port))
- (k))])
- (read (console-input-port)))))
-
+ (define (print-ex ex)
+ (flush-output-port (console-output-port))
+ (display "Unhandled exception\n" (console-error-port))
+ (print-condition ex (console-error-port)))
+
+ (define (reset k)
+ (reset-input-port! (console-input-port))
+ (k))
+
   (define wait
     (lambda (eval-proc escape-k)
       (call/cc
         (lambda (k)
- (with-exception-handler
- (lambda (con)
- (reset-input-port! (console-input-port))
- (k (void)))
- (lambda ()
- (with-exception-handler
- (lambda (con)
- (flush-output-port (console-output-port))
- (display "Unhandled exception\n" (console-error-port))
- (print-condition con (console-error-port))
- (when (interrupted-condition? con)
- (raise-continuable con)))
- (lambda ()
- (display-prompt 0)
- (let ([x (my-read k)])
- (cond
- [(eof-object? x)
- (newline (console-output-port))
- (escape-k (void))]
- [else
- (call-with-values
- (lambda () (eval-proc x))
- (lambda v*
- (unless (andmap (lambda (v) (eq? v (void))) v*)
- (for-each
- (lambda (v)
- (pretty-print v (console-output-port)))
- v*))))]))))))))
+ (display-prompt 0)
+ (let ([x (with-exception-handler
+ (lambda (ex)
+ (cond [(lexical-violation? ex)
+ (print-ex ex)
+ (reset k)]
+ [(interrupted-condition? ex)
+ (flush-output-port (console-output-port))
+ (newline (console-output-port))
+ ...

Read more...

Revision history for this message
Abdulaziz Ghuloum (aghuloum) wrote :

Applied Derick's patch in revision 1569. Thanks to the both of you.

Changed in ikarus:
assignee: nobody → aghuloum
importance: Undecided → Low
status: New → Fix Committed
Changed in ikarus:
milestone: none → 0.0.4
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.