{-# 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
{ _weakBag_nextId :: {-# UNPACK #-} !(IORef Int)
, _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a)))
}
#endif
#ifdef GHCJS_FAST_WEAK
newtype FastWeakBagTicket a = FastWeakBagTicket JSVal
#else
data FastWeakBagTicket a = FastWeakBagTicket
{ _weakBagTicket_weakItem :: {-# UNPACK #-} !(Weak 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 nextId children) = {-# SCC "insert" #-} do
a' <- evaluate a
myId <- atomicModifyIORef' nextId $ \n -> (succ n, n)
let cleanup = atomicModifyIORef' children $ \cs -> (IntMap.delete myId cs, ())
wa <- mkWeakPtr a' $ Just cleanup
atomicModifyIORef' children $ \cs -> (IntMap.insert myId wa cs, ())
return $ FastWeakBagTicket
{ _weakBagTicket_weakItem = wa
, _weakBagTicket_item = 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 = {-# SCC "empty" #-} do
nextId <- newIORef 1
children <- newIORef IntMap.empty
let bag = FastWeakBag
{ _weakBag_nextId = nextId
, _weakBag_children = children
}
return 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 bag = {-# SCC "isEmpty" #-} IntMap.null <$> readIORef (_weakBag_children 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 _ children) f = {-# SCC "traverse_" #-} do
cs <- liftIO $ readIORef children
forM_ cs $ \c -> do
ma <- liftIO $ deRefWeak c
mapM_ f ma
#endif
{-# DEPRECATED traverse "Use 'traverse_' instead" #-}
traverse :: forall a m. MonadIO m => FastWeakBag a -> (a -> m ()) -> m ()
traverse = 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 w _) = {-# SCC "remove" #-} finalize w
#endif