ocamlocaml-lwt

How to send multiple TCP messages and continue when one has succeeded


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.


Solution

  • 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.