[MLton] Bug: TextPrimIO does not distinguish between EOF and no data

Vesa Karvonen vesa.a.j.k at gmail.com
Wed Dec 12 13:49:31 PST 2007


On Dec 11, 2007 2:17 PM, Ville Laurikari <ville at laurikari.net> wrote:
> I noticed bad behavior with TextIO.canInput.  If the input stream has
> no data to read currently but is not yet at EOF, canInput returns
> "SOME 0" instead of NONE.  Also, the eof condition is set for the
> stream and all further read attempts fail as if the stream was at EOF.
>
> Attached is a program which demonstrates the problem.
>
>   $ mlton bug.sml
>   $ (echo foo; sleep 1; echo bar; sleep 1) | ./bug
>   foo
>   EOF
>
> Expected output would probably be more like this:
>   foo
>   Would block.
>   bar
>   Would block.
>   EOF

The problem seems to be that the canInput implementation in
basis-library/io/imperative-io.fun does not distinguish between
readArrNB returning NONE (means: eof not seen, any read will block)
and SOME 0 (means: eof seen and at eof).

Below is a patch that seems to fix the problem.  With the patch, I get
the following output from the program:

$ (echo foo; sleep 1; echo bar; sleep 1) | ./bug
Would block.
foo
Would block.
bar
Would block.
EOF

-Vesa Karvonen

Index: basis-library/io/imperative-io.fun
===================================================================
--- basis-library/io/imperative-io.fun	(revision 6257)
+++ basis-library/io/imperative-io.fun	(working copy)
@@ -541,23 +541,27 @@
                    (* 0 = !first *)
                    fun loop read =
                       if read = size
-                         then read
+                         then {read = read, eos = false}
                       else
                          let
                             val slice = AS.slice (buf, read, NONE)
                             val i = readArrNB slice
                          in
                             case i of
-                               NONE => read
+                               NONE => {read = read, eos = false}
                              | SOME i =>
-                                  if 0 = i then read else loop (read + i)
+                                  if 0 = i
+                                     then {read = read, eos = true}
+                                  else loop (read + i)
                          end
-                   val read = loop read
+                   val {read, eos} = loop read
                    val _ = last := read
                 in
-                   SOME (if read > 0
-                            then Int.min (n, read)
-                         else (state := Open {eos = true}; 0))
+                   if read > 0
+                      then SOME (Int.min (n, read))
+                   else if eos
+                      then (state := Open {eos = true}; SOME 0)
+                   else NONE
                 end)
        | Stream s => SIO.canInput (s, n)



More information about the MLton mailing list