module Data.Concurrent.LinkedMap (
LMap(), LMList(..),
newLMap, Token(), value, find, FindResult(..), tryInsert,
foldlWithKey, map, reverse, head, toList, fromList, findIndex,
halve, halve', dropUntil
)
where
import Data.IORef
import Data.Atomics
import Control.Reagent
import Control.Monad.IO.Class
import Control.Exception (assert)
import Prelude hiding (reverse, map, head)
data LMList k v =
Node k v !(IORef (LMList k v))
| Empty
type LMap k v = IORef (LMList k v)
newLMap :: IO (LMap k v)
newLMap = newIORef Empty
data Token k v = Token {
keyToInsert :: k,
value :: Maybe v,
nextRef :: IORef (LMList k v),
nextTicket :: Ticket (LMList k v)
}
data FindResult k v =
Found v
| NotFound (Token k v)
find :: Ord k => LMap k v -> k -> IO (FindResult k v)
find m k = findInner m Nothing
where
findInner m v = do
nextTicket <- readForCAS m
let stopHere = NotFound $ Token {keyToInsert = k, value = v, nextRef = m, nextTicket}
case peekTicket nextTicket of
Empty -> return stopHere
Node k' v' next ->
case compare k k' of
LT -> return stopHere
EQ -> return $ Found v'
GT -> findInner next (Just v')
tryInsert :: Token k v -> v -> IO (Maybe (LMap k v))
tryInsert Token { keyToInsert, nextRef, nextTicket } v = do
newRef <- newIORef $ peekTicket nextTicket
(success, _) <- casIORef nextRef nextTicket $ Node keyToInsert v newRef
return $ if success then Just nextRef else Nothing
foldlWithKey :: Monad m => (forall x . IO x -> m x) ->
(a -> k -> v -> m a) -> a -> LMap k v -> m a
foldlWithKey liftIO f !a !m = do
n <- liftIO$ readIORef m
case n of
Empty -> return a
Node k v next -> do
a' <- f a k v
foldlWithKey liftIO f a' next
map :: MonadIO m => (a -> b) -> LMap k a -> m (LMap k b)
map fn mp = do
tmp <- foldlWithKey liftIO
(\ acc k v -> do
r <- liftIO (newIORef acc)
return$! Node k (fn v) r)
Empty mp
tmp' <- liftIO (newIORef tmp)
reverse tmp'
reverse :: MonadIO m => LMap k v -> m (LMap k v)
reverse mp = liftIO . newIORef =<< loop Empty mp
where
loop !acc mp = do
n <- liftIO$ readIORef mp
case n of
Empty -> return acc
Node k v next -> do
r <- liftIO (newIORef acc)
loop (Node k v r) next
head :: LMap k v -> IO (Maybe k)
head lm = do
x <- readIORef lm
case x of
Empty -> return Nothing
Node k _ _ -> return $! Just k
toList :: LMap k v -> IO [(k,v)]
toList lm = do
x <- readIORef lm
case x of
Empty -> return []
Node k v tl -> do
ls <- toList tl
return $! (k,v) : ls
fromList :: [(k,v)] -> IO (LMap k v)
fromList ls = do
let loop [] = return Empty
loop ((k,v):tl) = do
tl' <- loop tl
ref <- newIORef tl'
return $! Node k v ref
lm <- loop ls
newIORef lm
halve' :: Ord k => Maybe k -> LMap k v -> IO (Maybe (LMap k v, LMap k v))
halve' mend lm = do
lml <- readIORef lm
res <- halve mend lml
case res of
Nothing -> return Nothing
Just (len1,_len2,tailhd) -> do
ls <- toList lm
l' <- fromList (take len1 ls)
r' <- newIORef tailhd
return $! Just $! (l',r')
halve :: Ord k => Maybe k -> LMList k v -> IO (Maybe (Int, Int, LMList k v))
halve mend ls = loop 0 ls ls
where
isEnd Empty = True
isEnd (Node k _ _) =
case mend of
Just end -> k >= end
Nothing -> False
emptCheck (0,l2,t) = return Nothing
emptCheck !x = return $! Just x
loop len tort hare | isEnd hare =
emptCheck (len, len, tort)
loop len tort@(Node _ _ next1) (Node k v next2) = do
next2' <- readIORef next2
case next2' of
x | isEnd x -> emptCheck (len, len+1, tort)
Node _ _ next3 -> do next1' <- readIORef next1
next3' <- readIORef next3
loop (len+1) next1' next3'
dropUntil :: Ord k => k -> LMList k v -> IO (LMList k v)
dropUntil _ Empty = return Empty
dropUntil stop nd@(Node k v tl)
| stop <= k = return nd
| otherwise = do tl' <- readIORef tl
dropUntil stop tl'
findIndex :: Eq k => LMList k v -> LMList k v -> IO (Maybe Int)
findIndex ls1 ls2 =
error "FINISHME - LinkedMap.findIndex"