I'm trying to use the OBus library with Lwt_react. This uses "functional reactive programming" for properties and signals.
The problem (as noted in the React documentation) is that OCaml may garbage collect your callback while you're still using it. There's a keep
function, which keeps the handler forever, but I don't want that. I do want to free it eventually, just not while I still need it.
So, I thought I'd attach the handler to a switch:
let keep ~switch handler =
Lwt_switch.add_hook (Some switch) (fun () ->
ignore handler;
Lwt.return ()
)
But my event handler gets garbage-collected anyway (which makes sense, since the code to turn off the switch is called when the signal arrives, so it's only the signal handler keeping the switch alive in the first place).
Here's a simplified (stand-alone) version of my code:
(* ocamlfind ocamlopt -package react,lwt,lwt.react,lwt.unix -linkpkg -o test test.ml *)
let finished_event, fire_finished = React.E.create ()
let setup () =
let switch = Lwt_switch.create () in
let finished, waker = Lwt.wait () in
let handler () = Lwt.wakeup waker () in
let dont_gc_me = Lwt_react.E.map handler finished_event in
ignore dont_gc_me; (* What goes here? *)
print_endline "Waiting for signal...";
Lwt.bind finished (fun () -> Lwt_switch.turn_off switch)
let () =
let finished = Lwt.protected (setup ()) in
Gc.full_major (); (* Force GC, to demonstrate problem *)
fire_finished (); (* Simulate send *)
Lwt_main.run finished;
print_endline "Done";
Without the Gc.full_major
line, this normally prints Done
. With it, it just hangs at Waiting for signal...
.
Edit: I've split setup
(the real code) from the test driver and added a Lwt.protected
wrapper to avoid masking the problem by accident of Lwt's cancellations.