Skip to content

Commit 4fcce33

Browse files
authored
Fully drain event queue during synchronous unmount. (dmjio#874)
* Drain event queue during synchronous unmount. - This obviates the need for threadDelay since all events are processed before unmounting, in a synchronous callback. - Let the GC recycle component thread. Since the componentName is deleted from the global componentMap, all references will go out of scope. * Drop reference to component thread inside of ComponentState.
1 parent 80e4e42 commit 4fcce33

File tree

1 file changed

+23
-14
lines changed

1 file changed

+23
-14
lines changed

src/Miso/Internal.hs

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ module Miso.Internal
2828
) where
2929
-----------------------------------------------------------------------------
3030
import Control.Exception (throwIO)
31-
import Control.Concurrent (ThreadId, killThread, threadDelay)
31+
import Control.Concurrent (ThreadId, killThread)
3232
import Control.Monad (forM, forM_, when, void)
3333
import Control.Monad.IO.Class
3434
import qualified Data.Aeson as A
@@ -37,6 +37,7 @@ import Data.IORef (IORef, newIORef, atomicModifyIORef', readIORef, ato
3737
import Data.Map.Strict (Map)
3838
import qualified Data.Map.Strict as M
3939
import qualified Data.Sequence as S
40+
import Data.Sequence (Seq)
4041
import qualified JavaScript.Array as JSArray
4142
import Language.Javascript.JSaddle
4243
import Prelude hiding (null)
@@ -89,7 +90,7 @@ initialize App {..} getView = do
8990
atomicWriteIORef componentModel newModel
9091
syncPoint
9192
eventLoop newModel
92-
componentMainThread <- FFI.forkJSM (eventLoop model)
93+
_ <- FFI.forkJSM (eventLoop model)
9394
registerComponent ComponentState {..}
9495
delegator componentMount componentVTree events (logLevel `elem` [DebugEvents, DebugAll])
9596
forM_ initialAction componentSink
@@ -106,12 +107,12 @@ data Prerender
106107
data ComponentState model action
107108
= ComponentState
108109
{ componentName :: MisoString
109-
, componentMainThread :: ThreadId
110110
, componentSubThreads :: [ThreadId]
111111
, componentMount :: JSVal
112112
, componentVTree :: IORef VTree
113113
, componentSink :: action -> JSM ()
114114
, componentModel :: IORef model
115+
, componentActions :: IORef (Seq action)
115116
}
116117
-----------------------------------------------------------------------------
117118
-- | componentMap
@@ -205,19 +206,33 @@ drawComponent prerender name App {..} snk = do
205206
ref <- liftIO (newIORef vtree)
206207
pure (name, mountElement, ref)
207208
-----------------------------------------------------------------------------
209+
-- | Drains the event queue before unmounting, executed synchronously
210+
drain
211+
:: App effect model action a
212+
-> ComponentState model action
213+
-> JSM ()
214+
drain app@App{..} cs@ComponentState {..} = do
215+
actions <- liftIO $ atomicModifyIORef' componentActions $ \actions -> (S.empty, actions)
216+
if S.null actions then pure () else go actions
217+
where
218+
go as = do
219+
x <- liftIO (readIORef componentModel)
220+
y <- foldEffects translate update componentSink (toList as) x
221+
liftIO (atomicWriteIORef componentModel y)
222+
drain app cs
223+
-----------------------------------------------------------------------------
208224
-- | Helper function for cleanly destroying a @Component@
209225
unmount
210226
:: Function
211227
-> App effect model action a
212228
-> ComponentState model action
213229
-> JSM ()
214-
unmount mountCallback App{..} ComponentState {..} = do
230+
unmount mountCallback app@App {..} cs@ComponentState {..} = do
215231
undelegator componentMount componentVTree events (logLevel `elem` [DebugEvents, DebugAll])
216232
freeFunction mountCallback
217233
liftIO (mapM_ killThread componentSubThreads)
218-
liftIO $ do
219-
killThread componentMainThread
220-
modifyIORef' componentMap (M.delete componentName)
234+
drain app cs
235+
liftIO $ modifyIORef' componentMap (M.delete componentName)
221236
-----------------------------------------------------------------------------
222237
-- | Internal function for construction of a Virtual DOM.
223238
--
@@ -243,9 +258,7 @@ runView prerender (Embed attributes (SomeComponent (Component key mount app))) s
243258
FFI.syncCallback $ do
244259
M.lookup mount <$> liftIO (readIORef componentMap) >>= \case
245260
Nothing -> pure ()
246-
Just componentState -> do
247-
liftIO (threadDelay (millis 1))
248-
-- dmj ^ introduce 1ms delay to account for recursive component unmounting
261+
Just componentState ->
249262
unmount mountCallback app componentState
250263
vcomp <- createNode "vcomp" HTML key "div"
251264
setAttrs vcomp attributes snk (logLevel app) (events app)
@@ -352,10 +365,6 @@ registerComponent componentState = liftIO
352365
$ modifyIORef' componentMap
353366
$ M.insert (componentName componentState) componentState
354367
-----------------------------------------------------------------------------
355-
-- | Millisecond helper, converts microseconds to milliseconds
356-
millis :: Int -> Int
357-
millis = (*1000)
358-
-----------------------------------------------------------------------------
359368
-- | Registers components in the global state
360369
renderStyles :: [CSS] -> JSM ()
361370
renderStyles styles =

0 commit comments

Comments
 (0)