When dealing with fibers you must be very careful not to cancel before joining, otherwise the join will leak.
Look at the following example:
for {
fiber <- IO.sleep(2.seconds).start
_ <- fiber.join.guarantee(putStrLn("canceled")).start
_ <- IO.sleep(1.second) >> fiber.cancel
} yield ()
fiber.join
will never terminate. You must be careful not to wait on a canceled fiber.
The easiest solution is building a synchronize mechanism on top. An alternative is to build a higher
level abstraction to avoid dealing with fiber like this await.
Cancelling a join action on a cancelled fiber works as expected:
for {
fiber <- IO.never.start
_ <- IO.race(fiber.join.guarantee(putStrLn("canceled")), IO.sleep(1.second) >> fiber.cancel)
} yield ()
Oleg Pyzhcov has this article about fibers safety that I would recommend reading.