on master help sign in
Sync callbacks for jsaddle-wkwebview take 2
commit3f48016114cfe661fe7b3be76241dc1f96d949f0 authorHamish Mackenzie <Hamish.K.Mackenzie@googlemail.com> dateFri 12 May 2017, 14:05 (9 years ago) parent4aa7ad6
5 files changed+67−51
jsaddle-warp/src/Language/Javascript/JSaddle/WebSockets.hs+1−1 jsaddle-wkwebview/src/Language/Javascript/JSaddle/WKWebView/Internal.hs+2−2 jsaddle/src/Language/Javascript/JSaddle/Run.hs+26−33 jsaddle/src/Language/Javascript/JSaddle/Run/Files.hs+25−11 jsaddle/src/Language/Javascript/JSaddle/Types.hs+13−4
jsaddle-warp/src/Language/Javascript/JSaddle/WebSockets.hs+1−1
@@ -53,7 +53,7 @@ jsaddleOr opts entryPoint = websocketsOr opts wsApp
5353 receiveDataMessage conn >>= \case
5454 (WS.Text t) ->
5555 case decode t of
56− Nothing -> error $ "jsaddle WebSocket decode failed : " <> show t
56+ Nothing -> error $ "jsaddle Results decode failed : " <> show t
5757 Just r -> processResult r
5858 _ -> error "jsaddle WebSocket unexpected binary data"
5959 start
jsaddle-wkwebview/src/Language/Javascript/JSaddle/WKWebView/Internal.hs+2−2
@@ -81,7 +81,7 @@ jsaddleResult ptrHandler s = do
8181 processResult <- deRefStablePtr ptrHandler
8282 result <- packCString s
8383 case decode (fromStrict result) of
84− Nothing -> error $ "jsaddle WebSocket decode failed : " <> show result
84+ Nothing -> error $ "jsaddle Results decode failed : " <> show result
8585 Just r -> processResult r
8686
8787jsaddleSyncResult :: StablePtr (Results -> IO Batch) -> JSaddleHandler -> CString -> IO ()
@@ -89,7 +89,7 @@ jsaddleSyncResult ptrHandler jsaddleHandler s = do
8989 syncProcessResult <- deRefStablePtr ptrHandler
9090 result <- packCString s
9191 case decode (fromStrict result) of
92− Nothing -> error $ "jsaddle WebSocket decode failed : " <> show result
92+ Nothing -> error $ "jsaddle Results decode failed : " <> show result
9393 Just r -> do -- void . forkIO $ do
9494 batch <- syncProcessResult r
9595 useAsCString (toStrict $ encode batch) $
jsaddle/src/Language/Javascript/JSaddle/Run.hs+26−33
@@ -49,7 +49,7 @@ import Control.Concurrent.STM.TChan
4949import Control.Concurrent.STM.TVar
5050 (writeTVar, readTVar, readTVarIO, modifyTVar', newTVarIO)
5151import Control.Concurrent.MVar
52− (tryTakeMVar, MVar, putMVar, takeMVar, newEmptyMVar)
52+ (tryTakeMVar, MVar, putMVar, takeMVar, newEmptyMVar, readMVar)
5353
5454import System.IO.Unsafe (unsafeInterleaveIO)
5555import System.Mem.Weak (addFinalizer)
@@ -61,7 +61,7 @@ import Data.Time.Clock (getCurrentTime,diffUTCTime)
6161import Data.IORef (newIORef, atomicWriteIORef, readIORef)
6262
6363import Language.Javascript.JSaddle.Types
64− (Command(..), AsyncCommand(..), Result(..), Results(..), JSContextRef(..), JSVal(..),
64+ (Command(..), AsyncCommand(..), Result(..), BatchResults(..), Results(..), JSContextRef(..), JSVal(..),
6565 Object(..), JSValueReceived(..), JSM(..), Batch(..), JSValueForSend(..))
6666import Language.Javascript.JSaddle.Exception (JSException(..))
6767-- import Language.Javascript.JSaddle.Native.Internal (wrapJSVal)
@@ -152,7 +152,6 @@ runJavaScript :: (Batch -> IO ()) -> JSM () -> IO (Results -> IO (), Results ->
152152runJavaScript sendBatch entryPoint = do
153153 startTime' <- getCurrentTime
154154 recvMVar <- newEmptyMVar
155− syncSendBatch <- newEmptyMVar
156155 lastAsyncBatch <- newEmptyMVar
157156 commandChan <- newTChanIO
158157 callbacks <- newTVarIO M.empty
@@ -176,7 +175,8 @@ runJavaScript sendBatch entryPoint = do
176175 let processResults :: Bool -> Results -> IO ()
177176 processResults syncCallbacks = \case
178177 (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)
180180 f'@(JSVal fNumber) <- runReaderT (unJSM $ wrapJSVal f) ctx
181181 this' <- runReaderT (unJSM $ wrapJSVal this) ctx
182182 args <- runReaderT (unJSM $ mapM wrapJSVal a) ctx
@@ -185,27 +185,20 @@ runJavaScript sendBatch entryPoint = do
185185 Nothing -> liftIO $ putStrLn "Callback called after it was freed"
186186 Just cb -> void . forkIO $ do
187187 runReaderT (unJSM $ cb f' this' args) ctx
188− when syncCallbacks $ do
188+ when syncCallbacks $
189189 doSendAsyncCommand ctx EndSyncBlock
190− void $ doSendCommand ctx Sync
191− Duplicate -> error "Error : Unexpected Duplicate"
192− m -> putMVar recvMVar m
190+ Duplicate nBatch nExpected -> do
191+ putStrLn $ "Error : Unexpected Duplicate. syncCallbacks=" <> show syncCallbacks <>
192+ " nBatch=" <> show nBatch <> " nExpected=" <> show nExpected
193+ void $ doSendCommand ctx Sync
194+ BatchResults n br -> putMVar recvMVar (n, br)
193195 asyncResults :: Results -> IO ()
194− asyncResults = processResults False
196+ asyncResults results =
197+ void . forkIO $ processResults False results
195198 syncResults :: Results -> IO Batch
196199 syncResults results = do
197− existing <- tryTakeMVar syncSendBatch
198− when (isJust existing) $ error "Error : Unexpected Sync Results"
199− tryTakeMVar lastAsyncBatch >>= \case
200− Just lastAsync -> processResults True results >> return lastAsync
201− Nothing -> do
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
208− takeMVar nextBatch
200+ void . forkIO $ processResults True results
201+ readMVar lastAsyncBatch
209202 logInfo s =
210203 readIORef loggingEnabled >>= \case
211204 True -> do
@@ -219,24 +212,24 @@ runJavaScript sendBatch entryPoint = do
219212 readBatch nBatch commandChan >>= \case
220213 (batch@(Batch cmds _ _), resultMVars) -> do
221214 logInfo (\x -> "Sync " <> x <> show (length cmds, last cmds))
222− tryTakeMVar syncSendBatch >>= \case
223− Just send -> send batch
224− Nothing -> do
225− putMVar lastAsyncBatch batch
226− sendBatch batch
227− takeMVar recvMVar >>= \r -> do
228− _ <- tryTakeMVar lastAsyncBatch
229− case r of
230− Success results | length results /= length resultMVars -> error "Unexpected number of jsaddle results"
231− | otherwise -> zipWithM_ putMVar resultMVars results
232− Failure results exception -> do
215+ _ <- tryTakeMVar lastAsyncBatch
216+ putMVar lastAsyncBatch batch
217+ sendBatch batch
218+ takeResult recvMVar nBatch >>= \case
219+ (n, _) | n /= nBatch -> error $ "Unexpected jsaddle results (expected batch " <> show nBatch <> ", got batch " <> show n <> ")"
220+ (_, Success results) | length results /= length resultMVars -> error "Unexpected number of jsaddle results"
221+ | otherwise -> zipWithM_ putMVar resultMVars results
222+ (_, Failure results exception) -> do
233223 -- The exception will only be rethrown in Haskell if/when one of the
234224 -- missing results (if any) is evaluated.
235225 putStrLn "A JavaScript exception was thrown! (may not reach Haskell code)"
236226 zipWithM_ putMVar resultMVars $ results <> repeat (ThrowJSValue exception)
237− _ -> error "Unexpected jsaddle results"
238227 return (asyncResults, syncResults, runReaderT (unJSM entryPoint) ctx)
239228 where
229+ takeResult recvMVar nBatch =
230+ takeMVar recvMVar >>= \case
231+ (n, _) | n < nBatch -> takeResult recvMVar nBatch
232+ r -> return r
240233 readBatch :: Int -> TChan (Either AsyncCommand (Command, MVar Result)) -> IO (Batch, [MVar Result])
241234 readBatch nBatch chan = do
242235 first <- atomically $ readTChan chan -- We want at least one command to send
jsaddle/src/Language/Javascript/JSaddle/Run/Files.hs+25−11
@@ -44,6 +44,8 @@ initState = "\
4444 \ jsaddle_values.set(4, window);\n\
4545 \ var jsaddle_index = 100;\n\
4646 \ var expectedBatch = 1;\n\
47+ \ var lastResults = [0, {}];\n\
48+ \ var inCallback = 0;\n\
4749 \"
4850
4951runBatch :: (ByteString -> ByteString) -> Maybe (ByteString -> ByteString) -> ByteString
@@ -52,6 +54,7 @@ runBatch send sendSync = "\
5254 \ var processBatch = function(timestamp) {\n\
5355 \ var batch = firstBatch;\n\
5456 \ var results = [];\n\
57+ \ inCallback++;\n\
5558 \ try {\n\
5659 \ syncDepth = initialSyncDepth || 0;\n\
5760 \ for(;;){\n\
@@ -108,7 +111,7 @@ runBatch send sendSync = "\
108111 \ jsaddle_values.set(nArg, arguments[i]);\n\
109112 \ args[i] = nArg;\n\
110113 \ }\n\
111− \ " <> send "{\"tag\": \"Callback\", \"contents\": [nFunction, nThis, args]}" <> "\n\
114+ \ " <> send "{\"tag\": \"Callback\", \"contents\": [lastResults[0], lastResults[1], nFunction, nThis, args]}" <> "\n\
112115 \ })})();\n\
113116 \ break;\n\
114117 \ case \"NewSyncCallback\":\n\
@@ -125,9 +128,13 @@ runBatch send sendSync = "\
125128 \ }\n" <> (
126129 case sendSync of
127130 Just s ->
128− " runBatch(" <> s "{\"tag\": \"Callback\", \"contents\": [nFunction, nThis, args]}" <> ", 1);\n"
131+ " if(inCallback > 0) {\n\
132+ \ " <> send "{\"tag\": \"Callback\", \"contents\": [lastResults[0], lastResults[1], nFunction, nThis, args]}" <> "\n\
133+ \ } else {\n\
134+ \ runBatch(" <> s "{\"tag\": \"Callback\", \"contents\": [lastResults[0], lastResults[1], nFunction, nThis, args]}" <> ", 1);\n\
135+ \ }\n"
129136 Nothing ->
130− " " <> send "{\"tag\": \"Callback\", \"contents\": [nFunction, nThis, args]}" <> "\n"
137+ " " <> send "{\"tag\": \"Callback\", \"contents\": [lastResults[0], lastResults[1], nFunction, nThis, args]}" <> "\n"
131138 ) <>
132139 " })})();\n\
133140 \ break;\n\
@@ -243,29 +250,35 @@ runBatch send sendSync = "\
243250 \ }\n\
244251 \ }\n\
245252 \ }\n\
246− \ if(syncDepth === 0) {\n\
247− \ " <> send "{\"tag\": \"Success\", \"contents\": results}" <> "\n\
253+ \ if(syncDepth <= 0) {\n\
254+ \ lastResults = [batch[2], {\"tag\": \"Success\", \"contents\": results}];\n\
255+ \ " <> send "{\"tag\": \"BatchResults\", \"contents\": [lastResults[0], lastResults[1]]}" <> "\n\
248256 \ break;\n\
249257 \ } else {\n" <> (
250258 case sendSync of
251259 Just s ->
252− " batch = " <> s "{\"tag\": \"Success\", \"contents\": results}" <> "\n\
260+ " lastResults = [batch[2], {\"tag\": \"Success\", \"contents\": results}];\n\
261+ \ batch = " <> s "{\"tag\": \"BatchResults\", \"contents\": [lastResults[0], lastResults[1]]}" <> ";\n\
253262 \ results = [];\n"
254263 Nothing ->
255− " " <> send "{\"tag\": \"Success\", \"contents\": results}" <> "\n\
264+ " " <> send "{\"tag\": \"BatchResults\", \"contents\": [batch[2], {\"tag\": \"Success\", \"contents\": results}]}" <> "\n\
256265 \ break;\n"
257266 ) <>
258267 " }\n\
259268 \ } else {\n\
260− \ if(syncDepth === 0) {\n\
269+ \ if(syncDepth <= 0) {\n\
261270 \ break;\n\
262271 \ } else {\n" <> (
263272 case sendSync of
264273 Just s ->
265− " batch = " <> s "{\"tag\": \"Duplicate\"}" <> "\n\
274+ " if(batch[2] === expectedBatch - 1) {\n\
275+ \ batch = " <> s "{\"tag\": \"BatchResults\", \"contents\": [lastResults[0], lastResults[1]]}" <> ";\n\
276+ \ } else {\n\
277+ \ batch = " <> s "{\"tag\": \"Duplicate\", \"contents\": [batch[2], expectedBatch]}" <> ";\n\
278+ \ }\n\
266279 \ results = [];\n"
267280 Nothing ->
268− " " <> send "{\"tag\": \"Duplicate\"}" <> "\n\
281+ " " <> send "{\"tag\": \"Duplicate\", \"contents\": [batch[2], expectedBatch]}" <> "\n\
269282 \ break;\n"
270283 ) <>
271284 " }\n\
@@ -275,8 +288,9 @@ runBatch send sendSync = "\
275288 \ catch (err) {\n\
276289 \ var n = ++jsaddle_index;\n\
277290 \ jsaddle_values.set(n, err);\n\
278− \ " <> send "{\"tag\": \"Failure\", \"contents\": [results,n]}" <> "\n\
291+ \ " <> send "{\"tag\": \"BatchResults\", \"contents\": [batch[2], {\"tag\": \"Failure\", \"contents\": [results, n]}]}" <> "\n\
279292 \ }\n\
293+ \ inCallback--;\n\
280294 \ };\n\
281295 \ if(batch[1] && (initialSyncDepth || 0) === 0) {\n\
282296 \ window.requestAnimationFrame(processBatch);\n\
jsaddle/src/Language/Javascript/JSaddle/Types.hs+13−4
@@ -73,6 +73,7 @@ module Language.Javascript.JSaddle.Types (
7373 , Command(..)
7474 , Batch(..)
7575 , Result(..)
76+ , BatchResults(..)
7677 , Results(..)
7778#endif
7879) where
@@ -385,10 +386,18 @@ instance ToJSON Result where
385386
386387instance FromJSON Result
387388
388−data Results = Success [Result]
389− | Failure [Result] JSValueReceived
390− | Duplicate
391− | Callback JSValueReceived JSValueReceived [JSValueReceived]
389+data BatchResults = Success [Result]
390+ | Failure [Result] JSValueReceived
391+ deriving (Show, Generic)
392+
393+instance ToJSON BatchResults where
394+ toEncoding = genericToEncoding defaultOptions
395+
396+instance FromJSON BatchResults
397+
398+data Results = BatchResults Int BatchResults
399+ | Duplicate Int Int
400+ | Callback Int BatchResults JSValueReceived JSValueReceived [JSValueReceived]
392401 | ProtocolError Text
393402 deriving (Show, Generic)
394403