Best practice on mixing Async and Lwt?

The safest way to do this is wrap Lwt code within Async, and to invoke the Lwt_main runtime thread via Async_unix.In_thread

Reporting back on how it went.

So far so good. I originally just knocked out a function that constructed the Lwt deferreds and ran Lwt_main.run inside of the Async.In_thread.run to get things going, and it has worked out well enough that I haven’t had to touch it.

let read_usb t len =
  In_thread.run (fun () ->
    Lwt_main.run
      begin
        let open Lwt.Infix in
        let s = String.make len (Char.of_int_exn 0x0) in
        USB.interrupt_recv ~handle:t.device_handle
          ~endpoint:endpoint_address s 0 len
        >>= fun transfer ->
        if Int.(<>) transfer len then
          failwithf "read_usb: failed to read %d bytes: %d" len transfer ();
        Lwt.return (Bytes.of_string s)
      end)

I’m a little squeamish about starting and finishing Lwt_main.run on every call. No idea how much overhead that actually involves, but, again, seems to work so I haven’t investigated further.

Down the line, it doesn’t seem like this will go well if I need to talk to two USB devices at the same time, because we could end up with two different threads launching Lwt_main.run, and I perhaps they will stomp on each other. It is possible to create a dedicated helper thread for In_thread.run to force serialization there, but that’s not a much better solution.

If I end up in such a place I imagine I’ll rewrite the underlying ocaml usb bindings to be more Async friendly but this seems good enough for now.