module Unison.Util.RefPromise
  ( Ticket,
    peekTicket,
    readForCAS,
    casIORef,
    Promise,
    newPromise,
    readPromise,
    tryReadPromise,
    writePromise,
  )
where

import Control.Concurrent.MVar (MVar, newEmptyMVar, readMVar, tryPutMVar, tryReadMVar)
import Data.Atomics (Ticket, casIORef, peekTicket, readForCAS)

newtype Promise a = Promise {forall a. Promise a -> MVar a
state :: MVar a}

-- create an empty promise
newPromise :: IO (Promise a)
newPromise :: forall a. IO (Promise a)
newPromise = (MVar a -> Promise a) -> IO (MVar a) -> IO (Promise a)
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap MVar a -> Promise a
forall a. MVar a -> Promise a
Promise IO (MVar a)
forall a. IO (MVar a)
newEmptyMVar

-- read the value of the promise
-- return immediately if the promise if full, block if empty
readPromise :: Promise a -> IO a
readPromise :: forall a. Promise a -> IO a
readPromise Promise {MVar a
$sel:state:Promise :: forall a. Promise a -> MVar a
state :: MVar a
state} = MVar a -> IO a
forall a. MVar a -> IO a
readMVar MVar a
state

-- try to read the value of the promise
-- immediately return Nothing if the promise is empty
tryReadPromise :: Promise a -> IO (Maybe a)
tryReadPromise :: forall a. Promise a -> IO (Maybe a)
tryReadPromise Promise {MVar a
$sel:state:Promise :: forall a. Promise a -> MVar a
state :: MVar a
state} = MVar a -> IO (Maybe a)
forall a. MVar a -> IO (Maybe a)
tryReadMVar MVar a
state

-- if the promise is empty, write the value, awake all readers and return True
-- if full, ignore the write and return False
writePromise :: Promise a -> a -> IO Bool
writePromise :: forall a. Promise a -> a -> IO Bool
writePromise Promise {MVar a
$sel:state:Promise :: forall a. Promise a -> MVar a
state :: MVar a
state} a
value = MVar a -> a -> IO Bool
forall a. MVar a -> a -> IO Bool
tryPutMVar MVar a
state a
value