reactor-0.1.3: Reactor/Deque.hs
{-# LANGUAGE UndecidableInstances, FlexibleContexts, DeriveDataTypeable #-}
module Reactor.Deque (
Deque
-- * Local stack operations
, empty -- :: (MonadIO m, MArray a e IO) => IO (Deque a e)
, push -- :: (MonadIO m, MArray a e IO) => e -> Deque a e -> IO ()
, pop -- :: (MonadIO m, MArray a e IO) => Deque a e -> IO (Maybe e)
-- * Performance tuning
, withCapacity -- :: (MonadIO m, MArray a e IO) => Int -> IO (Deque a e)
, minimumCapacity -- :: Int
, defaultCapacity -- :: Int
-- * Work stealing
, steal -- :: (MonadIO m, MArray a e IO) => Deque a e -> IO (Stolen e)
, Stolen(..)
) where
-- | For an explanation of the implementation, see \"Dynamic Circular Work-Stealing Deque\"
-- by David Chase and Yossi Lev of Sun Microsystems.
import Prelude hiding (read)
import Control.Applicative hiding (empty)
import Data.Bits.Atomic
import Foreign.Ptr
import Foreign.ForeignPtr
import Foreign.Storable
import Data.IORef
import Data.Array.MArray
import Control.Monad
import Control.Monad.IO.Class
import Data.Data
import System.IO.Unsafe
data Buffer a e = Buffer {-# UNPACK #-} !Int !(a Int e)
instance Typeable2 a => Typeable1 (Buffer a) where
typeOf1 tae = mkTyConApp bufferTyCon [typeOf1 (aInte tae)]
where aInte :: t a e -> a Int e
aInte = undefined
bufferTyCon :: TyCon
bufferTyCon = mkTyCon "Reactor.Deque.Buffer"
size :: Buffer a e -> Int
size (Buffer i _) = i
data Deque a e = Deque
{ _tb :: ForeignPtr Int
, _content :: IORef (Buffer a e)
}
instance (MArray a e IO, Show e) => Show (Deque a e) where
showsPrec d (Deque tb content) = unsafePerformIO $ do
(t,b) <- withForeignPtr tb $ \p -> (,) <$> peekTop p <*> peekBottom p
buffer <- readIORef content
contents <- forM [t..b-1] (read buffer)
return $ showParen (d > 10) $
showString "Deque (ptr " . showsPrec 11 t . showChar ' ' . showsPrec 11 b . showString ") (buffer " . showsPrec 11 contents . showChar ')'
instance Typeable2 a => Typeable1 (Deque a) where
typeOf1 dae = mkTyConApp dequeTyCon [typeOf1 (aInte dae)]
where aInte :: t a e -> a Int e
aInte = undefined
dequeTyCon :: TyCon
dequeTyCon = mkTyCon "Reactor.Deque.Deque"
ptr :: Storable a => a -> a -> IO (ForeignPtr a)
ptr a b = do
p <- mallocForeignPtrArray 2
withForeignPtr p $ \q -> do
poke q a
pokeElemOff q 1 b
return p
minimumCapacity :: Int
minimumCapacity = 16
defaultCapacity :: Int
defaultCapacity = 32
bufferWithCapacity :: MArray a e IO => Int -> IO (Buffer a e)
bufferWithCapacity i =
Buffer i <$> newArray_ (0, (minimumCapacity `max` i) - 1)
withCapacity :: (MonadIO m, MArray a e IO) => Int -> m (Deque a e)
withCapacity i = liftIO (Deque <$> ptr 0 0 <*> (bufferWithCapacity i >>= newIORef))
empty :: (MonadIO m, MArray a e IO) => m (Deque a e)
empty = withCapacity defaultCapacity
{-# INLINE empty #-}
-- unsafeRead
read :: MArray a e IO => Buffer a e -> Int -> IO e
read (Buffer s c) i = do
readArray c (i `mod` s)
{-# INLINE read #-}
-- unsafeWrite
write :: MArray a e IO => Buffer a e -> Int -> e -> IO ()
write (Buffer s c) i e = do
writeArray c (i `mod` s) e
{-# INLINE write #-}
grow :: MArray a e IO => Buffer a e -> Int -> Int -> IO (Buffer a e)
grow c b t = do
c' <- bufferWithCapacity (size c * 2)
forM_ [t..b-1] $ \i -> read c i >>= write c' i
return c'
{-# INLINE grow #-}
peekBottom :: Ptr Int -> IO Int
peekBottom p = peekElemOff p 1
peekTop :: Ptr Int -> IO Int
peekTop p = peek p
pokeBottom :: Ptr Int -> Int -> IO ()
pokeBottom p = pokeElemOff p 1
push :: (MonadIO m, MArray a e IO) => e -> Deque a e -> m ()
push o (Deque tb content) = liftIO $ withForeignPtr tb $ \p -> do
b <- peekBottom p
t <- peekTop p
a <- readIORef content
let size' = b - t
if size' >= size a
then do
a' <- grow a b t
writeIORef content a'
go p a' b
else go p a b
where
go p arr b = do
write arr b o
pokeBottom p (b + 1)
data Stolen e
= Empty
| Abort
| Stolen e
deriving (Data,Typeable,Eq,Ord,Show,Read)
steal :: (MonadIO m, MArray a e IO) => Deque a e -> m (Stolen e)
steal (Deque tb content) = liftIO $ withForeignPtr tb $ \p -> do
t <- peekTop p
b <- peekBottom p
a <- readIORef content
let size' = b - t
if size' <= 0
then return Empty
else do
o <- read a t
result <- compareAndSwapBool p t (t + 1)
return $ if result then Stolen o else Abort
{-
steal' :: MArray a e IO => Deque a e -> IO (Maybe e)
steal' deque = do
s <- steal deque
case s of
Stolen e -> return (Just e)
Empty -> return Nothing
Abort -> steal' deque
-}
pop :: (MonadIO m, MArray a e IO) => Deque a e -> m (Maybe e)
pop (Deque tb content) = liftIO $ withForeignPtr tb $ \p -> do
b <- peekBottom p
a <- readIORef content
let b' = b - 1
pokeBottom p b'
t <- peekTop p
let size' = b' - t
if size' < 0
then do
pokeBottom p t
return Nothing
else do
o <- read a b'
if size' > 0
then return (Just o)
else do
result <- compareAndSwapBool p t (t + 1)
if result
then do
pokeBottom p (t + 1)
return (Just o)
else do
pokeBottom p (t + 1)
return Nothing