{-# LANGUAGE MultiParamTypeClasses, TypeFamilies, FlexibleInstances, FlexibleContexts, UndecidableInstances #-}

module PrioritySync.Internal.Room
    (Room,
     newRoom,
     RoomCore.userData,
     inUse,
     Claim,
     claimedRoom,
     claimedThread,
     ClaimMode(..),
     claim,
     approveClaims,
     approve,
     RoomCore.isEmpty)
    where

import PrioritySync.Internal.RoomGroup
import PrioritySync.Internal.ClaimContext
import PrioritySync.Internal.RoomCore as RoomCore
import PrioritySync.Internal.RoomConstraint
import Control.Concurrent.STM
import Data.Map as Map

-- | Temporarily 'Acquire', and then release, or 'Release', and then acquire, some 'Room's for the duration of a critical section.
-- A simple example where a room might be used to prevent interleaving of 'stdout':
--
-- > room <- newRoom (MaxThreads 1)
-- > forkIO $ claim Acquire (Default,room) $ putStrLn "Hello World!"
-- > forkIO $ claim Acquire (Default,room) $ putStrLn "Foo!  Bar!"
claim :: (RoomGroup c,ClaimContext c) => ClaimMode -> c -> IO a -> IO a
claim claim_mode c actionIO = 
    do room_context_data <- newTVarIO (error "claim: BaseRoomContextData not yet available (please report a bug against the priority package)")
       claim_ (Map.fromList $ Prelude.map (flip (,) claim_mode) $ roomsOf c) 
              (\cs -> writeTVar room_context_data =<< approveClaimsEntering c cs) 
              (\cs -> writeTVar room_context_data =<< approveClaimsExiting c cs)
              (waitingAction c =<< readTVar room_context_data)
              actionIO