module Hasura.ShutdownLatch
  ( ShutdownLatch,
    newShutdownLatch,
    shutdownGracefully,
    waitForShutdown,
    shuttingDown,
  )
where

import Control.Concurrent.Extended qualified as C
import Hasura.Prelude

-- | A latch for the graceful shutdown of a server process.
newtype ShutdownLatch = ShutdownLatch {ShutdownLatch -> MVar ()
unShutdownLatch :: C.MVar ()}

newShutdownLatch :: IO ShutdownLatch
newShutdownLatch :: IO ShutdownLatch
newShutdownLatch = (MVar () -> ShutdownLatch) -> IO (MVar ()) -> IO ShutdownLatch
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap MVar () -> ShutdownLatch
ShutdownLatch IO (MVar ())
forall a. IO (MVar a)
C.newEmptyMVar

-- | Block the current thread, waiting on the latch.
waitForShutdown :: ShutdownLatch -> IO ()
waitForShutdown :: ShutdownLatch -> IO ()
waitForShutdown = MVar () -> IO ()
forall a. MVar a -> IO a
C.readMVar (MVar () -> IO ())
-> (ShutdownLatch -> MVar ()) -> ShutdownLatch -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShutdownLatch -> MVar ()
unShutdownLatch

-- | Initiate a graceful shutdown of the server associated with the provided
-- latch.
shutdownGracefully :: ShutdownLatch -> IO ()
shutdownGracefully :: ShutdownLatch -> IO ()
shutdownGracefully = IO Bool -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO Bool -> IO ())
-> (ShutdownLatch -> IO Bool) -> ShutdownLatch -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (MVar () -> () -> IO Bool) -> () -> MVar () -> IO Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip MVar () -> () -> IO Bool
forall a. MVar a -> a -> IO Bool
C.tryPutMVar () (MVar () -> IO Bool)
-> (ShutdownLatch -> MVar ()) -> ShutdownLatch -> IO Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShutdownLatch -> MVar ()
unShutdownLatch

-- | Returns True if the latch is set for shutdown and vice-versa
shuttingDown :: ShutdownLatch -> IO Bool
shuttingDown :: ShutdownLatch -> IO Bool
shuttingDown ShutdownLatch
latch = Bool -> Bool
not (Bool -> Bool) -> IO Bool -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> MVar () -> IO Bool
forall a. MVar a -> IO Bool
C.isEmptyMVar (ShutdownLatch -> MVar ()
unShutdownLatch ShutdownLatch
latch)