{-# LANGUAGE TypeFamilies, UndecidableInstances #-} module PrioritySync.Internal.Schedule (Schedule(..)) where import PrioritySync.Internal.UserData import PrioritySync.Internal.Room import PrioritySync.Internal.Queue import PrioritySync.Internal.ClaimContext import PrioritySync.Internal.RoomGroup import Control.Concurrent.STM import Control.Monad import Data.List -- | Schedule a task to run from a prioritized 'Queue'. The task will wait until it arrives at (or, with failover, near) the top of queue. Typical usage: -- -- > Schedule q 2 room1 -- -- Only the rooms inside the 'Schedule' declaration are claimed with scheduling. If access to a room doesn't need to be prioritized, it can be set outside -- the schedule: -- -- > (Schedule q 2 room1,room2) -- data Schedule p c = Schedule (Queue p) p c type instance UserData (Schedule p c) = UserData c instance (RoomGroup c) => RoomGroup (Schedule p c) where roomsOf (Schedule _ _ c) = roomsOf c instance (Ord p,RoomGroup c,ClaimContext c,ClaimHandle c ~ ()) => ClaimContext (Schedule p c) where type ClaimHandle (Schedule p c) = Maybe (TaskHandle p) approveClaimsEntering = scheduleClaims approveClaimsEntering approveClaimsExiting = scheduleClaims approveClaimsExiting waitingAction (Schedule _ _ c) Nothing = waitingAction c () waitingAction (Schedule _ _ c) (Just task) = flip unless retry . or =<< mapM (\m -> m >> return True `orElse` return False) [pullFromTop task >> return (), waitingAction c ()] scheduleClaims :: (Ord p,RoomGroup c,ClaimContext c,ClaimHandle c ~ ()) => (c -> [Claim (UserData c)] -> STM ()) -> Schedule p c -> [Claim (UserData c)] -> STM (Maybe (TaskHandle p)) scheduleClaims approveClaimsX (Schedule _ _ c) cs | null (intersect (map claimedRoom cs) $ roomsOf c) = approveClaimsX c cs >> return Nothing scheduleClaims approveClaimsX (Schedule q p c) cs = liftM Just $ putTask q p (approveClaimsX c cs)