{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UnboxedTuples #-}

-- | The goal of this Memory Pool is to provide the ability to allocate big chunks of
-- memory that can fit many `Block`s. Some memory allocators out there have a fairly large
-- minimal size requirement, which would be wasteful if many chunks of small size (eg. 32
-- bytes) are needed at once. Memory pool will allocate one page at a time as more blocks
-- is needed.
--
-- Currently there is no functionality for releasing unused pages. So, once a page is
-- allocated, it will be re-used when more `Block`s is needed, but it will not be GCed
-- until the whole `Pool` is GCed.
module Cardano.Memory.Pool (
  -- * Pool
  Pool,
  initPool,

  -- * Block
  Block (..),
  blockByteCount,
  grabNextBlock,

  -- * Helpers

  --
  -- Exported for testing
  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

-- | This is just a proxy type that carries information at the type level about the size
-- of the block in bytes supported by a particular instance of a `Pool`. Use
-- `blockByteCount` to get the byte size at the value level.
data Block (n :: Nat) = Block

-- | Number of bytes in a `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

-- | Internal helper type that manages each individual page. This is essentailly a mutable
-- linked list, which contains a memory buffer, a bit array that tracks which blocks in
-- the buffere are free and which ones are taken.
data Page n s = Page
  { forall (n :: Nat) s. Page n s -> ForeignPtr (Block n)
pageMemory :: !(ForeignPtr (Block n))
  -- ^ Contiguous memory buffer that holds all the blocks in the page.
  , forall (n :: Nat) s. Page n s -> MutablePrimArray s Int
pageBitArray :: !(MutablePrimArray s Int)
  -- ^ We use an Int array, because there are no built-in atomic primops for Word.
  , forall (n :: Nat) s. Page n s -> PVar Int s
pageFull :: !(PVar Int s)
  -- ^ This is a boolean flag which indicates when a page is full. It here as
  -- optimization only, because it allows us to skip iteration of the above bit
  -- array. It is an `Int` instead of a `Bool`, because GHC provides atomic primops for
  -- ByteArray, whcih is what `PVar` is based on.
  , forall (n :: Nat) s. Page n s -> MutVar s (Maybe (Page n s))
pageNextPage :: !(MutVar s (Maybe (Page n s)))
  -- ^ Link to the next page. Last page when this IORef contains `Nothing`
  }

-- | Thread-safe lock-free memory pool for managing large memory pages that contain of
-- many small `Block`s.
data Pool n s = Pool
  { forall (n :: Nat) s. Pool n s -> Page n s
poolFirstPage :: !(Page n s)
  -- ^ Initial page, which itself contains references to subsequent pages
  , forall (n :: Nat) s. Pool n s -> ST s (Page n s)
poolPageInitializer :: !(ST s (Page n s))
  -- ^ Page initializing action
  , forall (n :: Nat) s. Pool n s -> Ptr (Block n) -> IO ()
poolBlockFinalizer :: !(Ptr (Block n) -> IO ())
  -- ^ Finilizer that will be attached to each individual `ForeignPtr` of a reserved
  -- `Block`.
  }

-- | Useful function for testing. Check how many pages have been allocated thus far.
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)

-- | Initilizes the `Pool` that can be used for further allocation of @`ForeignPtr`
-- `Block` n@ with `grabNextBlock`.
initPool ::
  forall n s.
  KnownNat n =>
  -- | Number of groups per page. Must be a posititve number, otherwise error. One group
  -- contains as many blocks as the operating system has bits. A 64bit architecture will
  -- have 64 blocks per group. For example, if program is compiled on a 64 bit OS and you
  -- know ahead of time the maximum number of blocks that will be allocated through out
  -- the program, then the optimal value for this argument will @maxBlockNum/64@
  Int ->
  -- | Mempool page allocator. Some allocated pages might be immediately discarded,
  -- therefore number of pages utilized will not necessesarely match the number of times
  -- this action will be called.
  (forall a. Int -> ST s (ForeignPtr a)) ->
  -- | Finalizer to use for each block. It is an IO action because it will be executed by
  -- the Garbage Collector in a separate thread once the `Block` is no longer referenced.
  (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
      }

-- | Reserve a `ForeignPtr` of the `blockByteCount` size in the `Pool`. There is a default
-- finalizer attached to the `ForeignPtr` that will run `Block` pointer finalizer and
-- release that memory for re-use by other blocks allocated in the future. It is safe to
-- add more Haskell finalizers with `addForeignPtrConcFinalizer` if necessary.
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 #-}

-- | This is a helper function that will allocate a `Page` if the current `Page` in the
-- `Pool` is full. Whenever there are still block slots are available then supplied
-- @grabNext@ function will be used to reserve the slot in that `Page`.
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
              -- There is a slight chance of a race condition in that the next page could
              -- have been allocated and assigned to 'pageNextPage' by another thread
              -- since we last checked for it. This is not a problem since we can safely
              -- discard the page created in this thread and switch to the one that was
              -- assigned to 'pageNextPage'.
              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
                  -- Here we cleanup the newly allocated page in favor of the one that
                  -- was potentially created by another thread. It is important to
                  -- eagerly free up scarce resources.
                  --
                  -- This operation is idempotent and thread safe
                  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

-- | This is a helper function that will attempt to find the next available slot for the
-- `Block` and create a `ForeignPtr` with the size of `Block` in the `Page`. In case when
-- `Page` is full it will return `Nothing`.
grabNextPageForeignPtr ::
  forall n s.
  KnownNat n =>
  -- | Page to grab the block from
  Page n s ->
  -- | Finalizer to run, once the `ForeignPtr` holding on to `Ptr` `Block` is no longer used
  (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
    -- There is a slight chance that some Blocks will be cleared before the pageFull is
    -- set to True. This is not a problem because that memory will be recovered as soon as
    -- any other Block in the Page is finalized
    --
    -- TODO: Potentially verify that first Int in pageBitArray has all bits set, in
    -- order to prevent the degenerate case of all Blocks beeing finalized right before
    -- the page is marked as full.
    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 #-}

-- | Atomically AND an element of the array
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 #-}

-- | Atomically modify an element of the array
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 #-}

-- | Helper function that finds an index of the left-most bit that is not set.
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 #-}

-- | Finds an index of the next bit that is not set in the bit array and flips it
-- atomically. In case when all bits are set, then `Nothing` is returned. It is possible
-- that while search is ongoing bits that where checked get cleared. This is totally fine
-- for our implementation of mempool.
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 #-}