multithreadingocamllwt

Canceling a Lwt. thread


I can't for the life of me find a way to handle the canceling of an lwt thread.

Here is what I have, simplified.

#require "lwt.unix, lwt.ppx"
open Lwt.Infix

let program =
  let counter = ref 1 in
  let can_cancel = fst (Lwt.task ()) in

  Lwt.async_exception_hook := (fun _ ->
    prerr_endline "some exception");

  Lwt.on_cancel can_cancel (fun () ->
            Lwt_io.printl "correct ending" |> Lwt.ignore_result);

  Lwt.async begin fun () ->
    let rec forever () =

      try%lwt
        Lwt_io.printl "Hello World" >>= fun () ->

        if !counter = 3 then Lwt.cancel can_cancel
        else counter := !counter + 1;
        Lwt_unix.sleep 0.5 >>= forever
      with
        Lwt.Canceled -> Lwt_io.printl "canceled inside function"
    in

    Lwt.catch forever begin function
      | Lwt.Canceled -> Lwt_io.printl "Cancled exception happened"
      | _ -> Lwt.return ()
    end
  end;
  can_cancel

let () =
  Lwt_main.run program

You can see my several attempts to catch the Canceled exception, none of which work. My output is

utop cancelable.ml                                                     ⏎
Hello World
Hello World
Hello World
correct ending
Exception: Lwt.Canceled.

In the grander scheme of things, I have a unit Lwt.t list ref, which were created with Lwt.task, and then I plan on doing List.iter Lwt.cancel on the list, then replacing it with new threads of type unit Lwt.t


Solution

  • I was putting the try/with at the wrong level.

    This code shows the right place to put the try/with for the Lwt.Canceled exception

    #require "lwt.unix, lwt.ppx"
    open Lwt.Infix
    
    let program =
      let counter = ref 1 in
      let can_cancel = fst (Lwt.task ()) in
    
      Lwt.async begin fun () ->
        let rec forever () =
            Lwt_io.printl "Hello World" >>= fun () ->
            if !counter = 3 then Lwt.cancel can_cancel
            else counter := !counter + 1;
            Lwt_unix.sleep 0.5 >>= forever
        in
        forever ()
      end;
      can_cancel
    
    let () =
      try
        Lwt_main.run program
      with
        Lwt.Canceled -> Lwt_io.printl "ended" |> Lwt.ignore_result