cctools-workqueue-3.6.1.0.1.0.0: Control/Distributed/CCTools/WorkQueue/Internal/WorkQueue.hs
-- -*- indent-tabs-mode: nil -*- --
-- | Creation and operations on a 'WorkQueue'
module Control.Distributed.CCTools.WorkQueue.Internal.WorkQueue where
import Control.Distributed.CCTools.WorkQueue.Internal.Cast
import Control.Distributed.CCTools.WorkQueue.Internal.Types
import Control.Distributed.CCTools.WorkQueue.Internal.CastInstances ()
import Bindings.CCTools.WorkQueue
import Foreign.C
import Foreign.Ptr
import Foreign.ForeignPtr
import Foreign.Storable
import Foreign.Marshal.Utils (toBool)
import Control.Applicative ((<$>))
import Control.Lens
import Control.Monad ((>=>), void)
import System.IO.Unsafe
import Control.Monad.Loops
withJust :: Maybe a -> (a -> IO ()) -> IO ()
withJust m f = case m of
Just a -> f a
_ -> return ()
-- * WorkQueue creation/deletion
-- | The default parameters for initializing a 'WorkQueue'
--
-- > defaultQParams :: QueueParams
-- > defaultQParams = QP {
-- > _qport = Just . port $ fromIntegral c'WORK_QUEUE_DEFAULT_PORT
-- > , _name = Nothing
-- > , _fastabort = Nothing
-- > , _taskordering = Nothing
-- > , _priority = Nothing
-- > , _mode = Nothing
-- > , _logfile = Nothing
-- > , _scheduler = Nothing
-- > }
defaultQParams :: QueueParams
defaultQParams = QP {
_qport = Just . port $ fromIntegral c'WORK_QUEUE_DEFAULT_PORT
, _name = Nothing
, _fastabort = Nothing
, _taskordering = Nothing
, _priority = Nothing
, _mode = Nothing
, _logfile = Nothing
, _scheduler = Nothing
}
workqueue :: QueueParams -> IO WorkQueue
workqueue p = do
ptr <- c'work_queue_create (cast $ p^.qport)
q <- WQ <$> newForeignPtr p'work_queue_delete ptr
withJust (p^.fastabort ) (setFastAbort q)
withJust (p^.taskordering) (setTaskOrdering q)
withJust (p^.priority ) (setPriority q)
withJust (p^.mode ) (setMasterMode q)
withJust (p^.name ) (setName q)
withJust (p^.logfile ) (setLog q)
withJust (p^.scheduler ) (setScheduleAlg q)
return q
setName :: WorkQueue -> String -> IO ()
setName q n = withForeignPtr (q^.unWQ) $ \q' -> do
n' <- newCString n
c'work_queue_specify_name q' n'
setFastAbort :: WorkQueue -> FastAbort -> IO ()
setFastAbort q a = withForeignPtr (q^.unWQ) (void . flip c'work_queue_activate_fast_abort (cast a))
setTaskOrdering :: WorkQueue -> TaskOrdering -> IO ()
setTaskOrdering q o = withForeignPtr (q^.unWQ) (`c'work_queue_specify_task_order` cast o)
setPriority :: WorkQueue -> Int -> IO ()
setPriority q p = withForeignPtr (q^.unWQ) (`c'work_queue_specify_priority` cast p)
setMasterMode :: WorkQueue -> MasterMode -> IO ()
setMasterMode q m = withForeignPtr (q^.unWQ) (`c'work_queue_specify_master_mode` cast m)
setLog :: WorkQueue -> FilePath -> IO ()
setLog q p = withForeignPtr (q^.unWQ) $ \q' ->
c'work_queue_specify_log q' =<< newCString p
setScheduleAlg :: WorkQueue -> WorkerScheduleAlg -> IO ()
setScheduleAlg q a = withForeignPtr (q^.unWQ) (`c'work_queue_specify_algorithm` cast a)
shutdownWorkers :: WorkQueue -> NPlus Int -> IO (NPlus Int)
shutdownWorkers q n = withForeignPtr (q^.unWQ)
(fmap cast . flip c'work_queue_shut_down_workers (cast n))
-- * Task submission/retrieval
submit :: WorkQueue -> Task -> IO TaskID
submit q t = withForeignPtr (q^.unWQ)
(fmap cast . flip c'work_queue_submit (t^.unT))
wait :: WorkQueue -> Timeout -> IO (Maybe Task)
wait q t = withForeignPtr (q^.unWQ) $ \q' -> do
r <- c'work_queue_wait q' $ cast t
return $ if r == nullPtr
then Nothing
else Just $ T r
cancelTaskBy :: (Ptr C'work_queue -> a -> IO (Ptr C'work_queue_task))
-> (b -> IO a)
-> WorkQueue -> b -> IO (Maybe Task)
cancelTaskBy c'cancel mkAttr q attribute =
withForeignPtr (q^.unWQ) $ \q' -> do
a <- mkAttr attribute
r <- c'cancel q' a
return $ if r == nullPtr
then Nothing
else Just $ T r
cancelTaskID :: WorkQueue -> TaskID -> IO (Maybe Task)
cancelTaskID = cancelTaskBy c'work_queue_cancel_by_taskid (return . cast)
cancelTaskTag :: WorkQueue -> String -> IO (Maybe Task)
cancelTaskTag = cancelTaskBy c'work_queue_cancel_by_tasktag newCString
-- | Main pattern for processing tasks after submission
--
-- Basically:
--
-- > eventLoop q timeout update process =
-- > while (q is not empty)
-- > update q
-- > r <- wait q timeout
-- > when r is Just: process q r
eventLoop :: WorkQueue
-> Timeout
-> (WorkQueue -> IO ()) -- ^ "update": called before 'wait'
-> (WorkQueue -> Task -> IO ()) -- ^ "process" a returned 'Task'
-> IO ()
eventLoop q timeout update process =
whileM_ (not <$> isEmpty q) $ do
update q
r <- wait q timeout
maybe (return ()) (process q) r
-- * WorkQueue properties
getHunger :: WorkQueue -> IO Hunger
getHunger q = cast <$> withForeignPtr (q^.unWQ) c'work_queue_hungry
isFull :: WorkQueue -> IO Bool
isFull = fmap (Full ==) . getHunger
isEmpty :: WorkQueue -> IO Bool
isEmpty q = toBool <$> withForeignPtr (q^.unWQ) c'work_queue_empty
getPort :: WorkQueue -> IO Port
getPort q = P . cast <$> withForeignPtr (q^.unWQ) c'work_queue_port
getName :: WorkQueue -> IO String
getName q = peekCString =<< withForeignPtr (q^.unWQ) c'work_queue_name
-- * WorkQueue Runtime Statistics
getWorkerSummary :: WorkQueue -> IO String
getWorkerSummary q = peekCString =<< withForeignPtr (q^.unWQ) c'work_queue_get_worker_summary
getStats :: WorkQueue -> IO Stats
getStats q = withForeignPtr (q^.unWQ) $ \q' -> do
fptr <- mallocForeignPtr
withForeignPtr fptr $ c'work_queue_get_stats q'
return $ S fptr
withStats :: (a -> b) -> (C'work_queue_stats -> a) -> Stats -> IO b
withStats g f s = withForeignPtr (s^.unS) (peek >=> (return . g . f))
unsafeWithStats :: (a -> b) -> (C'work_queue_stats -> a) -> Stats -> b
unsafeWithStats g f s = unsafeDupablePerformIO $ withStats g f s
workersInit :: Stats -> Int
workersInit = unsafeWithStats fromIntegral c'work_queue_stats'workers_init
workersReady :: Stats -> Int
workersReady = unsafeWithStats fromIntegral c'work_queue_stats'workers_ready
workersBusy :: Stats -> Int
workersBusy = unsafeWithStats fromIntegral c'work_queue_stats'workers_busy
workersCancelling :: Stats -> Int
workersCancelling = unsafeWithStats fromIntegral c'work_queue_stats'workers_cancelling
tasksRunning :: Stats -> Int
tasksRunning = unsafeWithStats fromIntegral c'work_queue_stats'tasks_running
tasksWaiting :: Stats -> Int
tasksWaiting = unsafeWithStats fromIntegral c'work_queue_stats'tasks_waiting
tasksComplete :: Stats -> Int
tasksComplete = unsafeWithStats fromIntegral c'work_queue_stats'tasks_complete
totalTasksDispatched :: Stats -> Int
totalTasksDispatched = unsafeWithStats fromIntegral c'work_queue_stats'total_tasks_dispatched
totalTasksComplete :: Stats -> Int
totalTasksComplete = unsafeWithStats fromIntegral c'work_queue_stats'total_tasks_complete
totalWorkersJoined :: Stats -> Int
totalWorkersJoined = unsafeWithStats fromIntegral c'work_queue_stats'total_workers_joined
totalWorkersRemoved :: Stats -> Int
totalWorkersRemoved = unsafeWithStats fromIntegral c'work_queue_stats'total_workers_removed
totalBytesSent :: Stats -> Integer
totalBytesSent = unsafeWithStats fromIntegral c'work_queue_stats'total_bytes_sent
totalBytesReceived :: Stats -> Integer
totalBytesReceived = unsafeWithStats fromIntegral c'work_queue_stats'total_bytes_received
startTime :: Stats -> EpochTime MicroSeconds
startTime = unsafeWithStats (ET . fromIntegral) c'work_queue_stats'start_time
totalSendTime :: Stats -> DiffTime MicroSeconds
totalSendTime = unsafeWithStats (DT . fromIntegral) c'work_queue_stats'total_send_time
totalReceiveTime :: Stats -> DiffTime MicroSeconds
totalReceiveTime = unsafeWithStats (DT . fromIntegral) c'work_queue_stats'total_receive_time