Hi all. This is my first post, and I am quite new to OCaml.
I am working on a small TCP client utility, using Async/Core.
The connection is opened using
Tcp.with_connection (Tcp.Where_to_connect.of_host_and_port { host = "localhost"; port = myPort })
I need to be able to accept keyboard input, as well as read input from the socket. I use the Deferred.any for this purpose.
Calling Reader.read reader buf on the socket results in `Eof, which is OK, but when the method (containing the Deferred.any code) is called recursively, I get an exception:
Ah, I believe what is happening is that you have two simultaneous calls to Reader.read, which is disallowed (see the module comment on Reader):
Each of the read functions returns a deferred that will become determined when the read completes. It is an error to have two simultaneous reads. That is, if you call a read function, you should not call another read function until the first one completes.
One suggestion would be to use the Pipe interface rather than directly using Reader. You can call Reader.lines or Reader.pipe and then use Pipe.read_choice to “try” to read from the socket (without actually consuming the data if the other input arrives first).
You can read from two different sources simultaneously. Rather, you cannot call Reader.read on the sameReader.t twice simultaneously.
What I think is happening is that on the first call to loop, you read from user input and also the socket. One of them completes, which causes loop to be called again. But it calls Reader.read again, on the same two Reader.t's. If the one which was waiting for input in the first call is still waiting for input, then you have called Reader.read twice simultaneously on the same Reader.t, which is disallowed. (There is also a correctness bug, because you will throw away the data that you consumed from the first call to Reader.read on the slower input).
Instead, you can do something like:
let readInput in1 in2 =
Deferred.choice
[ Pipe.read_choice_single_consumer_exn in1 [%here] |> Deferred.Choice.map ~f:Either.first
; Pipe.read_choice_single_consumer_exn in2 [%here] |> Deferred.Choice.map ~f:Either.second
]
>>= function
| First input_from_in1 -> do_something ()
| Second input_from_in2 -> do_something_else ()
You can get Pipe.Reader.t's from the Reader.t using Reader.pipe (call once and reuse the same Pipe.Reader.t).