@@ -2,6 +2,8 @@
2{-# LANGUAGE CPP #-}
2{-# LANGUAGE CPP #-}
3{-# LANGUAGE LambdaCase #-}
3{-# LANGUAGE LambdaCase #-}
4{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE OverloadedStrings #-}
5+{-# LANGUAGE MagicHash #-}
6+{-# LANGUAGE UnboxedTuples #-}
5-----------------------------------------------------------------------------
7-----------------------------------------------------------------------------
7-- Module : Language.Javascript.JSaddle.Run
9-- Module : Language.Javascript.JSaddle.Run
@@ -39,25 +41,30 @@ import Language.Javascript.JSaddle.Types (JSM)
39import qualified JavaScript.Web.AnimationFrame as GHCJS
41import qualified JavaScript.Web.AnimationFrame as GHCJS
40 (waitForAnimationFrame)
42 (waitForAnimationFrame)
42−import Control.Exception (throwIO)
44+import Control.Exception (throwIO, evaluate)
43import Control.Monad (void, when, zipWithM_)
45import Control.Monad (void, when, zipWithM_)
44import Control.Monad.IO.Class (MonadIO(..))
46import Control.Monad.IO.Class (MonadIO(..))
45import Control.Monad.Trans.Reader (ask, runReaderT)
47import Control.Monad.Trans.Reader (ask, runReaderT)
46import Control.Monad.STM (atomically)
48import Control.Monad.STM (atomically)
47−import Control.Concurrent (forkIO)
49+import Control.Concurrent (forkIO, myThreadId)
48import Control.Concurrent.STM.TChan
50import Control.Concurrent.STM.TChan
49 (tryReadTChan, TChan, readTChan, writeTChan, newTChanIO)
51 (tryReadTChan, TChan, readTChan, writeTChan, newTChanIO)
50import Control.Concurrent.STM.TVar
52import Control.Concurrent.STM.TVar
51 (writeTVar, readTVar, readTVarIO, modifyTVar', newTVarIO)
53 (writeTVar, readTVar, readTVarIO, modifyTVar', newTVarIO)
52import Control.Concurrent.MVar
54import Control.Concurrent.MVar
53− (tryTakeMVar, MVar, putMVar, takeMVar, newEmptyMVar, readMVar)
55+ (tryTakeMVar, MVar, putMVar, takeMVar, newMVar, newEmptyMVar, readMVar, modifyMVar)
55import System.IO.Unsafe (unsafeInterleaveIO)
57import System.IO.Unsafe (unsafeInterleaveIO)
56−import System.Mem.Weak (addFinalizer)
58+import System.Mem.Weak (addFinalizer, mkWeak)
60+import GHC.Int (Int64(..))
61+import GHC.Base (IO(..), mkWeak#)
62+import GHC.Conc (ThreadId(..))
58import Data.Monoid ((<>))
63import Data.Monoid ((<>))
59−import qualified Data.Text as T (unpack)
64+import qualified Data.Text as T (unpack, pack)
60import qualified Data.Map as M (lookup, delete, insert, empty, size)
65import qualified Data.Map as M (lookup, delete, insert, empty, size)
67+import qualified Data.Set as S (empty, member, insert, delete)
61import Data.UUID.V4 (nextRandom)
68import Data.UUID.V4 (nextRandom)
62import Data.Time.Clock (getCurrentTime,diffUTCTime)
69import Data.Time.Clock (getCurrentTime,diffUTCTime)
63import Data.IORef (newIORef, atomicWriteIORef, readIORef)
70import Data.IORef (newIORef, atomicWriteIORef, readIORef)
@@ -66,7 +73,6 @@ import Language.Javascript.JSaddle.Types
66 (Command(..), AsyncCommand(..), Result(..), BatchResults(..), Results(..), JSContextRef(..), JSVal(..),
73 (Command(..), AsyncCommand(..), Result(..), BatchResults(..), Results(..), JSContextRef(..), JSVal(..),
67 Object(..), JSValueReceived(..), JSM(..), Batch(..), JSValueForSend(..))
74 Object(..), JSValueReceived(..), JSM(..), Batch(..), JSValueForSend(..))
68import Language.Javascript.JSaddle.Exception (JSException(..))
75import Language.Javascript.JSaddle.Exception (JSException(..))
69−-- import Language.Javascript.JSaddle.Native.Internal (wrapJSVal)
70import Control.DeepSeq (deepseq)
76import Control.DeepSeq (deepseq)
71import GHC.Stats (getGCStatsEnabled, getGCStats, GCStats(..))
77import GHC.Stats (getGCStatsEnabled, getGCStats, GCStats(..))
@@ -137,7 +143,7 @@ sendLazyCommand cmd = do
137 nextRefTVar <- nextRef <$> JSM ask
143 nextRefTVar <- nextRef <$> JSM ask
138 n <- liftIO . atomically $ do
144 n <- liftIO . atomically $ do
139 n <- subtract 1 <$> readTVar nextRefTVar
145 n <- subtract 1 <$> readTVar nextRefTVar
140− writeTVar nextRefTVar n
146+ writeTVar nextRefTVar $! n
142 s <- doSendAsyncCommand <$> JSM ask
148 s <- doSendAsyncCommand <$> JSM ask
143 liftIO $ s (cmd $ JSValueForSend n)
149 liftIO $ s (cmd $ JSValueForSend n)
@@ -157,6 +163,7 @@ runJavaScript sendBatch entryPoint = do
157 commandChan <- newTChanIO
163 commandChan <- newTChanIO
158 callbacks <- newTVarIO M.empty
164 callbacks <- newTVarIO M.empty
159 nextRef' <- newTVarIO 0
165 nextRef' <- newTVarIO 0
166+ finalizerThreads <- newMVar S.empty
160 loggingEnabled <- newIORef False
167 loggingEnabled <- newIORef False
161 let ctx = JSContextRef {
168 let ctx = JSContextRef {
162 contextId = contextId'
169 contextId = contextId'
@@ -173,13 +180,14 @@ runJavaScript sendBatch entryPoint = do
173 , freeCallback = \(Object (JSVal val)) -> atomically $ modifyTVar' callbacks (M.delete val)
180 , freeCallback = \(Object (JSVal val)) -> atomically $ modifyTVar' callbacks (M.delete val)
174 , nextRef = nextRef'
181 , nextRef = nextRef'
175 , doEnableLogging = atomicWriteIORef loggingEnabled
182 , doEnableLogging = atomicWriteIORef loggingEnabled
183+ , finalizerThreads = finalizerThreads
177 let processResults :: Bool -> Results -> IO ()
185 let processResults :: Bool -> Results -> IO ()
178 processResults syncCallbacks = \case
186 processResults syncCallbacks = \case
179 (ProtocolError err) -> error $ "Protocol error : " <> T.unpack err
187 (ProtocolError err) -> error $ "Protocol error : " <> T.unpack err
180− (Callback n br f this a) -> do
188+ (Callback n br (JSValueReceived fNumber) f this a) -> do
181 putMVar recvMVar (n, br)
189 putMVar recvMVar (n, br)
182− f'@(JSVal fNumber) <- runReaderT (unJSM $ wrapJSVal f) ctx
190+ f' <- runReaderT (unJSM $ wrapJSVal f) ctx
183 this' <- runReaderT (unJSM $ wrapJSVal this) ctx
191 this' <- runReaderT (unJSM $ wrapJSVal this) ctx
184 args <- runReaderT (unJSM $ mapM wrapJSVal a) ctx
192 args <- runReaderT (unJSM $ mapM wrapJSVal a) ctx
185 logInfo (("Call " <> show fNumber <> " ") <>)
193 logInfo (("Call " <> show fNumber <> " ") <>)
@@ -208,7 +216,7 @@ runJavaScript sendBatch entryPoint = do
208 True -> show . currentBytesUsed <$> getGCStats
216 True -> show . currentBytesUsed <$> getGCStats
209 False -> return "??"
217 False -> return "??"
210 cbCount <- M.size <$> readTVarIO callbacks
218 cbCount <- M.size <$> readTVarIO callbacks
211− putStrLn . s $ "M " <> currentBytesUsedStr <> "CB " <> show cbCount
219+ putStrLn . s $ "M " <> currentBytesUsedStr <> " CB " <> show cbCount <> " "
212 False -> return ()
220 False -> return ()
213 _ <- forkIO . numberForeverFromM_ 1 $ \nBatch ->
221 _ <- forkIO . numberForeverFromM_ 1 $ \nBatch ->
214 readBatch nBatch commandChan >>= \case
222 readBatch nBatch commandChan >>= \case
@@ -264,12 +272,27 @@ runJavaScript sendBatch entryPoint = do
264 loopAnimation (Left asyncCmd) (cmds, resultMVars) =
272 loopAnimation (Left asyncCmd) (cmds, resultMVars) =
265 atomically (readTChan chan) >>= \cmd -> loopAnimation cmd (Left asyncCmd:cmds, resultMVars)
273 atomically (readTChan chan) >>= \cmd -> loopAnimation cmd (Left asyncCmd:cmds, resultMVars)
275+addThreadFinalizer :: ThreadId -> IO () -> IO ()
276+addThreadFinalizer t@(ThreadId t#) (IO finalizer) =
277+ IO $ \s -> case mkWeak# t# t finalizer s of { (# s1, _ #) -> (# s1, () #) }
267wrapJSVal :: JSValueReceived -> JSM JSVal
279wrapJSVal :: JSValueReceived -> JSM JSVal
268wrapJSVal (JSValueReceived ref) = do
280wrapJSVal (JSValueReceived ref) = do
269 -- TODO make sure this ref has not already been wrapped (perhaps only in debug version)
281 -- TODO make sure this ref has not already been wrapped (perhaps only in debug version)
270 let result = JSVal ref
282 let result = JSVal ref
271− when (ref >= 5) $ do
283+ when (ref >= 5 || ref < 0) $ do
272 ctx <- JSM ask
284 ctx <- JSM ask
285+ liftIO . addFinalizer ref $ do
286+ ft <- takeMVar $ finalizerThreads ctx
287+ t@(ThreadId t#) <- myThreadId
288+ let tname = T.pack $ show t
273− liftIO . addFinalizer result $ doSendAsyncCommand ctx $ FreeRef $ JSValueForSend ref
289+ doSendAsyncCommand ctx $ FreeRef tname $ JSValueForSend ref
290+ if tname `S.member` ft
291+ then putMVar (finalizerThreads ctx) ft
293+ addThreadFinalizer t $ do
294+ modifyMVar (finalizerThreads ctx) $ \s -> return (S.delete tname s, ())
295+ doSendAsyncCommand ctx $ FreeRefs tname
296+ putMVar (finalizerThreads ctx) =<< evaluate (S.insert tname ft)
274 return result
297 return result