@@ -1,3 +1,4 @@
1+{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE CPP #-}
2{-# LANGUAGE LambdaCase #-}
3{-# LANGUAGE LambdaCase #-}
3{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE OverloadedStrings #-}
@@ -49,7 +50,7 @@ import Control.Concurrent.STM.TChan
49import Control.Concurrent.STM.TVar
50import Control.Concurrent.STM.TVar
50 (writeTVar, readTVar, readTVarIO, modifyTVar', newTVarIO)
51 (writeTVar, readTVar, readTVarIO, modifyTVar', newTVarIO)
51import Control.Concurrent.MVar
52import Control.Concurrent.MVar
52− (tryTakeMVar, MVar, putMVar, takeMVar, newEmptyMVar)
53+ (tryTakeMVar, MVar, putMVar, takeMVar, newEmptyMVar, readMVar)
54import System.IO.Unsafe (unsafeInterleaveIO)
55import System.IO.Unsafe (unsafeInterleaveIO)
55import System.Mem.Weak (addFinalizer)
56import System.Mem.Weak (addFinalizer)
@@ -61,7 +62,7 @@ import Data.Time.Clock (getCurrentTime,diffUTCTime)
61import Data.IORef (newIORef, atomicWriteIORef, readIORef)
62import Data.IORef (newIORef, atomicWriteIORef, readIORef)
63import Language.Javascript.JSaddle.Types
64import Language.Javascript.JSaddle.Types
64− (Command(..), AsyncCommand(..), Result(..), Results(..), JSContextRef(..), JSVal(..),
65+ (Command(..), AsyncCommand(..), Result(..), BatchResults(..), Results(..), JSContextRef(..), JSVal(..),
65 Object(..), JSValueReceived(..), JSM(..), Batch(..), JSValueForSend(..))
66 Object(..), JSValueReceived(..), JSM(..), Batch(..), JSValueForSend(..))
66import Language.Javascript.JSaddle.Exception (JSException(..))
67import Language.Javascript.JSaddle.Exception (JSException(..))
67-- import Language.Javascript.JSaddle.Native.Internal (wrapJSVal)
68-- import Language.Javascript.JSaddle.Native.Internal (wrapJSVal)
@@ -152,7 +153,6 @@ runJavaScript :: (Batch -> IO ()) -> JSM () -> IO (Results -> IO (), Results ->
152runJavaScript sendBatch entryPoint = do
153runJavaScript sendBatch entryPoint = do
153 startTime' <- getCurrentTime
154 startTime' <- getCurrentTime
154 recvMVar <- newEmptyMVar
155 recvMVar <- newEmptyMVar
155− syncSendBatch <- newEmptyMVar
156 lastAsyncBatch <- newEmptyMVar
156 lastAsyncBatch <- newEmptyMVar
157 commandChan <- newTChanIO
157 commandChan <- newTChanIO
158 callbacks <- newTVarIO M.empty
158 callbacks <- newTVarIO M.empty
@@ -176,7 +176,8 @@ runJavaScript sendBatch entryPoint = do
176 let processResults :: Bool -> Results -> IO ()
176 let processResults :: Bool -> Results -> IO ()
177 processResults syncCallbacks = \case
177 processResults syncCallbacks = \case
178 (ProtocolError err) -> error $ "Protocol error : " <> T.unpack err
178 (ProtocolError err) -> error $ "Protocol error : " <> T.unpack err
179− (Callback f this a) -> do
179+ (Callback n br f this a) -> do
180+ putMVar recvMVar (n, br)
180 f'@(JSVal fNumber) <- runReaderT (unJSM $ wrapJSVal f) ctx
181 f'@(JSVal fNumber) <- runReaderT (unJSM $ wrapJSVal f) ctx
181 this' <- runReaderT (unJSM $ wrapJSVal this) ctx
182 this' <- runReaderT (unJSM $ wrapJSVal this) ctx
182 args <- runReaderT (unJSM $ mapM wrapJSVal a) ctx
183 args <- runReaderT (unJSM $ mapM wrapJSVal a) ctx
@@ -185,27 +186,20 @@ runJavaScript sendBatch entryPoint = do
185 Nothing -> liftIO $ putStrLn "Callback called after it was freed"
186 Nothing -> liftIO $ putStrLn "Callback called after it was freed"
186 Just cb -> void . forkIO $ do
187 Just cb -> void . forkIO $ do
187 runReaderT (unJSM $ cb f' this' args) ctx
188 runReaderT (unJSM $ cb f' this' args) ctx
188− when syncCallbacks $ do
189+ when syncCallbacks $
189 doSendAsyncCommand ctx EndSyncBlock
190 doSendAsyncCommand ctx EndSyncBlock
191+ Duplicate nBatch nExpected -> do
192+ putStrLn $ "Error : Unexpected Duplicate. syncCallbacks=" <> show syncCallbacks <>
193+ " nBatch=" <> show nBatch <> " nExpected=" <> show nExpected
190− void $ doSendCommand ctx Sync
194+ void $ doSendCommand ctx Sync
191− Duplicate -> error "Error : Unexpected Duplicate"
192− m -> putMVar recvMVar m
195+ BatchResults n br -> putMVar recvMVar (n, br)
193 asyncResults :: Results -> IO ()
196 asyncResults :: Results -> IO ()
197+ asyncResults results =
194− asyncResults = processResults False
198+ void . forkIO $ processResults False results
195 syncResults :: Results -> IO Batch
199 syncResults :: Results -> IO Batch
196 syncResults results = do
200 syncResults results = do
197− existing <- tryTakeMVar syncSendBatch
201+ void . forkIO $ processResults True results
198− when (isJust existing) $ error "Error : Unexpected Sync Results"
199− tryTakeMVar lastAsyncBatch >>= \case
202+ readMVar lastAsyncBatch
200− Just lastAsync -> processResults True results >> return lastAsync
202− nextBatch <- newEmptyMVar
203− putMVar syncSendBatch (\batch -> do
204− existing <- tryTakeMVar nextBatch
205− when (isJust existing) $ error "Error : Unexpected Sync Results"
206− putMVar nextBatch batch)
207− processResults True results
209 logInfo s =
203 logInfo s =
210 readIORef loggingEnabled >>= \case
204 readIORef loggingEnabled >>= \case
211 True -> do
205 True -> do
@@ -215,28 +209,32 @@ runJavaScript sendBatch entryPoint = do
215 cbCount <- M.size <$> readTVarIO callbacks
209 cbCount <- M.size <$> readTVarIO callbacks
216 putStrLn . s $ "M " <> currentBytesUsedStr <> "CB " <> show cbCount
210 putStrLn . s $ "M " <> currentBytesUsedStr <> "CB " <> show cbCount
217 False -> return ()
211 False -> return ()
218− _ <- forkIO . forM_ [1..] $ \nBatch ->
212+ _ <- forkIO . numberForeverFromM_ 1 $ \nBatch ->
219 readBatch nBatch commandChan >>= \case
213 readBatch nBatch commandChan >>= \case
220 (batch@(Batch cmds _ _), resultMVars) -> do
214 (batch@(Batch cmds _ _), resultMVars) -> do
221 logInfo (\x -> "Sync " <> x <> show (length cmds, last cmds))
215 logInfo (\x -> "Sync " <> x <> show (length cmds, last cmds))
222− tryTakeMVar syncSendBatch >>= \case
216+ _ <- tryTakeMVar lastAsyncBatch
223− Just send -> send batch
225− putMVar lastAsyncBatch batch
217+ putMVar lastAsyncBatch batch
226− sendBatch batch
218+ sendBatch batch
227− takeMVar recvMVar >>= \r -> do
219+ takeResult recvMVar nBatch >>= \case
228− _ <- tryTakeMVar lastAsyncBatch
220+ (n, _) | n /= nBatch -> error $ "Unexpected jsaddle results (expected batch " <> show nBatch <> ", got batch " <> show n <> ")"
230− Success results | length results /= length resultMVars -> error "Unexpected number of jsaddle results"
221+ (_, Success results) | length results /= length resultMVars -> error "Unexpected number of jsaddle results"
231− | otherwise -> zipWithM_ putMVar resultMVars results
222+ | otherwise -> zipWithM_ putMVar resultMVars results
232− Failure results exception -> do
223+ (_, Failure results exception) -> do
233 -- The exception will only be rethrown in Haskell if/when one of the
224 -- The exception will only be rethrown in Haskell if/when one of the
234 -- missing results (if any) is evaluated.
225 -- missing results (if any) is evaluated.
235 putStrLn "A JavaScript exception was thrown! (may not reach Haskell code)"
226 putStrLn "A JavaScript exception was thrown! (may not reach Haskell code)"
236 zipWithM_ putMVar resultMVars $ results <> repeat (ThrowJSValue exception)
227 zipWithM_ putMVar resultMVars $ results <> repeat (ThrowJSValue exception)
237− _ -> error "Unexpected jsaddle results"
238 return (asyncResults, syncResults, runReaderT (unJSM entryPoint) ctx)
228 return (asyncResults, syncResults, runReaderT (unJSM entryPoint) ctx)
230+ numberForeverFromM_ :: (Monad m, Enum n) => n -> (n -> m a) -> m ()
231+ numberForeverFromM_ !n f = do
233+ numberForeverFromM_ (succ n) f
234+ takeResult recvMVar nBatch =
235+ takeMVar recvMVar >>= \case
236+ (n, _) | n < nBatch -> takeResult recvMVar nBatch
240 readBatch :: Int -> TChan (Either AsyncCommand (Command, MVar Result)) -> IO (Batch, [MVar Result])
238 readBatch :: Int -> TChan (Either AsyncCommand (Command, MVar Result)) -> IO (Batch, [MVar Result])
241 readBatch nBatch chan = do
239 readBatch nBatch chan = do
242 first <- atomically $ readTChan chan -- We want at least one command to send
240 first <- atomically $ readTChan chan -- We want at least one command to send