I'm writing some networking code currently and I need to send out a large number of messages and then wait for a single response.
Given that I have a function that returns the input and output channels for a socket I have:
let resps = List.map uris ~f:(fun uri ->
let%lwt (ic,oc) = connect uri in
let%lwt () = Lwt_io.write_value oc msg in
Lwt_io.read_value ic
) in
Lwt.pick resps
My understanding of this is that pick should cancel any ongoing requests after it has a fulfilled promise in resps
. The issue is that if any of those connections fails/is refused, an exception is raised Unix.ECONNREFUSED
.
My question is what is the correct semantics to force Lwt.pick
to ignore the exceptions?
Options I've thought of so far are to catch the exception explicity in the requests:
let resps = List.map uris ~f:(fun uri ->
try
let%lwt (ic,oc) = connect uri in
let%lwt () = Lwt_io.write_value oc msg in
Lwt_io.read_value ic
with Unix_error (e,_,_) -> ...
) in
Lwt.pick resps
But I'm not sure under what conditions the Lwt.pick
will view those promises are rejected?
Update: I'm now handling the errors with cancellable, unfulfillable promises:
fst @@ Lwt.task ()
This feels hacky but seems to work so far.
Handling the exception explicitly is right. Lwt promises are rejected when you either reject them explicitly (using Lwt.fail
), or when an exception is caught by Lwt, in a callback that should have returned a promise (like the one you would pass to Lwt.bind
).
However, for handling exceptions in code that calls into Lwt, you have to use try%lwt
instead of the plain try
.