{-# LANGUAGE CPP #-}
{-# LANGUAGE ExistentialQuantification #-}
#ifdef USE_REFLEX_OPTIMIZER
{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
#endif
#ifdef GHCJS_FAST_WEAK
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE JavaScriptFFI #-}
#endif
module Data.FastWeakBag
( FastWeakBag
, FastWeakBagTicket
, empty
, isEmpty
, insert
, traverse
, traverse_
, remove
#ifndef GHCJS_FAST_WEAK
, _weakBag_children
#endif
) where
import Prelude hiding (traverse)
import Control.Monad
import Control.Monad.IO.Class
#ifdef GHCJS_FAST_WEAK
import GHCJS.Types
import Reflex.FastWeak (js_isNull, unsafeFromRawJSVal, unsafeToRawJSVal)
#else
import Control.Exception
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IntMap
import Data.IORef
import System.Mem.Weak
#endif
#ifdef GHCJS_FAST_WEAK
newtype FastWeakBag a = FastWeakBag JSVal
#else
data FastWeakBag a = FastWeakBag
{ FastWeakBag a -> IORef Int
_weakBag_nextId :: {-# UNPACK #-} !(IORef Int)
, FastWeakBag a -> IORef (IntMap (Weak a))
_weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a)))
}
#endif
#ifdef GHCJS_FAST_WEAK
newtype FastWeakBagTicket a = FastWeakBagTicket JSVal
#else
data FastWeakBagTicket a = FastWeakBagTicket
{ FastWeakBagTicket a -> Weak a
_weakBagTicket_weakItem :: {-# UNPACK #-} !(Weak a)
, FastWeakBagTicket a -> a
_weakBagTicket_item :: {-# NOUNPACK #-} !a
}
#endif
{-# INLINE insert #-}
insert :: a
-> FastWeakBag a
-> IO (FastWeakBagTicket a)
#ifdef GHCJS_FAST_WEAK
insert a wb = js_insert (unsafeToRawJSVal a) wb
foreign import javascript unsafe "$r = new h$FastWeakBagTicket($2, $1);" js_insert :: JSVal -> FastWeakBag a -> IO (FastWeakBagTicket a)
#else
insert :: a -> FastWeakBag a -> IO (FastWeakBagTicket a)
insert a
a (FastWeakBag IORef Int
nextId IORef (IntMap (Weak a))
children) = {-# SCC "insert" #-} do
a
a' <- a -> IO a
forall a. a -> IO a
evaluate a
a
Int
myId <- IORef Int -> (Int -> (Int, Int)) -> IO Int
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef Int
nextId ((Int -> (Int, Int)) -> IO Int) -> (Int -> (Int, Int)) -> IO Int
forall a b. (a -> b) -> a -> b
$ \Int
n -> (Int -> Int
forall a. Enum a => a -> a
succ Int
n, Int
n)
let cleanup :: IO ()
cleanup = IORef (IntMap (Weak a))
-> (IntMap (Weak a) -> (IntMap (Weak a), ())) -> IO ()
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef (IntMap (Weak a))
children ((IntMap (Weak a) -> (IntMap (Weak a), ())) -> IO ())
-> (IntMap (Weak a) -> (IntMap (Weak a), ())) -> IO ()
forall a b. (a -> b) -> a -> b
$ \IntMap (Weak a)
cs -> (Int -> IntMap (Weak a) -> IntMap (Weak a)
forall a. Int -> IntMap a -> IntMap a
IntMap.delete Int
myId IntMap (Weak a)
cs, ())
Weak a
wa <- a -> Maybe (IO ()) -> IO (Weak a)
forall k. k -> Maybe (IO ()) -> IO (Weak k)
mkWeakPtr a
a' (Maybe (IO ()) -> IO (Weak a)) -> Maybe (IO ()) -> IO (Weak a)
forall a b. (a -> b) -> a -> b
$ IO () -> Maybe (IO ())
forall a. a -> Maybe a
Just IO ()
cleanup
IORef (IntMap (Weak a))
-> (IntMap (Weak a) -> (IntMap (Weak a), ())) -> IO ()
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef (IntMap (Weak a))
children ((IntMap (Weak a) -> (IntMap (Weak a), ())) -> IO ())
-> (IntMap (Weak a) -> (IntMap (Weak a), ())) -> IO ()
forall a b. (a -> b) -> a -> b
$ \IntMap (Weak a)
cs -> (Int -> Weak a -> IntMap (Weak a) -> IntMap (Weak a)
forall a. Int -> a -> IntMap a -> IntMap a
IntMap.insert Int
myId Weak a
wa IntMap (Weak a)
cs, ())
FastWeakBagTicket a -> IO (FastWeakBagTicket a)
forall (m :: * -> *) a. Monad m => a -> m a
return (FastWeakBagTicket a -> IO (FastWeakBagTicket a))
-> FastWeakBagTicket a -> IO (FastWeakBagTicket a)
forall a b. (a -> b) -> a -> b
$ FastWeakBagTicket :: forall a. Weak a -> a -> FastWeakBagTicket a
FastWeakBagTicket
{ _weakBagTicket_weakItem :: Weak a
_weakBagTicket_weakItem = Weak a
wa
, _weakBagTicket_item :: a
_weakBagTicket_item = a
a'
}
#endif
{-# INLINE empty #-}
empty :: IO (FastWeakBag a)
#ifdef GHCJS_FAST_WEAK
empty = js_empty
foreign import javascript unsafe "$r = new h$FastWeakBag();" js_empty :: IO (FastWeakBag a)
#else
empty :: IO (FastWeakBag a)
empty = {-# SCC "empty" #-} do
IORef Int
nextId <- Int -> IO (IORef Int)
forall a. a -> IO (IORef a)
newIORef Int
1
IORef (IntMap (Weak a))
children <- IntMap (Weak a) -> IO (IORef (IntMap (Weak a)))
forall a. a -> IO (IORef a)
newIORef IntMap (Weak a)
forall a. IntMap a
IntMap.empty
let bag :: FastWeakBag a
bag = FastWeakBag :: forall a. IORef Int -> IORef (IntMap (Weak a)) -> FastWeakBag a
FastWeakBag
{ _weakBag_nextId :: IORef Int
_weakBag_nextId = IORef Int
nextId
, _weakBag_children :: IORef (IntMap (Weak a))
_weakBag_children = IORef (IntMap (Weak a))
children
}
FastWeakBag a -> IO (FastWeakBag a)
forall (m :: * -> *) a. Monad m => a -> m a
return FastWeakBag a
bag
#endif
{-# INLINE isEmpty #-}
isEmpty :: FastWeakBag a -> IO Bool
#ifdef GHCJS_FAST_WEAK
isEmpty = js_isEmpty
foreign import javascript unsafe "(function(){ for(var i = 0; i < $1.tickets.length; i++) { if($1.tickets[i] !== null) { return false; } }; return true; })()" js_isEmpty :: FastWeakBag a -> IO Bool
#else
isEmpty :: FastWeakBag a -> IO Bool
isEmpty FastWeakBag a
bag = {-# SCC "isEmpty" #-} IntMap (Weak a) -> Bool
forall a. IntMap a -> Bool
IntMap.null (IntMap (Weak a) -> Bool) -> IO (IntMap (Weak a)) -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IORef (IntMap (Weak a)) -> IO (IntMap (Weak a))
forall a. IORef a -> IO a
readIORef (FastWeakBag a -> IORef (IntMap (Weak a))
forall a. FastWeakBag a -> IORef (IntMap (Weak a))
_weakBag_children FastWeakBag a
bag)
#endif
{-# INLINE traverse_ #-}
traverse_ :: forall a m. MonadIO m => FastWeakBag a -> (a -> m ()) -> m ()
#ifdef GHCJS_FAST_WEAK
traverse_ wb f = do
let go cursor = when (not $ js_isNull cursor) $ do
val <- liftIO $ js_getTicketValue cursor
f $ unsafeFromRawJSVal val
go =<< liftIO (js_getNext (FastWeakBagTicket cursor))
go =<< liftIO (js_getInitial wb)
foreign import javascript unsafe "(function(){ for(var i = $1.tickets.length - 1; i >= 0; i--) { if($1.tickets[i] !== null) { return $1.tickets[i]; } }; return null; })()" js_getInitial :: FastWeakBag a -> IO JSVal
foreign import javascript unsafe "$r = $1.val;" js_getTicketValue :: JSVal -> IO JSVal
foreign import javascript unsafe "(function(){ for(var i = $1.pos - 1; i >= 0; i--) { if($1.bag.tickets[i] !== null) { return $1.bag.tickets[i]; } }; return null; })()" js_getNext :: FastWeakBagTicket a -> IO JSVal
#else
traverse_ :: FastWeakBag a -> (a -> m ()) -> m ()
traverse_ (FastWeakBag IORef Int
_ IORef (IntMap (Weak a))
children) a -> m ()
f = {-# SCC "traverse_" #-} do
IntMap (Weak a)
cs <- IO (IntMap (Weak a)) -> m (IntMap (Weak a))
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (IntMap (Weak a)) -> m (IntMap (Weak a)))
-> IO (IntMap (Weak a)) -> m (IntMap (Weak a))
forall a b. (a -> b) -> a -> b
$ IORef (IntMap (Weak a)) -> IO (IntMap (Weak a))
forall a. IORef a -> IO a
readIORef IORef (IntMap (Weak a))
children
IntMap (Weak a) -> (Weak a -> m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ IntMap (Weak a)
cs ((Weak a -> m ()) -> m ()) -> (Weak a -> m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ \Weak a
c -> do
Maybe a
ma <- IO (Maybe a) -> m (Maybe a)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe a) -> m (Maybe a)) -> IO (Maybe a) -> m (Maybe a)
forall a b. (a -> b) -> a -> b
$ Weak a -> IO (Maybe a)
forall v. Weak v -> IO (Maybe v)
deRefWeak Weak a
c
(a -> m ()) -> Maybe a -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ a -> m ()
f Maybe a
ma
#endif
{-# DEPRECATED traverse "Use 'traverse_' instead" #-}
traverse :: forall a m. MonadIO m => FastWeakBag a -> (a -> m ()) -> m ()
traverse :: FastWeakBag a -> (a -> m ()) -> m ()
traverse = FastWeakBag a -> (a -> m ()) -> m ()
forall a (m :: * -> *).
MonadIO m =>
FastWeakBag a -> (a -> m ()) -> m ()
traverse_
{-# INLINE remove #-}
remove :: FastWeakBagTicket a -> IO ()
#ifdef GHCJS_FAST_WEAK
remove = js_remove
foreign import javascript unsafe "$1.bag.tickets[$1.pos] = null; $1.bag = new h$FastWeakBag(); $1.bag.tickets.push($1); $1.pos = 0;" js_remove :: FastWeakBagTicket a -> IO ()
#else
remove :: FastWeakBagTicket a -> IO ()
remove (FastWeakBagTicket Weak a
w a
_) = {-# SCC "remove" #-} Weak a -> IO ()
forall v. Weak v -> IO ()
finalize Weak a
w
#endif