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