Skip to content

Instantly share code, notes, and snippets.

@aantron
Created July 23, 2017 14:48
Show Gist options
  • Save aantron/4db12b8e5ca92f57f7a223e594c833cc to your computer and use it in GitHub Desktop.
Save aantron/4db12b8e5ca92f57f7a223e594c833cc to your computer and use it in GitHub Desktop.
Lwt.pick/Lwt.npick callback ordering

Compile and run the program below with

ocamlfind opt -linkpkg -package lwt.unix foo.ml && ./a.out

As written with Lwt.pick, p2 is canceled first, its callback (registered by Lwt.catch) runs first, and the output is

p2's callback sees p3 pending
p3's callback sees p2 failed

If changed to have Lwt.npick from the PR, p3 is completed first, its callback (registered by Lwt.bind) runs first, and the output is

p3's callback sees p2 pending
p2's callback sees p3 resolved

Ideally, changing pick to npick shouldn't produce this difference in callback execution order and seen states, as it makes refactoring relatively more dangerous.

let state_to_string state =
match state with
| Lwt.Return _ -> "resolved"
| Lwt.Fail _ -> "failed"
| Lwt.Sleep -> "pending"
let () =
let p1, r1 = Lwt.task () in
let p2, _ = Lwt.task () in
let p3 = Lwt.pick [p1; p2] in
(* Observe the state of p3 from a callback attached to p2: *)
let p2' =
Lwt.catch
(fun () -> p2)
(fun _exn ->
Lwt_io.printf
"p2's callback sees p3 %s\n"
(state_to_string (Lwt.state p3)))
in
(* Observe the state of p2 from a callback attached to p3: *)
let p3' =
Lwt.bind
p3
(fun _ ->
Lwt_io.printf
"p3's callback sees p2 %s\n"
(state_to_string (Lwt.state p2)))
in
(* Complete p1. This makes pick/npick resolve p3, and cancel p2. *)
Lwt.wakeup_later r1 ();
Lwt_main.run (Lwt.join [p2'; p3'])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment