{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UnboxedTuples #-}
module Cardano.Memory.Pool (
Pool,
initPool,
Block (..),
blockByteCount,
grabNextBlock,
countPages,
findNextZeroIndex,
) where
import Control.Applicative
import Control.Monad
import Data.Bits
import Data.Primitive.MutVar
import Data.Primitive.PVar
import Data.Primitive.PVar.Unsafe (atomicModifyIntArray#)
import Data.Primitive.PrimArray
import Foreign.ForeignPtr
import Foreign.Ptr
import GHC.Exts (fetchAndIntArray#)
import GHC.ForeignPtr (addForeignPtrConcFinalizer)
import GHC.IO
import GHC.Int
import GHC.ST
import GHC.TypeLits
data Block (n :: Nat) = Block
blockByteCount :: KnownNat n => Block n -> Int
blockByteCount :: forall (n :: Nat). KnownNat n => Block n -> Int
blockByteCount = forall a. Num a => Integer -> a
fromInteger forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (n :: Nat) (proxy :: Nat -> *).
KnownNat n =>
proxy n -> Integer
natVal
data Page n s = Page
{ forall (n :: Nat) s. Page n s -> ForeignPtr (Block n)
pageMemory :: !(ForeignPtr (Block n))
, forall (n :: Nat) s. Page n s -> MutablePrimArray s Int
pageBitArray :: !(MutablePrimArray s Int)
, forall (n :: Nat) s. Page n s -> PVar Int s
pageFull :: !(PVar Int s)
, forall (n :: Nat) s. Page n s -> MutVar s (Maybe (Page n s))
pageNextPage :: !(MutVar s (Maybe (Page n s)))
}
data Pool n s = Pool
{ forall (n :: Nat) s. Pool n s -> Page n s
poolFirstPage :: !(Page n s)
, forall (n :: Nat) s. Pool n s -> ST s (Page n s)
poolPageInitializer :: !(ST s (Page n s))
, forall (n :: Nat) s. Pool n s -> Ptr (Block n) -> IO ()
poolBlockFinalizer :: !(Ptr (Block n) -> IO ())
}
countPages :: Pool n s -> ST s Int
countPages :: forall (n :: Nat) s. Pool n s -> ST s Int
countPages Pool n s
pool = forall {m :: * -> *} {t} {n :: Nat}.
(PrimMonad m, Num t) =>
t -> Page n (PrimState m) -> m t
go Int
1 (forall (n :: Nat) s. Pool n s -> Page n s
poolFirstPage Pool n s
pool)
where
go :: t -> Page n (PrimState m) -> m t
go t
n Page {MutVar (PrimState m) (Maybe (Page n (PrimState m)))
pageNextPage :: MutVar (PrimState m) (Maybe (Page n (PrimState m)))
pageNextPage :: forall (n :: Nat) s. Page n s -> MutVar s (Maybe (Page n s))
pageNextPage} = do
forall (m :: * -> *) a.
PrimMonad m =>
MutVar (PrimState m) a -> m a
readMutVar MutVar (PrimState m) (Maybe (Page n (PrimState m)))
pageNextPage forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe (Page n (PrimState m))
Nothing -> forall (f :: * -> *) a. Applicative f => a -> f a
pure t
n
Just Page n (PrimState m)
nextPage -> t -> Page n (PrimState m) -> m t
go (t
n forall a. Num a => a -> a -> a
+ t
1) Page n (PrimState m)
nextPage
ixBitSize :: Int
ixBitSize :: Int
ixBitSize = forall b. FiniteBits b => b -> Int
finiteBitSize (Word
0 :: Word)
initPool ::
forall n s.
KnownNat n =>
Int ->
(forall a. Int -> ST s (ForeignPtr a)) ->
(Ptr (Block n) -> IO ()) ->
ST s (Pool n s)
initPool :: forall (n :: Nat) s.
KnownNat n =>
Int
-> (forall a. Int -> ST s (ForeignPtr a))
-> (Ptr (Block n) -> IO ())
-> ST s (Pool n s)
initPool Int
groupsPerPage forall a. Int -> ST s (ForeignPtr a)
memAlloc Ptr (Block n) -> IO ()
blockFinalizer = do
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Int
groupsPerPage forall a. Ord a => a -> a -> Bool
> Int
0) forall a b. (a -> b) -> a -> b
$
forall a. HasCallStack => [Char] -> a
error forall a b. (a -> b) -> a -> b
$
[Char]
"Groups per page should be a positive number, but got: "
forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show Int
groupsPerPage
let pageInit :: ST s (Page n s)
pageInit = do
ForeignPtr (Block n)
pageMemory <-
forall a. Int -> ST s (ForeignPtr a)
memAlloc forall a b. (a -> b) -> a -> b
$ Int
groupsPerPage forall a. Num a => a -> a -> a
* Int
ixBitSize forall a. Num a => a -> a -> a
* forall (n :: Nat). KnownNat n => Block n -> Int
blockByteCount (forall (n :: Nat). Block n
Block :: Block n)
MutablePrimArray s Int
pageBitArray <- forall (m :: * -> *) a.
(PrimMonad m, Prim a) =>
Int -> m (MutablePrimArray (PrimState m) a)
newPrimArray Int
groupsPerPage
forall a (m :: * -> *).
(Prim a, PrimMonad m) =>
MutablePrimArray (PrimState m) a -> Int -> Int -> a -> m ()
setPrimArray MutablePrimArray s Int
pageBitArray Int
0 Int
groupsPerPage Int
0
PVar Int s
pageFull <- forall s (m :: * -> *) a.
(MonadPrim s m, Prim a) =>
a -> m (PVar a s)
newPVar Int
0
MutVar s (Maybe (Page n s))
pageNextPage <- forall (m :: * -> *) a.
PrimMonad m =>
a -> m (MutVar (PrimState m) a)
newMutVar forall a. Maybe a
Nothing
forall (f :: * -> *) a. Applicative f => a -> f a
pure Page {ForeignPtr (Block n)
MutablePrimArray s Int
MutVar s (Maybe (Page n s))
PVar Int s
pageNextPage :: MutVar s (Maybe (Page n s))
pageFull :: PVar Int s
pageBitArray :: MutablePrimArray s Int
pageMemory :: ForeignPtr (Block n)
pageNextPage :: MutVar s (Maybe (Page n s))
pageFull :: PVar Int s
pageBitArray :: MutablePrimArray s Int
pageMemory :: ForeignPtr (Block n)
..}
Page n s
firstPage <- forall {n :: Nat}. ST s (Page n s)
pageInit
forall (f :: * -> *) a. Applicative f => a -> f a
pure
Pool
{ poolFirstPage :: Page n s
poolFirstPage = Page n s
firstPage
, poolPageInitializer :: ST s (Page n s)
poolPageInitializer = forall {n :: Nat}. ST s (Page n s)
pageInit
, poolBlockFinalizer :: Ptr (Block n) -> IO ()
poolBlockFinalizer = Ptr (Block n) -> IO ()
blockFinalizer
}
grabNextBlock :: KnownNat n => Pool n s -> ST s (ForeignPtr (Block n))
grabNextBlock :: forall (n :: Nat) s.
KnownNat n =>
Pool n s -> ST s (ForeignPtr (Block n))
grabNextBlock = forall (n :: Nat) s.
(Page n s
-> (Ptr (Block n) -> IO ()) -> ST s (Maybe (ForeignPtr (Block n))))
-> Pool n s -> ST s (ForeignPtr (Block n))
grabNextPoolBlockWith forall (n :: Nat) s.
KnownNat n =>
Page n s
-> (Ptr (Block n) -> IO ()) -> ST s (Maybe (ForeignPtr (Block n)))
grabNextPageForeignPtr
{-# INLINE grabNextBlock #-}
grabNextPoolBlockWith ::
(Page n s -> (Ptr (Block n) -> IO ()) -> ST s (Maybe (ForeignPtr (Block n)))) ->
Pool n s ->
ST s (ForeignPtr (Block n))
grabNextPoolBlockWith :: forall (n :: Nat) s.
(Page n s
-> (Ptr (Block n) -> IO ()) -> ST s (Maybe (ForeignPtr (Block n))))
-> Pool n s -> ST s (ForeignPtr (Block n))
grabNextPoolBlockWith Page n s
-> (Ptr (Block n) -> IO ()) -> ST s (Maybe (ForeignPtr (Block n)))
grabNext Pool n s
pool = Page n s -> ST s (ForeignPtr (Block n))
go (forall (n :: Nat) s. Pool n s -> Page n s
poolFirstPage Pool n s
pool)
where
go :: Page n s -> ST s (ForeignPtr (Block n))
go Page n s
page = do
Int
isPageFull <- forall s (m :: * -> *). MonadPrim s m => PVar Int s -> m Int
atomicReadIntPVar (forall (n :: Nat) s. Page n s -> PVar Int s
pageFull Page n s
page)
if Int -> Bool
intToBool Int
isPageFull
then
forall (m :: * -> *) a.
PrimMonad m =>
MutVar (PrimState m) a -> m a
readMutVar (forall (n :: Nat) s. Page n s -> MutVar s (Maybe (Page n s))
pageNextPage Page n s
page) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe (Page n s)
Nothing -> do
Page n s
newPage <- forall (n :: Nat) s. Pool n s -> ST s (Page n s)
poolPageInitializer Pool n s
pool
Maybe (Page n s)
mNextPage <-
forall (m :: * -> *) a b.
PrimMonad m =>
MutVar (PrimState m) a -> (a -> (a, b)) -> m b
atomicModifyMutVar' (forall (n :: Nat) s. Page n s -> MutVar s (Maybe (Page n s))
pageNextPage Page n s
page) forall a b. (a -> b) -> a -> b
$ \Maybe (Page n s)
mNextPage ->
(Maybe (Page n s)
mNextPage forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall a. a -> Maybe a
Just Page n s
newPage, Maybe (Page n s)
mNextPage)
case Maybe (Page n s)
mNextPage of
Maybe (Page n s)
Nothing -> Page n s -> ST s (ForeignPtr (Block n))
go Page n s
newPage
Just Page n s
existingPage -> do
forall a s. IO a -> ST s a
unsafeIOToST forall a b. (a -> b) -> a -> b
$ forall a. ForeignPtr a -> IO ()
finalizeForeignPtr (forall (n :: Nat) s. Page n s -> ForeignPtr (Block n)
pageMemory Page n s
newPage)
Page n s -> ST s (ForeignPtr (Block n))
go Page n s
existingPage
Just Page n s
nextPage -> Page n s -> ST s (ForeignPtr (Block n))
go Page n s
nextPage
else
Page n s
-> (Ptr (Block n) -> IO ()) -> ST s (Maybe (ForeignPtr (Block n)))
grabNext Page n s
page (forall (n :: Nat) s. Pool n s -> Ptr (Block n) -> IO ()
poolBlockFinalizer Pool n s
pool) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe (ForeignPtr (Block n))
Nothing -> Page n s -> ST s (ForeignPtr (Block n))
go Page n s
page
Just ForeignPtr (Block n)
ma -> forall (f :: * -> *) a. Applicative f => a -> f a
pure ForeignPtr (Block n)
ma
{-# INLINE grabNextPoolBlockWith #-}
intToBool :: Int -> Bool
intToBool :: Int -> Bool
intToBool Int
0 = Bool
False
intToBool Int
_ = Bool
True
grabNextPageForeignPtr ::
forall n s.
KnownNat n =>
Page n s ->
(Ptr (Block n) -> IO ()) ->
ST s (Maybe (ForeignPtr (Block n)))
grabNextPageForeignPtr :: forall (n :: Nat) s.
KnownNat n =>
Page n s
-> (Ptr (Block n) -> IO ()) -> ST s (Maybe (ForeignPtr (Block n)))
grabNextPageForeignPtr Page n s
page Ptr (Block n) -> IO ()
finalizer =
forall (n :: Nat) s.
KnownNat n =>
Page n s
-> (Ptr (Block n) -> IO () -> IO (ForeignPtr (Block n)))
-> ST s (Maybe (ForeignPtr (Block n)))
grabNextPageWithAllocator Page n s
page forall a b. (a -> b) -> a -> b
$ \Ptr (Block n)
blockPtr IO ()
resetIndex -> do
ForeignPtr (Block n)
fp <- forall a. Ptr a -> IO (ForeignPtr a)
newForeignPtr_ Ptr (Block n)
blockPtr
forall a. ForeignPtr a -> IO () -> IO ()
addForeignPtrConcFinalizer ForeignPtr (Block n)
fp forall a b. (a -> b) -> a -> b
$ Ptr (Block n) -> IO ()
finalizer Ptr (Block n)
blockPtr forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> IO ()
resetIndex
forall (f :: * -> *) a. Applicative f => a -> f a
pure ForeignPtr (Block n)
fp
{-# INLINE grabNextPageForeignPtr #-}
grabNextPageWithAllocator ::
forall n s.
KnownNat n =>
Page n s ->
(Ptr (Block n) -> IO () -> IO (ForeignPtr (Block n))) ->
ST s (Maybe (ForeignPtr (Block n)))
grabNextPageWithAllocator :: forall (n :: Nat) s.
KnownNat n =>
Page n s
-> (Ptr (Block n) -> IO () -> IO (ForeignPtr (Block n)))
-> ST s (Maybe (ForeignPtr (Block n)))
grabNextPageWithAllocator Page {ForeignPtr (Block n)
MutablePrimArray s Int
MutVar s (Maybe (Page n s))
PVar Int s
pageNextPage :: MutVar s (Maybe (Page n s))
pageFull :: PVar Int s
pageBitArray :: MutablePrimArray s Int
pageMemory :: ForeignPtr (Block n)
pageNextPage :: forall (n :: Nat) s. Page n s -> MutVar s (Maybe (Page n s))
pageFull :: forall (n :: Nat) s. Page n s -> PVar Int s
pageBitArray :: forall (n :: Nat) s. Page n s -> MutablePrimArray s Int
pageMemory :: forall (n :: Nat) s. Page n s -> ForeignPtr (Block n)
..} Ptr (Block n) -> IO () -> IO (ForeignPtr (Block n))
allocator = do
forall s. MutablePrimArray s Int -> ST s (Maybe Int)
setNextZero MutablePrimArray s Int
pageBitArray forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe Int
Nothing -> forall a. Maybe a
Nothing forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ forall s (m :: * -> *). MonadPrim s m => PVar Int s -> Int -> m ()
atomicWriteIntPVar PVar Int s
pageFull Int
1
Just Int
ix ->
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$
forall a s. IO a -> ST s a
unsafeIOToST forall a b. (a -> b) -> a -> b
$
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr (Block n)
pageMemory forall a b. (a -> b) -> a -> b
$ \Ptr (Block n)
pagePtr ->
let !blockPtr :: Ptr b
blockPtr =
forall a b. Ptr a -> Int -> Ptr b
plusPtr Ptr (Block n)
pagePtr forall a b. (a -> b) -> a -> b
$ Int
ix forall a. Num a => a -> a -> a
* forall (n :: Nat). KnownNat n => Block n -> Int
blockByteCount (forall (n :: Nat). Block n
Block :: Block n)
in Ptr (Block n) -> IO () -> IO (ForeignPtr (Block n))
allocator forall {b}. Ptr b
blockPtr forall a b. (a -> b) -> a -> b
$ do
let !(!Int
q, !Int
r) = Int
ix forall a. Integral a => a -> a -> (a, a)
`quotRem` Int
ixBitSize
!pageBitMask :: Int
pageBitMask = forall a. Bits a => a -> Int -> a
clearBit (forall a. Bits a => a -> a
complement Int
0) Int
r
forall a. ForeignPtr a -> IO ()
touchForeignPtr ForeignPtr (Block n)
pageMemory
forall s a. ST s a -> IO a
unsafeSTToIO forall a b. (a -> b) -> a -> b
$ forall s. MutablePrimArray s Int -> Int -> Int -> ST s ()
atomicAndIntMutablePrimArray MutablePrimArray s Int
pageBitArray Int
q Int
pageBitMask
forall s a. ST s a -> IO a
unsafeSTToIO forall a b. (a -> b) -> a -> b
$ forall s (m :: * -> *). MonadPrim s m => PVar Int s -> Int -> m ()
atomicWriteIntPVar PVar Int s
pageFull Int
0
{-# INLINE grabNextPageWithAllocator #-}
atomicAndIntMutablePrimArray :: MutablePrimArray s Int -> Int -> Int -> ST s ()
atomicAndIntMutablePrimArray :: forall s. MutablePrimArray s Int -> Int -> Int -> ST s ()
atomicAndIntMutablePrimArray (MutablePrimArray MutableByteArray# s
mba#) (I# Int#
i#) (I# Int#
m#) =
forall s a. STRep s a -> ST s a
ST forall a b. (a -> b) -> a -> b
$ \State# s
s# ->
case forall d.
MutableByteArray# d
-> Int# -> Int# -> State# d -> (# State# d, Int# #)
fetchAndIntArray# MutableByteArray# s
mba# Int#
i# Int#
m# State# s
s# of
(# State# s
s'#, Int#
_ #) -> (# State# s
s'#, () #)
{-# INLINE atomicAndIntMutablePrimArray #-}
atomicModifyMutablePrimArray :: MutablePrimArray s Int -> Int -> (Int -> (Int, a)) -> ST s a
atomicModifyMutablePrimArray :: forall s a.
MutablePrimArray s Int -> Int -> (Int -> (Int, a)) -> ST s a
atomicModifyMutablePrimArray (MutablePrimArray MutableByteArray# s
mba#) (I# Int#
i#) Int -> (Int, a)
f =
forall s a. STRep s a -> ST s a
ST forall a b. (a -> b) -> a -> b
$ forall d b.
MutableByteArray# d
-> Int# -> (Int# -> (# Int#, b #)) -> State# d -> (# State# d, b #)
atomicModifyIntArray# MutableByteArray# s
mba# Int#
i# (\Int#
x# -> case Int -> (Int, a)
f (Int# -> Int
I# Int#
x#) of (I# Int#
y#, a
a) -> (# Int#
y#, a
a #))
{-# INLINE atomicModifyMutablePrimArray #-}
findNextZeroIndex :: forall b. FiniteBits b => b -> Maybe Int
findNextZeroIndex :: forall b. FiniteBits b => b -> Maybe Int
findNextZeroIndex b
b =
let !i0 :: Int
i0 = forall b. FiniteBits b => b -> Int
countTrailingZeros b
b
i1 :: Int
i1 = forall b. FiniteBits b => b -> Int
countTrailingZeros (forall a. Bits a => a -> a
complement b
b)
maxBits :: Int
maxBits = forall b. FiniteBits b => b -> Int
finiteBitSize (forall a. HasCallStack => a
undefined :: b)
in if Int
i0 forall a. Eq a => a -> a -> Bool
== Int
0
then
if Int
i1 forall a. Eq a => a -> a -> Bool
== Int
maxBits
then forall a. Maybe a
Nothing
else forall a. a -> Maybe a
Just Int
i1
else forall a. a -> Maybe a
Just (Int
i0 forall a. Num a => a -> a -> a
- Int
1)
{-# INLINE findNextZeroIndex #-}
setNextZero :: MutablePrimArray s Int -> ST s (Maybe Int)
setNextZero :: forall s. MutablePrimArray s Int -> ST s (Maybe Int)
setNextZero MutablePrimArray s Int
ma = forall s a.
MutablePrimArray s Int
-> (Int -> Int -> (Int, Maybe a)) -> ST s (Maybe a)
ifindAtomicMutablePrimArray MutablePrimArray s Int
ma forall {a}. FiniteBits a => Int -> a -> (a, Maybe Int)
f
where
f :: Int -> a -> (a, Maybe Int)
f Int
i !a
w =
case forall b. FiniteBits b => b -> Maybe Int
findNextZeroIndex a
w of
Maybe Int
Nothing -> (a
w, forall a. Maybe a
Nothing)
Just !Int
bitIx -> (forall a. Bits a => a -> Int -> a
setBit a
w Int
bitIx, forall a. a -> Maybe a
Just (Int
ixBitSize forall a. Num a => a -> a -> a
* Int
i forall a. Num a => a -> a -> a
+ Int
bitIx))
{-# INLINE setNextZero #-}
ifindAtomicMutablePrimArray ::
MutablePrimArray s Int ->
(Int -> Int -> (Int, Maybe a)) ->
ST s (Maybe a)
ifindAtomicMutablePrimArray :: forall s a.
MutablePrimArray s Int
-> (Int -> Int -> (Int, Maybe a)) -> ST s (Maybe a)
ifindAtomicMutablePrimArray MutablePrimArray s Int
ma Int -> Int -> (Int, Maybe a)
f = do
Int
n <- forall (m :: * -> *) a.
(PrimMonad m, Prim a) =>
MutablePrimArray (PrimState m) a -> m Int
getSizeofMutablePrimArray MutablePrimArray s Int
ma
let go :: Int -> ST s (Maybe a)
go Int
i
| Int
i forall a. Ord a => a -> a -> Bool
>= Int
n = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing
| Bool
otherwise =
forall s a.
MutablePrimArray s Int -> Int -> (Int -> (Int, a)) -> ST s a
atomicModifyMutablePrimArray MutablePrimArray s Int
ma Int
i (Int -> Int -> (Int, Maybe a)
f Int
i) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe a
Nothing -> Int -> ST s (Maybe a)
go (Int
i forall a. Num a => a -> a -> a
+ Int
1)
Just a
a -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a
Just a
a
Int -> ST s (Maybe a)
go Int
0
{-# INLINE ifindAtomicMutablePrimArray #-}