{-# LANGUAGE EmptyDataDecls #-} {-# LANGUAGE ForeignFunctionInterface #-} -- | @Network.Libev@ is a low-level binding to the libev library -- (). The @libev@ documentation is available here: -- . module Network.Libev ( -- * Event loops -- | see EvLoopPtr , evDefaultLoop , evLoopNew , evLoop , evUnloop , evLoopDestroy -- ** Flags for 'evDefaultLoop' , evRecommendedBackends , evflag_auto , evflag_noenv , evbackend_select , evbackend_poll , evbackend_epoll , evbackend_kqueue , evbackend_devpoll , evbackend_port -- ** Locking for event loops , MutexCallback , setupLockingForLoop , freeMutexCallback -- ** Event flags , CEventType , CEvFlagType , ev_read , ev_write -- * @ev\_io@ -- | See libev docs: , EvIoPtr , IoCallback , mkEvIo , freeEvIo , mkIoCallback , freeIoCallback , evIoInit , evIoStart , evIoStop -- * @ev\_timer@ -- | See libev docs: , EvTimerPtr , TimerCallback , mkEvTimer , freeEvTimer , mkTimerCallback , freeTimerCallback , evTimerInit , evTimerStart , evTimerStop , evTimerAgain , evTimerRemaining -- * @ev\_async@@ -- | See libev docs: , EvAsyncPtr , AsyncCallback , mkEvAsync , freeEvAsync , evAsyncInit , evAsyncSend , evAsyncStart , evAsyncStop , mkAsyncCallback , freeAsyncCallback -- * Time functions , EvTimestamp , evNow , evTime -- * C utility functions , c_accept , c_close , c_read , c_write , c_setnonblocking ) where import Control.Concurrent.MVar import Prelude hiding (repeat) import Foreign import Foreign.C #include -- | 'CEventType' is a bitfield used to flag whether a file descriptor is -- readable, writable, or both. Valid values are 'ev_read' and -- 'ev_write'. TODO: deprecate and replace by a datatype type CEventType = CInt -- | 'CEvFlagType' is a bitfield used to pass flags into -- 'evDefaultLoop'. Values ('evflag_auto', 'evflag_noenv', etc.) are combined -- with bitwise or. TODO: replace with a newtype with a monoid instance type CEvFlagType = CInt #{enum CEvFlagType, , evflag_auto = EVFLAG_AUTO , evflag_noenv = EVFLAG_NOENV , evbackend_select = EVBACKEND_SELECT , evbackend_poll = EVBACKEND_POLL , evbackend_epoll = EVBACKEND_EPOLL , evbackend_kqueue = EVBACKEND_KQUEUE , evbackend_devpoll = EVBACKEND_DEVPOLL , evbackend_port = EVBACKEND_PORT } #{enum CEventType, , ev_read = EV_READ , ev_write = EV_WRITE } data EvLoop type EvLoopPtr = Ptr EvLoop data EvWatcher type EvWatcherPtr = Ptr EvWatcher data EvIo = EvIo { fd :: CInt, events :: CInt } type EvIoPtr = Ptr EvIo data EvTimer = EvTimer { repeat :: Double } type EvTimerPtr = Ptr EvTimer data EvAsync type EvAsyncPtr = Ptr EvAsync instance Storable EvWatcher where sizeOf _ = (#size struct ev_watcher) alignment _ = alignment (undefined :: CInt) instance Storable EvIo where sizeOf _ = #size struct ev_io alignment _ = alignment (undefined :: CInt) peek ptr = do fd' <- (#peek ev_io, fd) ptr events' <- (#peek ev_io, events) ptr return EvIo { fd = fd', events = events' } poke ptr (EvIo fd' events') = do (#poke ev_io, fd) ptr fd' (#poke ev_io, events) ptr events' instance Storable EvTimer where sizeOf _ = (#size struct ev_timer) alignment _ = alignment (undefined :: CInt) peek ptr = do repeat' <- (#peek ev_timer, repeat) ptr return EvTimer { repeat = repeat' } poke ptr (EvTimer repeat') = do (#poke ev_timer, repeat) ptr repeat' instance Storable EvAsync where sizeOf _ = (#size struct ev_async) alignment _ = alignment (undefined :: CInt) -- | An 'IoCallback' is called when a file descriptor becomes readable or -- writable. It takes a pointer to an @ev\_loop@ structure, a pointer to an -- @ev\_io@ structure, and an event mask. type IoCallback = EvLoopPtr -> EvIoPtr -> CEventType -> IO () -- | A 'TimerCallback' is called when a timer expires. It takes a pointer to an -- @ev\_loop@ structure, a pointer to an @ev\_io@ structure, and an (unused?) -- event mask. type TimerCallback = EvLoopPtr -> EvTimerPtr -> CEventType -> IO () -- | An 'AsyncCallback' is called when you wakeup an event loop with -- @ev_async_send@ type AsyncCallback = EvLoopPtr -> EvAsyncPtr -> CEventType -> IO () -- | 'MutexCallback' is called by @ev\_set\_loop\_release\_cb@ type MutexCallback = EvLoopPtr -> IO () -- | Libev timestamp values are C doubles containing the (floating) number of -- seconds since Jan 1, 1970. type EvTimestamp = CDouble -- | Set up the given loop for mutex locking from haskell-land -- if you want -- to touch the loop from other Haskell threads, you'll need to do this. The -- two FunPtr objects returned need to be explicitly freed with -- 'freeMutexCallback'. -- -- IMPORTANT: if you want multithreaded access to an 'EvLoopPtr', you'll have -- to acquire the 'MVar' returned here (using 'withMVar') whenever you call any -- of the @ev@ functions. Very bad C-land crash\/bang\/boom could otherwise -- result. -- -- ALSO IMPORTANT: any changes you make to an 'EvLoopPtr' from another thread -- while the event loop thread is blocked inside @ev\_loop()@ will NOT take -- effect until the the event loop thread unblocks. You'll need to set up an -- @ev\_async@ watcher in order to wake up the event loop thread. setupLockingForLoop :: EvLoopPtr -> IO (FunPtr MutexCallback, FunPtr MutexCallback, MVar ()) setupLockingForLoop loop = do mvar <- newMVar () acq <- mkMutexCallback $ acquire mvar rel <- mkMutexCallback $ release mvar evSetLoopReleaseCB loop rel acq return (rel, acq, mvar) where release mvar _ = putMVar mvar () acquire mvar _ = takeMVar mvar foreign import ccall safe "ev_set_loop_release_cb" evSetLoopReleaseCB :: EvLoopPtr -> FunPtr MutexCallback -> FunPtr MutexCallback -> IO () -- | Returns the default set of 'CEvFlagType' flags foreign import ccall unsafe "ev.h ev_recommended_backends" evRecommendedBackends :: IO CEvFlagType foreign import ccall unsafe "wev_default_loop" evDefaultLoop :: CInt -> IO EvLoopPtr foreign import ccall "wev_loop" evLoop :: EvLoopPtr -> CInt -> IO () foreign import ccall "wev_unloop" evUnloop :: EvLoopPtr -> CInt -> IO () foreign import ccall unsafe "wev_loop_new" evLoopNew :: CUInt -> IO EvLoopPtr foreign import ccall unsafe "wev_loop_destroy" evLoopDestroy :: EvLoopPtr -> IO () foreign import ccall unsafe "wev_io_init" evIoInit :: EvIoPtr -> FunPtr IoCallback -> CInt -> CEventType -> IO () foreign import ccall unsafe "wev_io_start" evIoStart :: EvLoopPtr -> EvIoPtr -> IO () foreign import ccall unsafe "wev_io_stop" evIoStop :: EvLoopPtr -> EvIoPtr -> IO () foreign import ccall unsafe "wev_async_init" evAsyncInit :: EvAsyncPtr -> FunPtr AsyncCallback -> IO () foreign import ccall unsafe "wev_async_send" evAsyncSend :: EvLoopPtr -> EvAsyncPtr -> IO () foreign import ccall unsafe "wev_async_start" evAsyncStart :: EvLoopPtr -> EvAsyncPtr -> IO () foreign import ccall unsafe "wev_async_stop" evAsyncStop :: EvLoopPtr -> EvAsyncPtr -> IO () foreign import ccall unsafe "wev_timer_init" evTimerInit :: EvTimerPtr -> FunPtr TimerCallback -> EvTimestamp -> EvTimestamp -> IO () foreign import ccall unsafe "wev_timer_start" evTimerStart :: EvLoopPtr -> EvTimerPtr -> IO () foreign import ccall unsafe "wev_timer_stop" evTimerStop :: EvLoopPtr -> EvTimerPtr -> IO () foreign import ccall unsafe "wev_timer_again" evTimerAgain :: EvLoopPtr -> EvTimerPtr -> IO () foreign import ccall unsafe "wev_timer_remaining" evTimerRemaining :: EvLoopPtr -> EvTimerPtr -> IO (EvTimestamp) foreign import ccall unsafe "unistd.h close" c_close :: CInt -> IO (CInt) foreign import ccall unsafe "unistd.h read" c_read :: CInt -> CString -> CSize -> IO (CSize) foreign import ccall unsafe "unistd.h write" c_write :: CInt -> CString -> CSize -> IO (CSize) -- | Calls @accept()@ and sets the socket non-blocking. foreign import ccall unsafe "c_accept" c_accept :: CInt -> IO (CInt) foreign import ccall unsafe "set_nonblocking" c_setnonblocking :: CInt -> IO () -- | Fetches the current time from the operating system. Usually 'evNow' is -- preferred since it avoids a context switch by returning a cached value. foreign import ccall unsafe "ev.h ev_time" evTime :: IO EvTimestamp -- | Fetch a the cached copy of the current time from a loop. foreign import ccall unsafe "ev.h ev_now" evNow :: EvLoopPtr -> IO EvTimestamp ----------------------- -- callback wrappers -- ----------------------- -- | Wrap up an 'IoCallback' so it can be delivered into C-land. This resource -- is not garbage-collected, you are responsible for freeing it with -- 'freeIoCallback'. foreign import ccall "wrapper" mkIoCallback :: IoCallback -> IO (FunPtr IoCallback) -- | Wrap up a 'TimerCallback' so it can be delivered into C-land. This -- resource is not garbage-collected, you are responsible for freeing it with -- 'freeTimerCallback'. foreign import ccall "wrapper" mkTimerCallback :: TimerCallback -> IO (FunPtr TimerCallback) -- | Wrap up an 'AsyncCallback' so it can be delivered into C-land. This -- resource is not garbage-collected, you are responsible for freeing it with -- 'freeAsyncCallback'. foreign import ccall "wrapper" mkAsyncCallback :: AsyncCallback -> IO (FunPtr AsyncCallback) foreign import ccall "wrapper" mkMutexCallback :: MutexCallback -> IO (FunPtr MutexCallback) freeIoCallback :: FunPtr IoCallback -> IO () freeIoCallback = freeHaskellFunPtr freeMutexCallback :: FunPtr MutexCallback -> IO () freeMutexCallback = freeHaskellFunPtr freeTimerCallback :: FunPtr TimerCallback -> IO () freeTimerCallback = freeHaskellFunPtr freeAsyncCallback :: FunPtr AsyncCallback -> IO () freeAsyncCallback = freeHaskellFunPtr -- mem allocators -- foreign import ccall unsafe "wmkevio" mkEvIo :: IO (EvIoPtr) -- foreign import ccall unsafe "wfreeevio" freeEvIo :: EvIoPtr -> IO () -- | Makes a new @ev_io@ struct using 'malloc'. You are responsible for freeing -- it with 'freeEvIo'. mkEvIo :: IO (EvIoPtr) mkEvIo = malloc -- | free() an 'EvIoPtr' freeEvIo :: EvIoPtr -> IO () freeEvIo = free -- | Makes a new @ev_timer@ struct using 'malloc'. You are responsible for freeing -- it with 'freeEvTimer'. mkEvTimer :: IO (EvTimerPtr) mkEvTimer = malloc -- | free() an 'EvTimer' freeEvTimer :: EvTimerPtr -> IO () freeEvTimer = free -- | Makes a new @ev_async@ struct using 'malloc'. You are responsible for -- freeing it with 'freeEvAsync'. mkEvAsync :: IO (EvAsyncPtr) mkEvAsync = malloc -- | free() an 'EvAsync' freeEvAsync :: EvAsyncPtr -> IO () freeEvAsync = free