@@ -73,6 +73,7 @@ import Language.Javascript.JSaddle.Types
73import Language.Javascript.JSaddle.Exception (JSException(..))
73import Language.Javascript.JSaddle.Exception (JSException(..))
74import Control.DeepSeq (deepseq)
74import Control.DeepSeq (deepseq)
75import GHC.Stats (getGCStatsEnabled, getGCStats, GCStats(..))
75import GHC.Stats (getGCStatsEnabled, getGCStats, GCStats(..))
76+import Data.Foldable (forM_)
78-- | Enable (or disable) JSaddle logging
79-- | Enable (or disable) JSaddle logging
@@ -162,25 +163,26 @@ runJavaScript sendBatch entryPoint = do
162 callbacks <- newTVarIO M.empty
163 callbacks <- newTVarIO M.empty
163 nextRef' <- newTVarIO 0
164 nextRef' <- newTVarIO 0
164 finalizerThreads' <- newMVar S.empty
165 finalizerThreads' <- newMVar S.empty
166+ animationFrameHandlers' <- newMVar []
165 loggingEnabled <- newIORef False
167 loggingEnabled <- newIORef False
166 let ctx = JSContextRef {
168 let ctx = JSContextRef {
167− contextId = contextId'
169+ contextId = contextId'
168− , startTime = startTime'
170+ , startTime = startTime'
169− , doSendCommand = \cmd -> cmd `deepseq` do
171+ , doSendCommand = \cmd -> cmd `deepseq` do
170− result <- newEmptyMVar
172+ result <- newEmptyMVar
171− atomically $ writeTChan commandChan (Right (cmd, result))
173+ atomically $ writeTChan commandChan (Right (cmd, result))
172− unsafeInterleaveIO $
174+ unsafeInterleaveIO $
173− takeMVar result >>= \case
175+ takeMVar result >>= \case
174− (ThrowJSValue (JSValueReceived v)) -> throwIO $ JSException (JSVal v)
176+ (ThrowJSValue (JSValueReceived v)) -> throwIO $ JSException (JSVal v)
175− r -> return r
177+ r -> return r
176− , doSendAsyncCommand = \cmd -> cmd `deepseq` atomically (writeTChan commandChan $ Left cmd)
178+ , doSendAsyncCommand = \cmd -> cmd `deepseq` atomically (writeTChan commandChan $ Left cmd)
177− , addCallback = \(Object (JSVal val)) cb -> atomically $ modifyTVar' callbacks (M.insert val cb)
179+ , addCallback = \(Object (JSVal val)) cb -> atomically $ modifyTVar' callbacks (M.insert val cb)
178− , freeCallback = \(Object (JSVal val)) -> atomically $ modifyTVar' callbacks (M.delete val)
179− , nextRef = nextRef'
180+ , nextRef = nextRef'
180− , doEnableLogging = atomicWriteIORef loggingEnabled
181+ , doEnableLogging = atomicWriteIORef loggingEnabled
181− , finalizerThreads = finalizerThreads'
182+ , finalizerThreads = finalizerThreads'
183+ , animationFrameHandlers = animationFrameHandlers'
183− let processResults :: Bool -> Results -> IO ()
185+ processResults :: Bool -> Results -> IO ()
184 processResults syncCallbacks = \case
186 processResults syncCallbacks = \case
185 (ProtocolError err) -> error $ "Protocol error : " <> T.unpack err
187 (ProtocolError err) -> error $ "Protocol error : " <> T.unpack err
186 (Callback n br (JSValueReceived fNumber) f this a) -> do
188 (Callback n br (JSValueReceived fNumber) f this a) -> do
@@ -224,14 +226,20 @@ runJavaScript sendBatch entryPoint = do
224 putMVar lastAsyncBatch batch
226 putMVar lastAsyncBatch batch
225 sendBatch batch
227 sendBatch batch
226 takeResult recvMVar nBatch >>= \case
228 takeResult recvMVar nBatch >>= \case
227− (n, _) | n /= nBatch -> error $ "Unexpected jsaddle results (expected batch " <> show nBatch <> ", got batch " <> show n <> ")"
229+ (n, _) | n /= nBatch -> error $ "Unexpected jsaddle results (expected batch " <> show nBatch <> ", got batch " <> show n <> ")"
230+ (_, Success callbacksToFree results)
228− (_, Success results) | length results /= length resultMVars -> error "Unexpected number of jsaddle results"
231+ | length results /= length resultMVars -> error "Unexpected number of jsaddle results"
229− | otherwise -> zipWithM_ putMVar resultMVars results
233+ zipWithM_ putMVar resultMVars results
234+ forM_ callbacksToFree $ \(JSValueReceived val) ->
235+ atomically (modifyTVar' callbacks (M.delete val))
230− (_, Failure results exception) -> do
236+ (_, Failure callbacksToFree results exception) -> do
231− -- The exception will only be rethrown in Haskell if/when one of the
237+ -- The exception will only be rethrown in Haskell if/when one of the
232− -- missing results (if any) is evaluated.
238+ -- missing results (if any) is evaluated.
233− putStrLn "A JavaScript exception was thrown! (may not reach Haskell code)"
239+ putStrLn "A JavaScript exception was thrown! (may not reach Haskell code)"
234− zipWithM_ putMVar resultMVars $ results <> repeat (ThrowJSValue exception)
240+ zipWithM_ putMVar resultMVars $ results <> repeat (ThrowJSValue exception)
241+ forM_ callbacksToFree $ \(JSValueReceived val) ->
242+ atomically (modifyTVar' callbacks (M.delete val))
235 return (asyncResults, syncResults, runReaderT (unJSM entryPoint) ctx)
243 return (asyncResults, syncResults, runReaderT (unJSM entryPoint) ctx)
237 numberForeverFromM_ :: (Monad m, Enum n) => n -> (n -> m a) -> m ()
245 numberForeverFromM_ :: (Monad m, Enum n) => n -> (n -> m a) -> m ()