    {-# LINE 1 "libraries/base/./System/Event/KQueue.hsc" #-}
    {-# LANGUAGE ForeignFunctionInterface, GeneralizedNewtypeDeriving,
    {-# LINE 2 "libraries/base/./System/Event/KQueue.hsc" #-}
        NoImplicitPrelude, RecordWildCards #-}

    module System.Event.KQueue
        (
          new
        , available
        ) where

    import qualified System.Event.Internal as E

    {-# LINE 13 "libraries/base/./System/Event/KQueue.hsc" #-}

    {-# LINE 23 "libraries/base/./System/Event/KQueue.hsc" #-}

    import Control.Concurrent.MVar (MVar, newMVar, swapMVar, withMVar)
    import Control.Monad (when, unless)
    import Data.Bits (Bits(..))
    import Data.Word (Word16, Word32)
    import Foreign.C.Error (throwErrnoIfMinus1)
    import Foreign.C.Types (CInt, CLong, CTime)
    import Foreign.Marshal.Alloc (alloca)
    import Foreign.Ptr (Ptr, nullPtr)
    import Foreign.Storable (Storable(..))
    import GHC.Base
    import GHC.Enum (toEnum)
    import GHC.Err (undefined)
    import GHC.Num (Num(..))
    import GHC.Real (ceiling, floor, fromIntegral)
    import GHC.Show (Show(show))
    import System.Event.Internal (Timeout(..))
    import System.Posix.Internals (c_close)
    import System.Posix.Types (Fd(..))
    import qualified System.Event.Array as A

    {-# LINE 49 "libraries/base/./System/Event/KQueue.hsc" #-}
    import Foreign.C.Types (CIntPtr, CUIntPtr)

    {-# LINE 51 "libraries/base/./System/Event/KQueue.hsc" #-}

    {-# LINE 53 "libraries/base/./System/Event/KQueue.hsc" #-}

    {-# LINE 54 "libraries/base/./System/Event/KQueue.hsc" #-}

    {-# LINE 55 "libraries/base/./System/Event/KQueue.hsc" #-}

    -- Handle brokenness on some BSD variants, notably OS X up to at least
    -- 10.6.  If NOTE_EOF isn't available, we have no way to receive a
    -- notification from the kernel when we reach EOF on a plain file.

    {-# LINE 62 "libraries/base/./System/Event/KQueue.hsc" #-}

    available :: Bool
    available = True
    {-# INLINE available #-}

    ------------------------------------------------------------------------
    -- Exported interface

    data EventQueue = EventQueue {
          eqFd       :: {-# UNPACK #-} !QueueFd
        , eqChanges  :: {-# UNPACK #-} !(MVar (A.Array Event))
        , eqEvents   :: {-# UNPACK #-} !(A.Array Event)
        }

    new :: IO E.Backend
    new = do
      qfd <- kqueue
      changesArr <- A.empty
      changes <- newMVar changesArr
      events <- A.new 64
      let !be = E.backend poll modifyFd delete (EventQueue qfd changes events)
      return be

    delete :: EventQueue -> IO ()
    delete q = do
      _ <- c_close . fromQueueFd . eqFd $ q
      return ()

    modifyFd :: EventQueue -> Fd -> E.Event -> E.Event -> IO ()
    modifyFd q fd oevt nevt = withMVar (eqChanges q) $ \ch -> do
      let addChange filt flag = A.snoc ch $ event fd filt flag noteEOF
      when (oevt `E.eventIs` E.evtRead)  $ addChange filterRead flagDelete
      when (oevt `E.eventIs` E.evtWrite) $ addChange filterWrite flagDelete
      when (nevt `E.eventIs` E.evtRead)  $ addChange filterRead flagAdd
      when (nevt `E.eventIs` E.evtWrite) $ addChange filterWrite flagAdd

    poll :: EventQueue
         -> Timeout
         -> (Fd -> E.Event -> IO ())
         -> IO ()
    poll EventQueue{..} tout f = do
        changesArr <- A.empty
        changes <- swapMVar eqChanges changesArr
        changesLen <- A.length changes
        len <- A.length eqEvents
        when (changesLen > len) $ A.ensureCapacity eqEvents (2 * changesLen)
        n <- A.useAsPtr changes $ \changesPtr chLen ->
               A.unsafeLoad eqEvents $ \evPtr evCap ->
                 withTimeSpec (fromTimeout tout) $
                   kevent eqFd changesPtr chLen evPtr evCap

        unless (n == 0) $ do
            cap <- A.capacity eqEvents
            when (n == cap) $ A.ensureCapacity eqEvents (2 * cap)
            A.forM_ eqEvents $ \e -> f (fromIntegral (ident e)) (toEvent (filter e))

    ------------------------------------------------------------------------
    -- FFI binding

    newtype QueueFd = QueueFd {
          fromQueueFd :: CInt
        } deriving (Eq, Show)

    {-# LINE 167 "libraries/base/./System/Event/KQueue.hsc" #-}
    data Event = KEvent {
          ident  :: {-# UNPACK #-} !CUIntPtr
        , filter :: {-# UNPACK #-} !Filter
        , flags  :: {-# UNPACK #-} !Flag
        , fflags :: {-# UNPACK #-} !FFlag
        , data_  :: {-# UNPACK #-} !CIntPtr
        , udata  :: {-# UNPACK #-} !(Ptr ())
        } deriving Show

    event :: Fd -> Filter -> Flag -> FFlag -> Event
    event fd filt flag fflag = KEvent (fromIntegral fd) filt flag fflag 0 nullPtr

    instance Storable Event where
        sizeOf _ = (32)
    {-# LINE 181 "libraries/base/./System/Event/KQueue.hsc" #-}
        alignment _ = alignment (undefined :: CInt)

        peek ptr = do
            ident'  <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr
    {-# LINE 185 "libraries/base/./System/Event/KQueue.hsc" #-}
            filter' <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr
    {-# LINE 186 "libraries/base/./System/Event/KQueue.hsc" #-}
            flags'  <- (\hsc_ptr -> peekByteOff hsc_ptr 10) ptr
    {-# LINE 187 "libraries/base/./System/Event/KQueue.hsc" #-}
            fflags' <- (\hsc_ptr -> peekByteOff hsc_ptr 12) ptr
    {-# LINE 188 "libraries/base/./System/Event/KQueue.hsc" #-}
            data'   <- (\hsc_ptr -> peekByteOff hsc_ptr 16) ptr
    {-# LINE 189 "libraries/base/./System/Event/KQueue.hsc" #-}
            udata'  <- (\hsc_ptr -> peekByteOff hsc_ptr 24) ptr
    {-# LINE 190 "libraries/base/./System/Event/KQueue.hsc" #-}
            let !ev = KEvent ident' (Filter filter') (Flag flags') fflags' data'
                             udata'
            return ev

        poke ptr ev = do
            (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr (ident ev)
    {-# LINE 196 "libraries/base/./System/Event/KQueue.hsc" #-}
            (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr (filter ev)
    {-# LINE 197 "libraries/base/./System/Event/KQueue.hsc" #-}
            (\hsc_ptr -> pokeByteOff hsc_ptr 10) ptr (flags ev)
    {-# LINE 198 "libraries/base/./System/Event/KQueue.hsc" #-}
            (\hsc_ptr -> pokeByteOff hsc_ptr 12) ptr (fflags ev)
    {-# LINE 199 "libraries/base/./System/Event/KQueue.hsc" #-}
            (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr (data_ ev)
    {-# LINE 200 "libraries/base/./System/Event/KQueue.hsc" #-}
            (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr (udata ev)
    {-# LINE 201 "libraries/base/./System/Event/KQueue.hsc" #-}

    {-# LINE 202 "libraries/base/./System/Event/KQueue.hsc" #-}

    newtype FFlag = FFlag Word32
        deriving (Eq, Show, Storable)

    noteEOF  :: FFlag
    noteEOF  = FFlag 0

    {-# LINE 209 "libraries/base/./System/Event/KQueue.hsc" #-}

    newtype Flag = Flag Word16
        deriving (Eq, Show, Storable)

    flagAdd      :: Flag
    flagAdd      = Flag 1
    flagDelete   :: Flag
    flagDelete   = Flag 2

    {-# LINE 217 "libraries/base/./System/Event/KQueue.hsc" #-}

    newtype Filter = Filter Word16
        deriving (Bits, Eq, Num, Show, Storable)

    filterRead    :: Filter
    filterRead    = Filter (-1)
    filterWrite   :: Filter
    filterWrite   = Filter (-2)

    {-# LINE 225 "libraries/base/./System/Event/KQueue.hsc" #-}

    data TimeSpec = TimeSpec {
          tv_sec  :: {-# UNPACK #-} !CTime
        , tv_nsec :: {-# UNPACK #-} !CLong
        }

    instance Storable TimeSpec where
        sizeOf _ = (16)
    {-# LINE 233 "libraries/base/./System/Event/KQueue.hsc" #-}
        alignment _ = alignment (undefined :: CInt)

        peek ptr = do
            tv_sec'  <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr
    {-# LINE 237 "libraries/base/./System/Event/KQueue.hsc" #-}
            tv_nsec' <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr
    {-# LINE 238 "libraries/base/./System/Event/KQueue.hsc" #-}
            let !ts = TimeSpec tv_sec' tv_nsec'
            return ts

        poke ptr ts = do
            (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr (tv_sec ts)
    {-# LINE 243 "libraries/base/./System/Event/KQueue.hsc" #-}
            (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr (tv_nsec ts)
    {-# LINE 244 "libraries/base/./System/Event/KQueue.hsc" #-}

    kqueue :: IO QueueFd
    kqueue = QueueFd `fmap` throwErrnoIfMinus1 "kqueue" c_kqueue

    -- TODO: We cannot retry on EINTR as the timeout would be wrong.
    -- Perhaps we should just return without calling any callbacks.
    kevent :: QueueFd -> Ptr Event -> Int -> Ptr Event -> Int -> Ptr TimeSpec
           -> IO Int
    kevent k chs chlen evs evlen ts
        = fmap fromIntegral $ E.throwErrnoIfMinus1NoRetry "kevent" $

    {-# LINE 257 "libraries/base/./System/Event/KQueue.hsc" #-}
          c_kevent k chs (fromIntegral chlen) evs (fromIntegral evlen) ts

    {-# LINE 259 "libraries/base/./System/Event/KQueue.hsc" #-}

    withTimeSpec :: TimeSpec -> (Ptr TimeSpec -> IO a) -> IO a
    withTimeSpec ts f =
        if tv_sec ts < 0 then
            f nullPtr
          else
            alloca $ \ptr -> poke ptr ts >> f ptr

    fromTimeout :: Timeout -> TimeSpec
    fromTimeout Forever     = TimeSpec (-1) (-1)
    fromTimeout (Timeout s) = TimeSpec (toEnum sec) (toEnum nanosec)
      where
        sec :: Int
        sec     = floor s

        nanosec :: Int
        nanosec = ceiling $ (s - fromIntegral sec) * 1000000000

    toEvent :: Filter -> E.Event
    toEvent (Filter f)
        | f == (-1) = E.evtRead
    {-# LINE 280 "libraries/base/./System/Event/KQueue.hsc" #-}
        | f == (-2) = E.evtWrite
    {-# LINE 281 "libraries/base/./System/Event/KQueue.hsc" #-}
        | otherwise = error $ "toEvent: unknown filter " ++ show f

    foreign import ccall unsafe "kqueue"
        c_kqueue :: IO CInt

    {-# LINE 291 "libraries/base/./System/Event/KQueue.hsc" #-}
    foreign import ccall safe "kevent"
        c_kevent :: QueueFd -> Ptr Event -> CInt -> Ptr Event -> CInt
                 -> Ptr TimeSpec -> IO CInt

    {-# LINE 297 "libraries/base/./System/Event/KQueue.hsc" #-}

    {-# LINE 299 "libraries/base/./System/Event/KQueue.hsc" #-}