content: {-
    Copyright (c) Meta Platforms, Inc. and affiliates.
    All rights reserved.
  
    This source code is licensed under the BSD-style license found in the
    LICENSE file in the root directory of this source tree.
  -}
  
  {-# LANGUAGE CPP #-}
  module Facebook.Service
    ( runFacebookService
    , runFacebookService'
    , withBackgroundFacebookService
    , withBackgroundFacebookService'
    , runFacebookServiceDeferredAlive
    , runFacebookServiceDeferredAlive'
    , withBackgroundFacebookServiceDeferredAlive
    , withBackgroundFacebookServiceDeferredAlive'
    , waitForTerminateSignals
    ) where
  
  import Control.Concurrent.MVar
  import Control.Exception
  import Control.Monad (void)
  import Data.IORef
  import System.Posix.Signals
  
  import Fb303Core.Types
  import Facebook.Fb303
  import Thrift.Processor
  #ifdef FBTHRIFT
  import Thrift.Server.CppServer
  #else
  import Thrift.Server.HTTP
  #endif
  
  -- | Runs a facebook service on a CPPServer
  -- Handles fb303 statuses as well as shutting down on process signals
  runFacebookService
    :: Processor s
    => Fb303State -- ^ Mandatory fb303 state
    -> (forall r. s r -> IO r) -- ^ your handler
    -> ServerOptions -- ^ server options
    -> IO ()
  runFacebookService fb303 handler = do
    runFacebookService' fb303 handler nilPostProcess
  
  runFacebookService'
    :: Processor s
    => Fb303State -- ^ Mandatory fb303 state
    -> (forall r. s r -> IO r) -- ^ your handler
    -> (forall r. s r -> Either SomeException r -> Header)
    -> ServerOptions -- ^ server options
    -> IO ()
  runFacebookService' fb303State handler postProcess opts = do
    withBackgroundFacebookService' fb303State handler postProcess opts $
      const waitForTerminateSignals
  
  -- | Like 'runFacebookService' but does not set the fb303 status to
  -- @ALIVE@ when the server starts up. It is the caller's responsibility
  -- to set the status to alive via the 'Fb303State' when the server is
  -- ready to receive requests.
  runFacebookServiceDeferredAlive
    :: Processor s
    => Fb303State -- ^ Mandatory fb303 state
    -> (forall r. s r -> IO r) -- ^ your handler
    -> ServerOptions -- ^ server options
    -> IO ()
  runFacebookServiceDeferredAlive fb303 handler = do
    runFacebookServiceDeferredAlive' fb303 handler nilPostProcess
  
  runFacebookServiceDeferredAlive'
    :: Processor s
    => Fb303State -- ^ Mandatory fb303 state
    -> (forall r. s r -> IO r) -- ^ your handler
    -> (forall r. s r -> Either SomeException r -> Header)
    -> ServerOptions -- ^ server options
    -> IO ()
  runFacebookServiceDeferredAlive' fb303State handler postProcess opts = do
    withBackgroundFacebookServiceDeferredAlive'
      fb303State handler postProcess opts
      $ const waitForTerminateSignals
  
  -- | Block indefinitely until the process receives SIGTERM or SIGINT,
  -- and then return.
  waitForTerminateSignals :: IO ()
  waitForTerminateSignals = do
    -- To wait in Haskell-land while the server is taking requests,
    -- use an mvar that gets filled when the right signals are read
    mvar <- newEmptyMVar
    let sigHandler = void $ tryPutMVar mvar ()
    withSignalHandler sigTERM sigHandler $ \_ ->
      withSignalHandler sigINT sigHandler $ \_ ->
        -- Haskell will wait here until being instructed to stop
        takeMVar mvar
    where
      withSignalHandler sig h = bracket
        (installHandler sig (Catch h) Nothing)
        (\old -> installHandler sig old Nothing)
  
  -- | Runs a facebook service on a CPPServer
  -- Handles fb303 statuses and gracefully shuts down the server once the
  -- passed in wait action completes.
  withBackgroundFacebookService
    :: Processor s
    => Fb303State -- ^ Mandatory fb303 state
    -> (forall r. s r -> IO r) -- ^ your handler
    -> ServerOptions -- ^ server options
    -> (Server -> IO a) -- ^ wait action
    -> IO a
  withBackgroundFacebookService fb303 handler =
    withBackgroundFacebookService' fb303 handler nilPostProcess
  
  withBackgroundFacebookService'
    :: Processor s
    => Fb303State -- ^ Mandatory fb303 state
    -> (forall r. s r -> IO r) -- ^ your handler
    -> (forall r. s r -> Either SomeException r -> Header)
    -> ServerOptions -- ^ server options
    -> (Server -> IO a) -- ^ wait action
    -> IO a
  withBackgroundFacebookService' fb303 handler postProcess opts waitAction =
    withBackgroundFacebookService_ fb303 handler postProcess opts $ \server ->
      bracketFb303 fb303 Fb303_status_ALIVE Fb303_status_STOPPING $
        waitAction server
  
  -- | Like 'withBackgroundFacebookService', but does not set the fb303
  -- status to @ALIVE@ when the server starts up. It is the caller's
  -- responsibility to set the status to alive via the 'Fb303State'
  -- when the server is ready to receive requests.
  withBackgroundFacebookServiceDeferredAlive
    :: Processor s
    => Fb303State -- ^ Mandatory fb303 state
    -> (forall r. s r -> IO r) -- ^ your handler
    -> ServerOptions -- ^ server options
    -> (Server -> IO a) -- ^ wait action
    -> IO a
  withBackgroundFacebookServiceDeferredAlive fb303 handler =
    withBackgroundFacebookServiceDeferredAlive' fb303 handler nilPostProcess
  
  withBackgroundFacebookServiceDeferredAlive'
    :: Processor s
    => Fb303State -- ^ Mandatory fb303 state
    -> (forall r. s r -> IO r) -- ^ your handler
    -> (forall r. s r -> Either SomeException r -> Header)
    -> ServerOptions -- ^ server options
    -> (Server -> IO a) -- ^ wait action
    -> IO a
  withBackgroundFacebookServiceDeferredAlive' fb303 handler postP opts waitAction
    =
    withBackgroundFacebookService_ fb303 handler postP opts $ \server ->
      waitAction server
        `finally` writeIORef (fb303_status fb303) Fb303_status_STOPPING
  
  bracketFb303 :: Fb303State -> Fb303_status -> Fb303_status -> IO a -> IO a
  bracketFb303 Fb303State{..} a b =
    bracket_ (writeIORef fb303_status a) (writeIORef fb303_status b)
  
  withBackgroundFacebookService_
    :: Processor s
    => Fb303State
    -> (forall r. s r -> IO r)
    -> (forall r. s r -> Either SomeException r -> Header)
    -> ServerOptions
    -> (Server -> IO a)
    -> IO a
  withBackgroundFacebookService_ fb303 handler postProcess opts waitAction = do
    bracketFb303 fb303 Fb303_status_STARTING Fb303_status_STOPPED $ do
      withBackgroundServer' handler postProcess opts waitAction
  
  nilPostProcess :: a -> b -> [c]
  nilPostProcess _ _ = []
  