BufferedReader - Block at end of stream

631 views Asked by At

I'm writing some functions that use clojure.async to abstract away from reading/writing to sockets. My intent is that values can be put into a channel to have them written, and popped from a channel to be read. This way, the user doesn't need to worry about Readers/Writers.

The code below reads from a socket in a loop, forwarding anything it reads into a channel. The channel is returned so it can be read from. My problem is, when the end of the stream is reached, instead of blocking, it sits there spinning until something can be read. If I have a few of these processes going, it makes a noticeable performance issue on my computer.

The easiest way I could see to fix this is somehow have BufferedReader's readLine block on EOF instead of returning nil. From what I can see though, this isn't possible. That doesn't surprise me though, since blocking on EOF for most streams would be very odd. For socket streams though, EOF doesn't seem to have a specific meaning, since messages can still be received even once the EOF is reached.

Is there a way to prevent the loop from spinning while waiting for input once the EOF has been reached?

(ns chat.so-example
  (:require [clojure.core.async :as a :refer [chan go >!]])
  (:import [java.net Socket SocketException]
           [java.io BufferedReader InputStreamReader InputStream]))

(defn valid-message? [msg]
  (and (some? msg)
       (not (empty? msg))))

(defn new-input-chan [^Socket sock]
  (let [^InputStream in (.getInputStream sock)
        buff-reader     (BufferedReader. (InputStreamReader. in))
        in-chan         (chan)]
    (go
      (try
        (while (.isConnected sock) ; This spins when EOF is reached
          (let [line (.readLine buff-reader)]
            (when (valid-message? line)
              (>! in-chan line))))
        (catch SocketException se
          (#_"Handle exception"))
        (finally
          (a/close! in-chan))))
    in-chan))
2

There are 2 answers

5
amalloy On

The only thing spinning here is you. None of the methods in the classes you are using spin. Read the documentation for isConnected and isClosed or isInputShutdown and a solution should become clear. It also would have helped you to read the description of readLine, which is very clear about what it returns when there is no more input to read.

0
Bloemelau On

The java.io.BufferedReader.ready() method can check whether there's more to be read, even after EOF. Also, Sockets can be coerced into BufferedReader simply using (clojure.java.io/reader socket). Same for writer.

I'm not very familiar with core.async though, so no idea if that helps.