<< Back to previous view

[NREPL-39] Using functions reading from *in* causes "java.io.IOException: Write end dead" Created: 03/Feb/13  Updated: 07/Apr/13  Resolved: 21/Mar/13

Status: Closed
Project: tools.nrepl
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Adrian Bendel Assignee: Colin Jones
Resolution: Completed Votes: 0
Labels: None

Attachments: Text File 0001-Use-a-queue-for-stdin-to-avoid-PipedReader-Writer.patch    

 Description   

When calling (read-line) from lein repl or ccw repl, waiting around four minutes (on my machine),
evaluating any form causes the following exception:

ERROR: Unhandled REPL handler exception processing message {:code (+ 1 1), :id 08e30b70-ab0f-4a47-8937-3c040ce06f4f, :ns user, :op eval, :session 916200cc-e476-4ac4-98f6-76057a9020be}
java.io.IOException: Write end dead
	at java.io.PipedReader.ready(Unknown Source)
	at clojure.tools.nrepl.middleware.session.proxy$java.io.PipedReader$0.ready(Unknown Source)
	at java.io.BufferedReader.ready(Unknown Source)
	at java.io.FilterReader.ready(Unknown Source)
	at java.io.PushbackReader.ready(Unknown Source)
	at clojure.tools.nrepl.middleware.session$add_stdin$fn__1103.invoke(session.clj:197)
	at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__840.invoke(middleware.clj:17)
	at clojure.tools.nrepl.middleware.session$session$fn__1093.invoke(session.clj:165)
	at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__840.invoke(middleware.clj:17)
	at clojure.tools.nrepl.server$handle_STAR_.invoke(server.clj:18)
	at clojure.tools.nrepl.server$handle$fn__1132.invoke(server.clj:27)
	at clojure.core$binding_conveyor_fn$fn__4130.invoke(core.clj:1836)
	at clojure.lang.AFn.call(AFn.java:18)
	at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
	at java.util.concurrent.FutureTask.run(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)

While googling a bit on this and without concrete knowledge of the implementation in nrepl,
it seems like closing a thread that wrote to PipedWriter, before closing the writer or writing to it from another thread,
causes PipedReader#ready to throw that exception.



 Comments   
Comment by Colin Jones [ 06/Mar/13 5:46 PM ]

I found this related article: http://techtavern.wordpress.com/2008/07/16/whats-this-ioexception-write-end-dead/

So it seems like we may need something a little more robust. I'm thinking just a LinkedBlockingQueue that gets written to on input, with a Reader proxy over the top to hook up to Clojure's LineNumberingPushbackReader. I've started fiddling with that idea, passing most of the tests already, and I think this is the right path to go down in general. Yell at me if you see a problem with this plan.

Comment by Chas Emerick [ 07/Mar/13 4:50 AM ]

I had no idea that PipedReader & co. actually track the threads that touch them. That makes them entirely inappropriate for the *in* case.

+1 to your plan, thanks for chasing this!

Comment by Colin Jones [ 08/Mar/13 3:28 PM ]

OK, this works for me against REPLy.

I'd have preferred to avoid the dynamic binding (skipping-eol), but I haven't been able to think of another way to accomplish what it does. The problem is that we don't want to send another :need-input message just to clear newlines, but we don't have any visibility into what's in the LineNumberingPushbackReader's buffer (neither the full deal, nor the pushback). My initial thought was to replace the LineNumberingPushbackReader directly, with a custom thing, but I think the assumptions around that class in `read` and `read-line` are too concretely baked in to allow it (especially before 1.5, when the buffer size became configurable).

And there's no room for adding bits to the Reader interface, so in order to effect a behavior difference in the Reader, depending on what the high-level state is, I don't know that there are any options aside from dynamic binding or an atom (and of the two, a var seemed more robust, no worries about finally & friends).

Comment by Colin Jones [ 21/Mar/13 11:39 PM ]

Pushed to master: https://github.com/clojure/tools.nrepl/commit/3cd3d0e8b2091fc70fde1587d3f0a995cbf8b283

Generated at Sat Apr 19 12:29:26 CDT 2014 using JIRA 4.4#649-r158309.