2017年3月24日金曜日

A Glitch of Lwt.cancel

Lwt.cancel cannot cancel the thread in which itself is running in lwt.2.7.0.  The following code demostrates what I mean:


(* ocamlfind ocamlopt -linkpkg -package lwt,lwt.unix x.ml *)

open Lwt

module Test1 = struct
    let self = ref None
    
    let from_some = function
      | Some x -> x
      | None -> assert false
    
    let s =
      pause () >>= fun () ->
      cancel (from_some !self);
      prerr_endline "cancel called";
      pause () >>= fun () ->
      prerr_endline "direct cancel cannot cancel itself";
      return ()
    
    let () =
      self := Some s;
      prerr_endline "set";
      Lwt_main.run @@ s
end

module Test2 = struct
    let self = ref None
    
    let from_some = function
      | Some x -> x
      | None -> assert false
    
    let s =
      pause () >>= fun () ->
      async (fun () ->
        pause () >>= fun () ->
        cancel (from_some !self); (* should give an exception Canceled *)
        prerr_endline "cancel called";
        return ());
      pause () >>= fun () ->
      prerr_endline "not dead";
      return ()
    
    let () =
      self := Some s;
      prerr_endline "set";
      Lwt_main.run @@ s
end

TEST1 cannot cancel itself: cancel has no effect.  TEST2, however, can.  Only the difference is the existence of async and pause:

    async (fun () -> pause () >>= fun () -> cancel <self>)

I do not fully understand why this difference we have, but the key seems to be call Lwt.cancel from another thread.