Confused about moonpool cancellation

I’m evaluating moonpool and got confused by its cancellation behaviour; here’s a repro. I expected ~protect:false to allow the first failwith "BOOM" to cancel its parent and hence the entire tree, with the fibers raising on check_if_cancelled (maybe not the main one if it terminates fast), but instead all the fibers print their outputs and then the main fiber fails.

Moonpool_fib.main (fun _ ->
  Moonpool.Background_thread.with_ () (fun on ->
    (* Pretend this is some long-running read loop *)
    for i = 1 to 10 do
      Moonpool_fib.check_if_cancelled ();
      Moonpool_fib.spawn ~on ~protect:false (fun () ->
          Moonpool_fib.check_if_cancelled ();
          Format.printf "%d@." i;
          failwith "BOOM"
      )
    done
  )
)

I don’t know if this is by design and I’m misunderstanding moonpool’s cancellation semantics, or this is a bug and I should open an issue.

Edit: Thiking about it, the Background_thread might be redundant for this repro because main also is single-threaded, but I used it because it was present in my actual code for other reasons.

It seems you uncovered a bug resulting from the recent port to Picos :slight_smile: .

I have a potential fix. The bug was that each fiber created two computations, not one; we would cancel one in a timely fashion, but check_if_cancelled would check the other one. My version of the test did include a Moonpool_fib.yield() after the spawn, to give fibers a chance to run, but even then once the first fiber runs it should indeed cancel the other ones.

1 Like