{-# LANGUAGE TypeFamilies, FlexibleContexts #-}
module PrioritySync.Internal.Dispatch
    (dispatch,TaskHandle,reprioritize,getResult,tryGetResult)
    where

import PrioritySync.Internal.Prioritized
import PrioritySync.Internal.Receipt
import PrioritySync.Internal.RoomGroup
import PrioritySync.Internal.Room
import PrioritySync.Internal.ClaimContext
import Control.Concurrent
import Control.Concurrent.STM

data TaskHandle p a = TaskHandle {
    task_reprioritize :: (p -> p) -> STM (),
    task_result :: TVar (Maybe a) }

-- | Perform a task on another thread.  This task can be reprioritized and canceled.
dispatch :: (RoomGroup c,ClaimContext c,Prioritized (ClaimHandle c)) => 
            c -> IO a -> IO (TaskHandle (Priority (ClaimHandle c)) a)
dispatch c actionM =
    do result <- newTVarIO Nothing
       receive_task_handle <- newTVarIO Nothing
       _ <- forkIO $ (atomically . writeTVar result . Just) =<< claim Acquire (Receipt c (writeTVar receive_task_handle . Just) (const $ return ())) actionM
       task_handle <- atomically $ maybe retry return =<< readTVar receive_task_handle
       return $ TaskHandle (reprioritize task_handle) result

-- | Change the priority of a task.  This will not work if the task has already started.
instance Prioritized (TaskHandle p a) where
    type Priority (TaskHandle p a) = p
    reprioritize = task_reprioritize

-- | Wait for the result from this task.
getResult :: TaskHandle p a -> STM a
getResult task = maybe retry return =<< readTVar (task_result task)

-- | Non-blocking version of 'getResult'.
tryGetResult :: TaskHandle p a -> STM (Maybe a)
tryGetResult = readTVar . task_result