{-# LANGUAGE BangPatterns , MagicHash , UnboxedTuples #-}
module Utilities (
IndexedMVar()
, newIndexedMVar, putMVarIx, readMVarIx, tryReadMVarIx
, nextHighestPowerOfTwo
, touchIORef
) where
import Control.Concurrent.MVar
import Control.Exception
import Control.Applicative
import Data.Bits
import Data.Word
import Data.Atomics
import Data.IORef
import GHC.Prim(touch#)
import GHC.IORef(IORef(..))
import GHC.STRef(STRef(..))
import GHC.Base(IO(..))
import Prelude
newtype IndexedMVar a = IndexedMVar (IORef [(Int, MVar a)])
newIndexedMVar :: IO (IndexedMVar a)
newIndexedMVar = IndexedMVar <$> newIORef []
readMVarIx :: IndexedMVar a -> Int -> IO a
{-# INLINE readMVarIx #-}
readMVarIx mvIx i = do
readMVar =<< getMVarIx mvIx i
tryReadMVarIx :: IndexedMVar a -> Int -> IO (Maybe a)
{-# INLINE tryReadMVarIx #-}
tryReadMVarIx mvIx i = do
tryReadMVar =<< getMVarIx mvIx i
putMVarIx :: IndexedMVar a -> Int -> a -> IO ()
{-# INLINE putMVarIx #-}
putMVarIx mvIx i a = do
flip putMVar a =<< getMVarIx mvIx i
getMVarIx :: IndexedMVar a -> Int -> IO (MVar a)
{-# INLINE getMVarIx #-}
getMVarIx (IndexedMVar v) i = do
mv <- newEmptyMVar
tk0 <- readForCAS v
let go tk = do
let !xs = peekTicket tk
case findInsert i mv xs of
Left alreadyPresentMVar -> return alreadyPresentMVar
Right xs' -> do
(success,newTk) <- casIORef v tk xs'
if success
then return mv
else go newTk
go tk0
findInsert :: Int -> mvar -> [(Int,mvar)] -> Either mvar [(Int,mvar)]
{-# INLINE findInsert #-}
findInsert i mv = ins where
ins [] = Right [(i,mv)]
ins xss@((i',x):xs) =
case compare i i' of
GT -> Right $ (i,mv):xss
EQ -> Left x
LT -> fmap ((i',x):) $ ins xs
nextHighestPowerOfTwo :: Int -> Int
nextHighestPowerOfTwo 0 = 1
nextHighestPowerOfTwo n
| n > maxPowerOfTwo = error $ "The next power of two greater than "++(show n)++" exceeds the highest value representable by Int."
| otherwise =
let !nhp2 = 2 ^ (ceiling (logBase 2 $ fromIntegral $ abs n :: Float) :: Int)
in assert (nhp2 > 0 && popCount (fromIntegral nhp2 :: Word) == 1)
nhp2
where maxPowerOfTwo = (floor $ sqrt $ (fromIntegral (maxBound :: Int)::Float)) ^ (2::Int)
touchIORef :: IORef a -> IO ()
touchIORef (IORef (STRef v)) = IO $ \s ->
case touch# v s of
s' -> (# s', () #)