-- |
-- Module      : Streamly.Internal.Data.IORef.Unboxed
-- Copyright   : (c) 2019 Composewell Technologies
--
-- License     : BSD3
-- Maintainer  : streamly@composewell.com
-- Stability   : experimental
-- Portability : GHC
--
-- A mutable variable in a mutation capable monad (IO) holding a 'Unboxed'
-- value. This allows fast modification because of unboxed storage.
--
-- = Multithread Consistency Notes
--
-- In general, any value that straddles a machine word cannot be guaranteed to
-- be consistently read from another thread without a lock.  GHC heap objects
-- are always machine word aligned, therefore, a 'IORef' is also word aligned.
-- On a 64-bit platform, writing a 64-bit aligned type from one thread and
-- reading it from another thread should give consistent old or new value. The
-- same holds true for 32-bit values on a 32-bit platform.

module Streamly.Internal.Data.IORef.Unboxed
    (
      IORef

    -- * Construction
    , newIORef

    -- * Write
    , writeIORef
    , modifyIORef'

    -- * Read
    , readIORef
    , toStreamD
    )
where

#include "inline.hs"

import Data.Proxy (Proxy(..))
import Control.Monad.IO.Class (MonadIO(..))
import Streamly.Internal.Data.Unboxed
    ( MutableByteArray(..)
    , Unbox
    , sizeOf
    , peekWith
    , pokeWith
    , newUnpinnedBytes
    )

import qualified Streamly.Internal.Data.Stream.StreamD.Type as D

-- | An 'IORef' holds a single 'Unbox'-able value.
newtype IORef a = IORef MutableByteArray

-- | Create a new 'IORef'.
--
-- /Pre-release/
{-# INLINE newIORef #-}
newIORef :: forall a. Unbox a => a -> IO (IORef a)
newIORef :: forall a. Unbox a => a -> IO (IORef a)
newIORef a
x = do
    MutableByteArray
var <- Int -> IO MutableByteArray
newUnpinnedBytes (forall a. Unbox a => Proxy a -> Int
sizeOf (forall {k} (t :: k). Proxy t
Proxy :: Proxy a))
    forall a. Unbox a => MutableByteArray -> Int -> a -> IO ()
pokeWith MutableByteArray
var Int
0 a
x
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a. MutableByteArray -> IORef a
IORef MutableByteArray
var

-- | Write a value to an 'IORef'.
--
-- /Pre-release/
{-# INLINE writeIORef #-}
writeIORef :: Unbox a => IORef a -> a -> IO ()
writeIORef :: forall a. Unbox a => IORef a -> a -> IO ()
writeIORef (IORef MutableByteArray
var) = forall a. Unbox a => MutableByteArray -> Int -> a -> IO ()
pokeWith MutableByteArray
var Int
0

-- | Read a value from an 'IORef'.
--
-- /Pre-release/
{-# INLINE readIORef #-}
readIORef :: Unbox a => IORef a -> IO a
readIORef :: forall a. Unbox a => IORef a -> IO a
readIORef (IORef MutableByteArray
var) = forall a. Unbox a => MutableByteArray -> Int -> IO a
peekWith MutableByteArray
var Int
0

-- | Modify the value of an 'IORef' using a function with strict application.
--
-- /Pre-release/
{-# INLINE modifyIORef' #-}
modifyIORef' :: Unbox a => IORef a -> (a -> a) -> IO ()
modifyIORef' :: forall a. Unbox a => IORef a -> (a -> a) -> IO ()
modifyIORef' IORef a
var a -> a
g = do
  a
x <- forall a. Unbox a => IORef a -> IO a
readIORef IORef a
var
  forall a. Unbox a => IORef a -> a -> IO ()
writeIORef IORef a
var (a -> a
g a
x)

-- | Generate a stream by continuously reading the IORef.
--
-- /Pre-release/
{-# INLINE_NORMAL toStreamD #-}
toStreamD :: (MonadIO m, Unbox a) => IORef a -> D.Stream m a
toStreamD :: forall (m :: * -> *) a.
(MonadIO m, Unbox a) =>
IORef a -> Stream m a
toStreamD IORef a
var = forall (m :: * -> *) a s.
(State StreamK m a -> s -> m (Step s a)) -> s -> Stream m a
D.Stream forall {m :: * -> *} {p}. MonadIO m => p -> () -> m (Step () a)
step ()

    where

    {-# INLINE_LATE step #-}
    step :: p -> () -> m (Step () a)
step p
_ () = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall a. Unbox a => IORef a -> IO a
readIORef IORef a
var) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \a
x -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall s a. a -> s -> Step s a
D.Yield a
x ()