{-# OPTIONS_GHC -fno-implicit-prelude #-}
--------------------------------------------------------------------------------
-- |
-- Module : Foreign.Marshal.Pool
-- Copyright : (c) Sven Panne 2002-2004
-- License : BSD-style (see the file libraries/base/LICENSE)
--
-- Maintainer : sven.panne@aedion.de
-- Stability : provisional
-- Portability : portable
--
-- This module contains support for pooled memory management. Under this scheme,
-- (re-)allocations belong to a given pool, and everything in a pool is
-- deallocated when the pool itself is deallocated. This is useful when
-- 'Foreign.Marshal.Alloc.alloca' with its implicit allocation and deallocation
-- is not flexible enough, but explicit uses of 'Foreign.Marshal.Alloc.malloc'
-- and 'free' are too awkward.
--
--------------------------------------------------------------------------------
module Foreign.Marshal.Pool (
-- * Pool management
Pool,
newPool, -- :: IO Pool
freePool, -- :: Pool -> IO ()
withPool, -- :: (Pool -> IO b) -> IO b
-- * (Re-)Allocation within a pool
pooledMalloc, -- :: Storable a => Pool -> IO (Ptr a)
pooledMallocBytes, -- :: Pool -> Int -> IO (Ptr a)
pooledRealloc, -- :: Storable a => Pool -> Ptr a -> IO (Ptr a)
pooledReallocBytes, -- :: Pool -> Ptr a -> Int -> IO (Ptr a)
pooledMallocArray, -- :: Storable a => Pool -> Int -> IO (Ptr a)
pooledMallocArray0, -- :: Storable a => Pool -> Int -> IO (Ptr a)
pooledReallocArray, -- :: Storable a => Pool -> Ptr a -> Int -> IO (Ptr a)
pooledReallocArray0, -- :: Storable a => Pool -> Ptr a -> Int -> IO (Ptr a)
-- * Combined allocation and marshalling
pooledNew, -- :: Storable a => Pool -> a -> IO (Ptr a)
pooledNewArray, -- :: Storable a => Pool -> [a] -> IO (Ptr a)
pooledNewArray0 -- :: Storable a => Pool -> a -> [a] -> IO (Ptr a)
) where
#ifdef __GLASGOW_HASKELL__
import GHC.Base ( Int, Monad(..), (.), not )
import GHC.Err ( undefined )
import GHC.Exception ( block, unblock, throw, catchException )
import GHC.IOBase ( IO, IORef, newIORef, readIORef, writeIORef, )
import GHC.List ( elem, length )
import GHC.Num ( Num(..) )
#else
import Data.IORef ( IORef, newIORef, readIORef, writeIORef )
#if defined(__NHC__)
import IO ( bracket )
#else
import Control.Exception ( bracket )
#endif
#endif
import Control.Monad ( liftM )
import Data.List ( delete )
import Foreign.Marshal.Alloc ( mallocBytes, reallocBytes, free )
import Foreign.Marshal.Array ( pokeArray, pokeArray0 )
import Foreign.Marshal.Error ( throwIf )
import Foreign.Ptr ( Ptr, castPtr )
import Foreign.Storable ( Storable(sizeOf, poke) )
--------------------------------------------------------------------------------
-- To avoid non-H98 stuff like existentially quantified data constructors, we
-- simply use pointers to () below. Not very nice, but...
-- | A memory pool.
newtype Pool = Pool (IORef [Ptr ()])
-- | Allocate a fresh memory pool.
newPool :: IO Pool
newPool = liftM Pool (newIORef [])
-- | Deallocate a memory pool and everything which has been allocated in the
-- pool itself.
freePool :: Pool -> IO ()
freePool (Pool pool) = readIORef pool >>= freeAll
where freeAll [] = return ()
freeAll (p:ps) = free p >> freeAll ps
-- | Execute an action with a fresh memory pool, which gets automatically
-- deallocated (including its contents) after the action has finished.
withPool :: (Pool -> IO b) -> IO b
#ifdef __GLASGOW_HASKELL__
withPool act = -- ATTENTION: cut-n-paste from Control.Exception below!
block (do
pool <- newPool
val <- catchException
(unblock (act pool))
(\e -> do freePool pool; throw e)
freePool pool
return val)
#else
withPool = bracket newPool freePool
#endif
--------------------------------------------------------------------------------
-- | Allocate space for storable type in the given pool. The size of the area
-- allocated is determined by the 'sizeOf' method from the instance of
-- 'Storable' for the appropriate type.
pooledMalloc :: Storable a => Pool -> IO (Ptr a)
pooledMalloc = pm undefined
where
pm :: Storable a' => a' -> Pool -> IO (Ptr a')
pm dummy pool = pooledMallocBytes pool (sizeOf dummy)
-- | Allocate the given number of bytes of storage in the pool.
pooledMallocBytes :: Pool -> Int -> IO (Ptr a)
pooledMallocBytes (Pool pool) size = do
ptr <- mallocBytes size
ptrs <- readIORef pool
writeIORef pool (ptr:ptrs)
return (castPtr ptr)
-- | Adjust the storage area for an element in the pool to the given size of
-- the required type.
pooledRealloc :: Storable a => Pool -> Ptr a -> IO (Ptr a)
pooledRealloc = pr undefined
where
pr :: Storable a' => a' -> Pool -> Ptr a' -> IO (Ptr a')
pr dummy pool ptr = pooledReallocBytes pool ptr (sizeOf dummy)
-- | Adjust the storage area for an element in the pool to the given size.
pooledReallocBytes :: Pool -> Ptr a -> Int -> IO (Ptr a)
pooledReallocBytes (Pool pool) ptr size = do
let cPtr = castPtr ptr
throwIf (not . (cPtr `elem`)) (\_ -> "pointer not in pool") (readIORef pool)
newPtr <- reallocBytes cPtr size
ptrs <- readIORef pool
writeIORef pool (newPtr : delete cPtr ptrs)
return (castPtr newPtr)
-- | Allocate storage for the given number of elements of a storable type in the
-- pool.
pooledMallocArray :: Storable a => Pool -> Int -> IO (Ptr a)
pooledMallocArray = pma undefined
where
pma :: Storable a' => a' -> Pool -> Int -> IO (Ptr a')
pma dummy pool size = pooledMallocBytes pool (size * sizeOf dummy)
-- | Allocate storage for the given number of elements of a storable type in the
-- pool, but leave room for an extra element to signal the end of the array.
pooledMallocArray0 :: Storable a => Pool -> Int -> IO (Ptr a)
pooledMallocArray0 pool size =
pooledMallocArray pool (size + 1)
-- | Adjust the size of an array in the given pool.
pooledReallocArray :: Storable a => Pool -> Ptr a -> Int -> IO (Ptr a)
pooledReallocArray = pra undefined
where
pra :: Storable a' => a' -> Pool -> Ptr a' -> Int -> IO (Ptr a')
pra dummy pool ptr size = pooledReallocBytes pool ptr (size * sizeOf dummy)
-- | Adjust the size of an array with an end marker in the given pool.
pooledReallocArray0 :: Storable a => Pool -> Ptr a -> Int -> IO (Ptr a)
pooledReallocArray0 pool ptr size =
pooledReallocArray pool ptr (size + 1)
--------------------------------------------------------------------------------
-- | Allocate storage for a value in the given pool and marshal the value into
-- this storage.
pooledNew :: Storable a => Pool -> a -> IO (Ptr a)
pooledNew pool val = do
ptr <- pooledMalloc pool
poke ptr val
return ptr
-- | Allocate consecutive storage for a list of values in the given pool and
-- marshal these values into it.
pooledNewArray :: Storable a => Pool -> [a] -> IO (Ptr a)
pooledNewArray pool vals = do
ptr <- pooledMallocArray pool (length vals)
pokeArray ptr vals
return ptr
-- | Allocate consecutive storage for a list of values in the given pool and
-- marshal these values into it, terminating the end with the given marker.
pooledNewArray0 :: Storable a => Pool -> a -> [a] -> IO (Ptr a)
pooledNewArray0 pool marker vals = do
ptr <- pooledMallocArray0 pool (length vals)
pokeArray0 marker ptr vals
return ptr