module Concur.Core.Notify
  ( Notify
  , fetch
  , await
  , notify
  , newNotify
  , newNotifyIO
  ) where

import           Control.Concurrent.STM (STM, TVar, newTVar, newTVarIO,
                                         readTVar, retry, writeTVar)
import           Control.Monad          (void)

-- TODO: Use Weak TVar pointers as appropriate
newtype Notify a = Notify (TVar (Maybe a))

fetch :: Notify a -> STM (Maybe a)
fetch (Notify v) = tryTakeTVar v

await :: Notify a -> STM a
await (Notify v) = takeTVar v

notify :: Notify a -> a -> STM ()
notify (Notify v) a = void (writeTVar v (Just a))

newNotify :: STM (Notify a)
newNotify = Notify <$> newTVar Nothing

newNotifyIO :: IO (Notify a)
newNotifyIO = Notify <$> newTVarIO Nothing

takeTVar :: TVar (Maybe a) -> STM a
takeTVar t = do
  m <- readTVar t
  case m of
    Nothing -> retry
    Just a  -> do writeTVar t Nothing; return a

tryTakeTVar :: TVar (Maybe a) -> STM (Maybe a)
tryTakeTVar t = do
  m <- readTVar t
  case m of
    Nothing -> return Nothing
    Just a  -> do writeTVar t Nothing; return (Just a)