2

Is there a way to shutdown the cats effect runtime from within an application, similar to System.exit(0)?

There is a shutdown method on IORuntime but it is not documented and it behaves weirdly when I call it on IORuntime.global: it does not shut the application down, it just seems to hang it. Also accessing runtime on my IOApp just throws a NullPointerException so I cannot use that. What I want to do is: from within my application a want to make the complete runtime shutdown cleanly, i.e. cancelling the main fiber and running all finalisers. I know I can implement such a shutdown-hook myself, but I was wondering if that is already supported somehow by cats effect, which is what the existence of these methods suggest.

  • 1
    I guess the cannonical answer would be something like: build your app with Resource and Fibers, at some place inside Resources put something which awaits Signal before exiting Resource .use. Then closing resources and exiting `main` would handle shutdown properly - when you need to exit, you run this Signal. Basically, you have to design your whole architecture to handle shutdown gracefully, with no global hacks. – Mateusz Kubuszok Aug 08 '23 at 11:58
  • Yes, the right way to cleanly shutdown a **CE** app, is using some form of kill switch. You may ask for more details in the **Discord** server: https://discord.gg/rtzPcX9p – Luis Miguel Mejía Suárez Aug 08 '23 at 13:15
  • Ok i get that, I know how to implement that, I just wanted to know if there is a global kill switch, totally fine if there isn't. I still wonder what the usecase `shutdown` function on IORuntime is supposed to be... – Georg Prohaska Aug 08 '23 at 17:57
  • From what I see `IORuntime` is not supposed to be called by you, it's something that runtime passes around for its own convenience, that's why it's not documented. – Mateusz Kubuszok Aug 09 '23 at 07:51

1 Answers1

1

You can do something like (pseudocode)

object MyApp extends IOApp.Simple {
  def run =
    Deferred[IO].flatMap { shutdown =>
      val app = buildMyAppResource(shutdown)
      IO.race(shutdown.get, app.useForever)
    }
}

Inside your app code, it can call shutdown.complete(()). The IO.race will run both fibers until one completes, then cancel the other. The useForever means your app won't close on its own, and the shutdown.get will wait until it's completed. So when your app calls shutdown.complete, the app resource will be interrupted and cancelled, letting the program exit after doing a clean resource release

Daenyth
  • 35,856
  • 13
  • 85
  • 124