packages feed

primal-0.3.0.0: src/Data/Prim/Ref.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UnboxedTuples #-}
-- |
-- Module      : Data.Prim.Ref
-- Copyright   : (c) Alexey Kuleshevich 2020
-- License     : BSD3
-- Maintainer  : Alexey Kuleshevich <alexey@kuleshevi.ch>
-- Stability   : experimental
-- Portability : non-portable
--
module Data.Prim.Ref
  ( Ref(..)
  , IORef
  , STRef
  -- * Create
  , newRef
  , newDeepRef
  , isSameRef
  -- * Read/write
  , readRef
  , swapRef
  , swapDeepRef
  , writeRef
  , writeDeepRef
  -- * Modify
  -- ** Pure
  , modifyRef
  , modifyDeepRef
  , modifyRef_
  , modifyFetchNewRef
  , modifyFetchOldRef
  -- ** Monadic
  , modifyRefM
  , modifyDeepRefM
  , modifyRefM_
  , modifyFetchNewRefM
  , modifyFetchOldRefM
  -- * Atomic
  , atomicReadRef
  , atomicSwapRef
  , atomicWriteRef
  , atomicModifyRef
  , atomicModifyRef_
  , atomicModifyFetchRef
  , atomicModifyFetchNewRef
  , atomicModifyFetchOldRef
  , atomicModifyFetchBothRef
  -- ** Original
  , casRef
  , atomicModifyRef2
  , atomicModifyRef2_
  , atomicModifyFetchNewRef2
  , atomicModifyFetchOldRef2
  , atomicModifyFetchBothRef2
  , atomicModifyFetchRef2
  -- * Lazy
  -- It is recommended to refrain from usage of lazy functions because they are a memory
  -- leak waiting to happen
  , newLazyRef
  , writeLazyRef
  , swapLazyRef
  , modifyLazyRef
  , modifyLazyRefM
  , atomicWriteLazyRef
  , atomicModifyLazyRef
  , atomicModifyFetchNewLazyRef
  , atomicModifyFetchOldLazyRef
  , atomicModifyFetchBothLazyRef
  , atomicModifyFetchLazyRef
  -- * Conversion
  -- ** STRef
  , toSTRef
  , fromSTRef
  -- ** IORef
  , toIORef
  , fromIORef
  -- * Weak Pointer
  , mkWeakRef
  ) where

import Control.DeepSeq
import Control.Prim.Monad
import Foreign.Prim
import Foreign.Prim.WeakPtr
import qualified GHC.IORef as IO
import qualified GHC.STRef as ST

-- | Mutable variable that can hold any value. This is just like `Data.STRef.STRef`, but
-- with type arguments flipped and is generalized to work in `MonadPrim`. It only stores a
-- reference to the value which means it works on boxed values. If the type can be unboxed
-- with `Data.Prim.Class.Prim` class, consider using
-- [@PVar@](https://hackage.haskell.org/package/pvar) package instead.
--
-- @since 0.3.0
data Ref a s = Ref (MutVar# s a)

-- | Uses `isSameRef`
instance Eq (Ref a s) where
  (==) = isSameRef

-- | Compatibility synonym
type IORef a = Ref a RW

-- | Compatibility synonym
type STRef s a = Ref a s

-- | Check whether supplied `Ref`s refer to the exact same one or not.
--
-- @since 0.3.0
isSameRef :: Ref a s -> Ref a s -> Bool
isSameRef (Ref ref1#) (Ref ref2#) = isTrue# (sameMutVar# ref1# ref2#)
{-# INLINE isSameRef #-}


-- | Create a new mutable variable. Initial value will be forced to WHNF (weak head normal form).
--
-- ==== __Examples__
--
-- >>> import Debug.Trace
-- >>> import Data.Prim.Ref
-- >>> ref <- newRef (trace "Initial value is evaluated" (217 :: Int))
-- Initial value is evaluated
-- >>> modifyFetchOldRef ref succ
-- 217
-- >>> readRef ref
-- 218
--
-- @since 0.3.0
newRef :: MonadPrim s m => a -> m (Ref a s)
newRef a = a `seq` newLazyRef a
{-# INLINE newRef #-}


-- | Create a new mutable variable. Same as `newRef`, but ensures that value is evaluated
-- to normal form.
--
-- ==== __Examples__
--
-- >>> import Debug.Trace
-- >>> import Data.Prim.Ref
-- >>> ref <- newDeepRef (Just (trace "Initial value is evaluated" (217 :: Int)))
-- Initial value is evaluated
-- >>> readRef ref
-- Just 217
--
-- @since 0.3.0
newDeepRef :: (NFData a, MonadPrim s m) => a -> m (Ref a s)
newDeepRef a = a `deepseq` newLazyRef a
{-# INLINE newDeepRef #-}

-- | Create a new mutable variable. Initial value stays unevaluated.
--
-- ==== __Examples__
--
-- In below example you will see that initial value is never evaluated.
--
-- >>> import Debug.Trace
-- >>> import Data.Prim.Ref
-- >>> ref <- newLazyRef (trace "Initial value is evaluated" (undefined :: Int))
-- >>> writeRef ref 1024
-- >>> modifyFetchNewRef ref succ
-- 1025
--
-- @since 0.3.0
newLazyRef :: MonadPrim s m => a -> m (Ref a s)
newLazyRef a =
  prim $ \s ->
    case newMutVar# a s of
      (# s', ref# #) -> (# s', Ref ref# #)
{-# INLINE newLazyRef #-}

----------------
-- Read/Write --
----------------

-- | Read contents of the mutable variable
--
-- ==== __Examples__
--
-- >>> import Data.Prim.Ref
-- >>> ref <- newRef "Hello World!"
-- >>> readRef ref
-- "Hello World!"
--
-- @since 0.3.0
readRef :: MonadPrim s m => Ref a s -> m a
readRef (Ref ref#) = prim (readMutVar# ref#)
{-# INLINE readRef #-}


-- | Swap a value of a mutable variable with a new one, while retrieving the old one. New
-- value is evaluated prior to it being written to the variable.
--
-- ==== __Examples__
--
-- >>> ref <- newRef (Left "Initial" :: Either String String)
-- >>> swapRef ref (Right "Last")
-- Left "Initial"
-- >>> readRef ref
-- Right "Last"
--
-- @since 0.3.0
swapRef :: MonadPrim s m => Ref a s -> a -> m a
swapRef ref a = readRef ref <* writeRef ref a
{-# INLINE swapRef #-}


-- | Swap a value of a mutable variable with a new one lazily, while retrieving the old
-- one. New value is __not__ evaluated prior to it being written to the variable.
--
-- ==== __Examples__
--
-- >>> ref <- newRef "Initial"
-- >>> swapLazyRef ref undefined
-- "Initial"
-- >>> _ <- swapLazyRef ref "Different"
-- >>> readRef ref
-- "Different"
--
-- @since 0.3.0
swapLazyRef :: MonadPrim s m => Ref a s -> a -> m a
swapLazyRef ref a = readRef ref <* writeLazyRef ref a
{-# INLINE swapLazyRef #-}


-- | Swap a value of a mutable variable with a new one, while retrieving the old one. New
-- value is evaluated to __normal__ form prior to it being written to the variable.
--
-- ==== __Examples__
--
-- >>> ref <- newRef (Just "Initial")
-- >>> swapDeepRef ref (Just (errorWithoutStackTrace "foo"))
-- *** Exception: foo
-- >>> readRef ref
-- Just "Initial"
--
-- @since 0.3.0
swapDeepRef :: (NFData a, MonadPrim s m) => Ref a s -> a -> m a
swapDeepRef ref a = readRef ref <* writeDeepRef ref a
{-# INLINE swapDeepRef #-}


-- | Write a value into a mutable variable strictly. If evaluating a value results in
-- exception, original value in the mutable variable will not be affected. Another great
-- benfit of this over `writeLazyRef` is that it helps avoiding memory leaks.
--
-- ==== __Examples__
--
-- >>> ref <- newRef "Original value"
-- >>> import Control.Prim.Exception
-- >>> _ <- try $ writeRef ref undefined :: IO (Either SomeException ())
-- >>> readRef ref
-- "Original value"
-- >>> writeRef ref "New total value"
-- >>> readRef ref
-- "New total value"
--
-- @since 0.3.0
writeRef :: MonadPrim s m => Ref a s -> a -> m ()
writeRef ref !a = writeLazyRef ref a
{-# INLINE writeRef #-}


-- | Same as `writeRef`, but will evaluate the argument to Normal Form prior to writing it
-- to the `Ref`
--
-- @since 0.3.0
writeDeepRef :: (NFData a, MonadPrim s m) => Ref a s -> a -> m ()
writeDeepRef ref a = a `deepseq` writeLazyRef ref a
{-# INLINE writeDeepRef #-}

-- | Write a value into a mutable variable lazily.
--
-- ==== __Examples__
--
-- >>> ref <- newRef "Original value"
-- >>> import Debug.Trace
-- >>> writeLazyRef ref (trace "'New string' is evaluated" "New string")
-- >>> x <- readRef ref
-- >>> writeRef ref (trace "'Totally new string' is evaluated" "Totally new string")
-- 'Totally new string' is evaluated
-- >>> putStrLn x
-- 'New string' is evaluated
-- New string
--
-- @since 0.3.0
writeLazyRef :: MonadPrim s m => Ref a s -> a -> m ()
writeLazyRef (Ref ref#) a = prim_ (writeMutVar# ref# a)
{-# INLINE writeLazyRef #-}


------------
-- Modify --
------------


-- | Apply a pure function to the contents of a mutable variable strictly. Returns the
-- artifact produced by the modifying function. Artifact is not forced, therfore it cannot
-- affect the outcome of modification. This function is a faster alternative to
-- `atomicModifyRef`, except without any guarantees of atomicity and ordering of mutable
-- operations during concurrent modification of the same `Ref`. For lazy version see
-- `modifyLazyRef` and for strict evaluation to normal form see `modifyDeepRef`.
--
-- @since 0.3.0
modifyRef :: MonadPrim s m => Ref a s -> (a -> (a, b)) -> m b
modifyRef ref f = modifyRefM ref (pure . f)
{-# INLINE modifyRef #-}

-- | Same as `modifyRef`, except it will evaluate result of computation to normal form.
--
-- @since 0.3.0
modifyDeepRef :: (NFData a, MonadPrim s m) => Ref a s -> (a -> (a, b)) -> m b
modifyDeepRef ref f = modifyDeepRefM ref (pure . f)
{-# INLINE modifyDeepRef #-}

-- | Apply a pure function to the contents of a mutable variable strictly.
--
-- @since 0.3.0
modifyRef_ :: MonadPrim s m => Ref a s -> (a -> a) -> m ()
modifyRef_ ref f = modifyRefM_ ref (pure . f)
{-# INLINE modifyRef_ #-}

-- | Apply a pure function to the contents of a mutable variable strictly. Returns the new value.
--
-- @since 0.3.0
modifyFetchNewRef :: MonadPrim s m => Ref a s -> (a -> a) -> m a
modifyFetchNewRef ref f = modifyFetchNewRefM ref (pure . f)
{-# INLINE modifyFetchNewRef #-}

-- | Apply a pure function to the contents of a mutable variable strictly. Returns the old value.
--
-- ==== __Examples__
--
-- >>> ref1 <- newRef (10 :: Int)
-- >>> ref2 <- newRef (201 :: Int)
-- >>> modifyRefM_ ref1 (\x -> modifyFetchOldRef ref2 (* x))
-- >>> readRef ref1
-- 201
-- >>> readRef ref2
-- 2010
--
-- @since 0.3.0
modifyFetchOldRef :: MonadPrim s m => Ref a s -> (a -> a) -> m a
modifyFetchOldRef ref f = modifyFetchOldRefM ref (pure . f)
{-# INLINE modifyFetchOldRef #-}


-- | Apply a pure function to the contents of a mutable variable lazily. Returns the
-- artifact produced by the modifying function.
--
-- @since 0.3.0
modifyLazyRef :: MonadPrim s m => Ref a s -> (a -> (a, b)) -> m b
modifyLazyRef ref f = modifyLazyRefM ref (pure . f)
{-# INLINE modifyLazyRef #-}



-- | Modify value of a mutable variable with a monadic action. It is not strict in a
-- return value of type @b@, but the ne value written into the mutable variable is
-- evaluated to WHNF.
--
-- ==== __Examples__
--
modifyRefM :: MonadPrim s m => Ref a s -> (a -> m (a, b)) -> m b
modifyRefM ref f = do
  (a', b) <- f =<< readRef ref
  b <$ writeRef ref a'
{-# INLINE modifyRefM #-}


-- | Same as `modifyRefM`, except evaluates new value to normal form prior ot it being
-- written to the mutable ref.
modifyDeepRefM :: (NFData a, MonadPrim s m) => Ref a s -> (a -> m (a, b)) -> m b
modifyDeepRefM ref f = do
  (a', b) <- f =<< readRef ref
  b <$ writeDeepRef ref a'
{-# INLINE modifyDeepRefM #-}


-- | Modify value of a mutable variable with a monadic action. Result is written strictly.
--
-- ==== __Examples__
--
-- >>> ref <- newRef (Just "Some value")
-- >>> modifyRefM_ ref $ \ mv -> Nothing <$ mapM_ putStrLn mv
-- Some value
-- >>> readRef ref
-- Nothing
--
-- @since 0.3.0
modifyRefM_ :: MonadPrim s m => Ref a s -> (a -> m a) -> m ()
modifyRefM_ ref f = readRef ref >>= f >>= writeRef ref
{-# INLINE modifyRefM_ #-}

-- | Apply a monadic action to the contents of a mutable variable strictly. Returns the old value.
--
-- ==== __Examples__
--
-- >>> refName <- newRef "My name is: "
-- >>> refMyName <- newRef "Alexey"
-- >>> myName <- modifyFetchOldRefM refMyName $ \ name -> "Leo" <$ modifyRef_ refName (++ name)
-- >>> readRef refName >>= putStrLn
-- My name is: Alexey
-- >>> putStrLn myName
-- Alexey
-- >>> readRef refMyName >>= putStrLn
-- Leo
--
-- @since 0.3.0
modifyFetchOldRefM :: MonadPrim s m => Ref a s -> (a -> m a) -> m a
modifyFetchOldRefM ref f = do
  a <- readRef ref
  a <$ (writeRef ref =<< f a)
{-# INLINE modifyFetchOldRefM #-}


-- | Apply a monadic action to the contents of a mutable variable strictly. Returns the new value.
--
-- @since 0.3.0
modifyFetchNewRefM :: MonadPrim s m => Ref a s -> (a -> m a) -> m a
modifyFetchNewRefM ref f = do
  a <- readRef ref
  a' <- f a
  a' <$ writeRef ref a'
{-# INLINE modifyFetchNewRefM #-}

-- | Same as `modifyRefM`, but do not evaluate the new value written into the `Ref`.
--
-- @since 0.3.0
modifyLazyRefM :: MonadPrim s m => Ref a s -> (a -> m (a, b)) -> m b
modifyLazyRefM ref f = do
  a <- readRef ref
  (a', b) <- f a
  b <$ writeLazyRef ref a'
{-# INLINE modifyLazyRefM #-}

------------
-- Atomic --
------------


-- | Evaluate a value and write it atomically into a `Ref`. This is different from
-- `writeRef` because [a memory barrier](https://en.wikipedia.org/wiki/Memory_barrier)
-- will be issued. Use this instead of `writeRef` in order to guarantee the ordering of
-- operations in a concurrent environment.
--
-- @since 0.3.0
atomicWriteRef :: MonadPrim s m => Ref e s -> e -> m ()
atomicWriteRef ref !x = atomicModifyRef_ ref (const x)
{-# INLINE atomicWriteRef #-}

-- | This will behave exactly the same as `readRef` when the `Ref` is accessed within a
-- single thread only. However, despite being slower, it can help with with restricting
-- order of operations in cases when multiple threads perform modifications to the `Ref`
-- because it implies a memory barrier.
--
-- @since 0.3.0
atomicReadRef :: MonadPrim s m => Ref e s -> m e
atomicReadRef ref = atomicModifyFetchOldRef ref id

-- | Same as `atomicWriteRef`, but also returns the old value.
--
-- @since 0.3.0
atomicSwapRef :: MonadPrim s m => Ref e s -> e -> m e
atomicSwapRef ref x = atomicModifyFetchOldRef ref (const x)
{-# INLINE atomicSwapRef #-}

numTriesCAS :: Int
numTriesCAS = 35

-- | Apply a function to the value stored in a mutable `Ref` atomically. Function is
-- applied strictly with respect to the newly returned value, which matches the semantics
-- of `atomicModifyIORef'`, however the difference is that the artifact returned by the
-- action is not evaluated.
--
-- ====__Example__
--
-- >>> 
--
-- @since 0.3.0
atomicModifyRef :: MonadPrim s m => Ref a s -> (a -> (a, b)) -> m b
atomicModifyRef ref f = readRef ref >>= loop (0 :: Int)
  where
    loop i old
      | i < numTriesCAS = do
        case f old of
          (!new, result) -> do
            (success, current) <- casRef ref old new
            if success
              then pure result
              else loop (i + 1) current
      | otherwise = atomicModifyRef2 ref f
{-# INLINE atomicModifyRef #-}



atomicModifyRef2 :: MonadPrim s m => Ref a s -> (a -> (a, b)) -> m b
atomicModifyRef2 (Ref ref#) f =
#if __GLASGOW_HASKELL__ <= 806
  let g prev =
        case f prev of
          r@(!_new, _result) -> r
   in prim (atomicModifyMutVar# ref# g)
#else
  prim $ \s ->
    case atomicModifyMutVar2# ref# f s of
      (# s', _old, (!_new, result) #) -> (# s', result #)
#endif
{-# INLINE atomicModifyRef2 #-}


atomicModifyRef_ :: MonadPrim s m => Ref a s -> (a -> a) -> m ()
atomicModifyRef_ ref f = readRef ref >>= loop (0 :: Int)
  where
    loop i old
      | i < numTriesCAS = do
        (success, current) <- casRef ref old $! f old
        unless success $ loop (i + 1) current
      | otherwise = atomicModifyRef2_ ref f
{-# INLINE atomicModifyRef_ #-}

atomicModifyRef2_ :: MonadPrim s m => Ref a s -> (a -> a) -> m ()
atomicModifyRef2_ (Ref ref#) f =
  prim_ $ \s ->
    case atomicModifyMutVar_# ref# f s of
      (# s', _prev, !_cur #) -> s'
{-# INLINE atomicModifyRef2_ #-}

atomicModifyFetchOldRef :: MonadPrim s m => Ref a s -> (a -> a) -> m a
atomicModifyFetchOldRef ref f = readRef ref >>= loop (0 :: Int)
  where
    loop i old
      | i < numTriesCAS = do
        (success, current) <- casRef ref old $! f old
        if success
          then pure old
          else loop (i + 1) current
      | otherwise = atomicModifyFetchOldRef2 ref f
{-# INLINE atomicModifyFetchNewRef #-}

atomicModifyFetchOldRef2 :: MonadPrim s m => Ref a s -> (a -> a) -> m a
atomicModifyFetchOldRef2 (Ref ref#) f =
  prim $ \s ->
    case atomicModifyMutVar_# ref# f s of
      (# s', _prev, !_cur #) -> (# s', _prev #)
{-# INLINE atomicModifyFetchOldRef2 #-}


atomicModifyFetchNewRef :: MonadPrim s m => Ref a s -> (a -> a) -> m a
atomicModifyFetchNewRef ref f = readRef ref >>= loop (0 :: Int)
  where
    loop i old
      | i < numTriesCAS = do
        (success, current) <- casRef ref old $! f old
        if success
          then pure current
          else loop (i + 1) current
      | otherwise = atomicModifyFetchNewRef2 ref f
{-# INLINE atomicModifyFetchOldRef #-}

atomicModifyFetchNewRef2 :: MonadPrim s m => Ref a s -> (a -> a) -> m a
atomicModifyFetchNewRef2 (Ref ref#) f =
  prim $ \s ->
    case atomicModifyMutVar_# ref# f s of
      (# s', _prev, !cur #) -> (# s', cur #)
{-# INLINE atomicModifyFetchNewRef2 #-}



atomicModifyFetchBothRef :: MonadPrim s m => Ref a s -> (a -> a) -> m (a, a)
atomicModifyFetchBothRef ref f = readRef ref >>= loop (0 :: Int)
  where
    loop i old
      | i < numTriesCAS = do
        (success, current) <- casRef ref old $! f old
        if success
          then pure (old, current)
          else loop (i + 1) current
      | otherwise = atomicModifyFetchBothRef2 ref f
{-# INLINE atomicModifyFetchBothRef #-}


atomicModifyFetchBothRef2 :: MonadPrim s m => Ref a s -> (a -> a) -> m (a, a)
atomicModifyFetchBothRef2 (Ref ref#) f =
  prim $ \s ->
    case atomicModifyMutVar_# ref# f s of
      (# s', prev, !cur #) -> (# s', (prev, cur) #)
{-# INLINE atomicModifyFetchBothRef2 #-}

-- | Appy a function to the value in mutable `Ref` atomically
--
-- @since 0.3.0
atomicModifyFetchRef :: MonadPrim s m => Ref a s -> (a -> (a, b)) -> m (a, a, b)
atomicModifyFetchRef ref f = readRef ref >>= loop (0 :: Int)
  where
    loop i old
      | i < numTriesCAS = do
        case f old of
          (!new, result) -> do
            (success, current) <- casRef ref old new
            if success
              then pure (old, new, result)
              else loop (i + 1) current
      | otherwise = atomicModifyFetchRef2 ref f
{-# INLINE atomicModifyFetchRef #-}


-- TODO: Test this property
-- @atomicModifyIORef' ref (\x -> (x+1, undefined))@
--
-- will increment the 'IORef' and then throw an exception in the calling
-- thread.

atomicModifyFetchRef2 :: MonadPrim s m => Ref a s -> (a -> (a, b)) -> m (a, a, b)
atomicModifyFetchRef2 ref f =
  atomicModifyFetchLazyRef ref $ \current ->
    case f current of
      r@(!_new, _res) -> r
{-# INLINE atomicModifyFetchRef2 #-}


-- atomicModifyRef2 :: MonadPrim s m => Ref a s -> (a -> (a, b)) -> m (a, a, b)
-- (Ref ref#) f =
--   let g a =
--         case f a of
--           t@(a', _) -> a' `seq` t
--    in prim $ \s ->
--         case atomicModifyMutVar2# ref# g s of
--           (# s', old, (new, b) #) ->
--             case seq# new s' of
--               (# s'', new' #) ->
--                 case seq# b s'' of
--                   (# s''', b' #) -> (# s''', (old, new', b') #)

-- atomicModifyRef2 :: MonadPrim s m => Ref a s -> (a -> (a, b)) -> m b
-- atomicModifyRef2 (Ref ref#) f =
--   prim $ \s ->
--     case atomicModifyMutVar2# ref# f s of
--       (# s', _old, res #) -> (# s', res #)
-- {-# INLINE atomicModifyRef2 #-}






atomicModifyFetchBothLazyRef :: MonadPrim s m => Ref a s -> (a -> a) -> m (a, a)
atomicModifyFetchBothLazyRef (Ref ref#) f =
  prim $ \s ->
    case atomicModifyMutVar_# ref# f s of
      (# s', prev, cur #) -> (# s', (prev, cur) #)
{-# INLINE atomicModifyFetchBothLazyRef #-}


casRef :: MonadPrim s m => Ref a s -> a -> a -> m (Bool, a)
casRef (Ref ref#) expOld new =
  prim $ \s ->
    case casMutVar# ref# expOld new s of
      (# s', failed#, actualOld #) ->
        (# s', (isTrue# (failed# ==# 0#), actualOld) #)
{-# INLINE casRef #-}

atomicModifyFetchLazyRef :: MonadPrim s m => Ref a s -> (a -> (a, b)) -> m (a, a, b)
atomicModifyFetchLazyRef (Ref ref#) f =
  prim $ \s ->
    case atomicModifyMutVar2# ref# f s of
      (# s', old, ~(new, res) #) -> (# s', (old, new, res) #)
{-# INLINE atomicModifyFetchLazyRef #-}


atomicModifyLazyRef :: MonadPrim s m => Ref a s -> (a -> (a, b)) -> m b
atomicModifyLazyRef (Ref ref#) f = prim (atomicModifyMutVar# ref# f)
{-# INLINE atomicModifyLazyRef #-}

atomicModifyLazyRef_ :: MonadPrim s m => Ref a s -> (a -> a) -> m ()
atomicModifyLazyRef_ (Ref ref#) f =
  prim_ $ \s ->
    case atomicModifyMutVar_# ref# f s of
      (# s', _prev, _cur #) -> s'
{-# INLINE atomicModifyLazyRef_ #-}

atomicModifyFetchOldLazyRef :: MonadPrim s m => Ref a s -> (a -> a) -> m a
atomicModifyFetchOldLazyRef (Ref ref#) f =
  prim $ \s ->
    case atomicModifyMutVar_# ref# f s of
      (# s', prev, _cur #) -> (# s', prev #)

atomicModifyFetchNewLazyRef :: MonadPrim s m => Ref a s -> (a -> a) -> m a
atomicModifyFetchNewLazyRef (Ref ref#) f =
  prim $ \s ->
    case atomicModifyMutVar_# ref# f s of
      (# s', _prev, cur #) -> (# s', cur #)

atomicWriteLazyRef :: MonadPrim s m => Ref b s -> b -> m ()
atomicWriteLazyRef ref x = atomicModifyLazyRef_ ref (const x)


-- | Convert `Ref` to `STRef`
--
-- @since 0.3.0
toSTRef :: Ref a s -> ST.STRef s a
toSTRef (Ref ref#) = ST.STRef ref#
{-# INLINE toSTRef #-}

-- | Convert `STRef` to `Ref`
--
-- @since 0.3.0
fromSTRef :: ST.STRef s a -> Ref a s
fromSTRef (ST.STRef ref#) = Ref ref#
{-# INLINE fromSTRef #-}

-- | Convert `Ref` to `IORef`
--
-- @since 0.3.0
toIORef :: Ref a RW -> IO.IORef a
toIORef = coerce . toSTRef
{-# INLINE toIORef #-}

-- | Convert `IORef` to `Ref`
--
-- @since 0.3.0
fromIORef :: IO.IORef a -> Ref a RW
fromIORef = fromSTRef . coerce
{-# INLINE fromIORef #-}




-- | Create a `Weak` pointer associated with the supplied `Ref`.
--
-- Same as `Data.IORef.mkWeakRef` from @base@, but works in any `MonadPrim` with
-- `RealWorld` state token.
--
-- @since 0.3.0
mkWeakRef ::
     forall a b m. MonadUnliftPrim RW m
  => Ref a RW
  -> m b -- ^ An action that will get executed whenever `Ref` gets garbage collected by
         -- the runtime.
  -> m (Weak (Ref a RW))
mkWeakRef ref@(Ref ref#) !finalizer =
  runInPrimBase finalizer $ \f# s ->
    case mkWeak# ref# ref f# s of
      (# s', weak# #) -> (# s', Weak weak# #)
{-# INLINE mkWeakRef #-}



-- atomicModifyIORef' :: IORef a -> (a -> (a,b)) -> IO b
-- -- See Note [atomicModifyIORef' definition]
-- atomicModifyIORef' ref f = do
--   (_old, (_new, !res)) <- atomicModifyIORef2 ref $
--     \old -> case f old of
--        r@(!_new, _res) -> r
--   pure res
-- atomicModifyIORef' :: IORef a -> (a -> (a,b)) -> IO b
-- atomicModifyIORef' (IORef (STRef r#)) f =
--   IO
--     (\s ->
--        case atomicModifyMutVar2# r# f s of
--          (# s', old, res@(!_new, _) #) -> (# s', (old, res) #))

-- atomicModifyIORef2 :: IORef a -> (a -> (a,b)) -> IO (a, (a, b))
-- atomicModifyIORef2 ref f = do
--   r@(_old, (_new, _res)) <- atomicModifyIORef2Lazy ref f
--   return r

-- atomicModifyIORef2Lazy :: IORef a -> (a -> (a,b)) -> IO (a, (a, b))
-- atomicModifyIORef2Lazy (IORef (STRef r#)) f =
--   IO (\s -> case atomicModifyMutVar2# r# f s of
--               (# s', old, res #) -> (# s', (old, res) #))