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.