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.
  -}
  
  module Util.Fd
    ( withFdEventNotification
    ) where
  
  import Control.Exception
  import Control.Monad
  import GHC.Event hiding (closeFd)
  import Foreign.C
  import System.Posix.IO
  import System.Posix.Types
  
  -- | Uses a file descriptor and GHC's event manager to run a callback
  -- once the file descriptor is written to. Is less expensive than a new FFI
  -- call from C++ back into Haskell.
  withFdEventNotification
    :: Exception e
    => e -- ^ Exception to throw when unable to get the event manager
    -> IO () -- ^ The callback to run on fd write
    -> Lifetime -- ^ OneShot or MultiShot
    -> (Fd -> IO a) -- ^ Action to run with the file descriptor to write to
    -> IO a
  withFdEventNotification err callback lifetime action = do
    evm <- maybe (throw err) return =<< getSystemEventManager
    withEventFd $ \fd ->
      bracket (registerFd evm cb fd evtRead lifetime) (unregisterFd evm) $
        const $ action fd
    where
      cb _ _ = callback
  
  withEventFd :: (Fd -> IO a) -> IO a
  withEventFd = bracket
    (do fd <- c_eventfd 0 0
        when (fd == -1) $ throwErrno "eventFd"
        return $ Fd fd)
    closeFd
  
  foreign import ccall unsafe "eventfd"
    c_eventfd :: CInt -> CInt -> IO CInt
  