Skip to content
This repository was archived by the owner on Jan 2, 2021. It is now read-only.

Commit 188cb30

Browse files
committed
Improved naming and docs
1 parent 23d53a4 commit 188cb30

File tree

2 files changed

+67
-64
lines changed

2 files changed

+67
-64
lines changed

src/Development/IDE/Core/Service.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ shutdown = shakeShut
8585
runAction :: IdeState -> Action a -> IO a
8686
runAction ide action = do
8787
bar <- newBarrier
88-
res <- shakeRunGently ide (do v <- action; liftIO $ signalBarrier bar v; return v)
88+
res <- shakeEnqueue ide (do v <- action; liftIO $ signalBarrier bar v; return v)
8989
-- shakeRun might throw an exception (either through action or a default rule),
9090
-- in which case action may not complete successfully, and signalBarrier might not be called.
9191
-- Therefore we wait for either res (which propagates the exception) or the barrier.

src/Development/IDE/Core/Shake.hs

+66-63
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ module Development.IDE.Core.Shake(
2525
IdeRule, IdeResult, GetModificationTime(..),
2626
shakeOpen, shakeShut,
2727
shakeRun,
28-
shakeRunGently,
28+
shakeEnqueue,
2929
shakeProfile,
3030
use, useWithStale, useNoFile, uses, usesWithStale,
3131
use_, useNoFile_, uses_,
@@ -213,10 +213,13 @@ type IdeRule k v =
213213
, NFData v
214214
)
215215

216+
-- | A live Shake session with the ability to enqueue Actions for running.
217+
-- Keeps the 'ShakeDatabase' open, so at most one 'ShakeSession' per database.
216218
data ShakeSession = ShakeSession
217219
{ cancelShakeSession :: !(IO ())
218220
-- ^ Close the Shake session
219221
, runInShakeSession :: !(forall a . Action a -> IO (IO a))
222+
-- ^ Enqueue an action in the Shake session.
220223
}
221224

222225
nilShakeSession :: ShakeSession
@@ -395,7 +398,11 @@ withMVar' var unmasked masked = mask $ \restore -> do
395398
putMVar var a'
396399
pure c
397400

398-
-- | Spawn immediately. If you are already inside a call to shakeRun that will be aborted with an exception.
401+
-- | Spawn immediately, restarting the current 'ShakeSession' and 'ShakeDatabase',
402+
-- and implicily typechecking all the modules before running the actions.
403+
-- Any computation running in the current session will be aborted with an exception.
404+
--
405+
-- Appropriate for file system events.
399406
shakeRun :: IdeState -> [Action a] -> IO (IO [a])
400407
shakeRun it@IdeState{shakeExtras=ShakeExtras{logger}, ..} acts =
401408
withMVar'
@@ -407,70 +414,66 @@ shakeRun it@IdeState{shakeExtras=ShakeExtras{logger}, ..} acts =
407414
-- It is crucial to be masked here, otherwise we can get killed
408415
-- between spawning the new thread and updating shakeSession.
409416
-- See https://github.com/digital-asset/ghcide/issues/79
410-
(\() -> realShakeRun it acts)
417+
(\() -> newSession it acts)
411418

412-
-- | Append an action to the existing 'ShakeSession'.
413-
-- Assumes an existing 'ShakeSession' is available.
414-
shakeRunGently :: IdeState -> Action a -> IO (IO a)
415-
shakeRunGently IdeState{shakeExtras=ShakeExtras{..}, ..} act =
419+
-- | Enqueue an action in the existing 'ShakeSession'.
420+
-- Returns a computation to block until the action is run.
421+
-- Assumes a 'ShakeSession' is available.
422+
--
423+
-- Appropriate for user actions other than edits.
424+
shakeEnqueue :: IdeState -> Action a -> IO (IO a)
425+
shakeEnqueue IdeState{shakeExtras=ShakeExtras{..}, ..} act =
416426
withMVar shakeSession $ \s -> runInShakeSession s act
417427

418-
realShakeRun :: IdeState -> [Action a] -> IO (ShakeSession, IO [a])
419-
realShakeRun IdeState{shakeExtras=ShakeExtras{..}, ..} acts = do
420-
actionQueue :: TQueue (Action ()) <- newTQueueIO
421-
start <- offsetTime
422-
423-
let
424-
-- A daemon-like action used to inject additional work
425-
pumpAction =
426-
forever $ join $ liftIO $ atomically $ readTQueue actionQueue
427-
428-
workThread restore = do
429-
let acts' = (Nothing <$ pumpAction) : (fmap Just <$> acts)
430-
res <- try (restore $ shakeRunDatabaseProfile shakeProfileDir shakeDb acts')
431-
runTime <- start
432-
let res' = case res of
433-
Left e -> "exception: " <> displayException e
434-
Right _ -> "completed"
435-
profile = case res of
436-
Right (_, Just fp) ->
437-
let link = case filePathToUri' $ toNormalizedFilePath' fp of
438-
NormalizedUri _ x -> x
439-
in ", profile saved at " <> T.unpack link
440-
_ -> ""
441-
442-
-- Wrap up in a thread to avoid calling interruptible
443-
-- operations inside the masked section
444-
let wrapUp = do
445-
logDebug logger $ T.pack $
446-
"Finishing shakeRun (took " ++ showDuration runTime ++ ", " ++ res' ++ profile ++ ")"
447-
448-
return (fst <$> res, wrapUp)
449-
450-
-- Do the work in a background thread
451-
aThread <- asyncWithUnmask workThread
452-
453-
-- run the wrap up unmasked
454-
_ <- async $ do
455-
(_, wrapUp) <- wait aThread
456-
wrapUp
457-
458-
-- 'runInShakeSession' is used to append work in this Shake session
459-
-- The session stays open until 'cancelShakeSession' is called
460-
-- This should only be necessary iff the (virtual) filesystem has changed
461-
let runInShakeSession :: forall a . Action a -> IO (IO a)
462-
runInShakeSession act = do
463-
res <- newEmptyMVar
464-
atomically $ writeTQueue actionQueue (act >>= liftIO . putMVar res)
465-
return (takeMVar res)
466-
467-
cancelShakeSession = cancel aThread
468-
469-
initialResult = do
470-
(res,_) <- wait aThread
471-
either (throwIO @SomeException) (return . catMaybes) res
472-
473-
pure (ShakeSession{..}, initialResult)
428+
-- Set up a new 'ShakeSession' with a set of initial actions.
429+
-- Only valid after aborting the previous one
430+
newSession :: IdeState -> [Action a] -> IO (ShakeSession, IO [a])
431+
newSession IdeState{shakeExtras=ShakeExtras{..}, ..} acts = do
432+
actionQueue :: TQueue (Action ()) <- newTQueueIO
433+
start <- offsetTime
434+
let
435+
-- A daemon-like action used to inject additional work
436+
-- Only one
437+
pumpAction =
438+
forever $ join $ liftIO $ atomically $ readTQueue actionQueue
439+
workThread restore = do
440+
let acts' = (Nothing <$ pumpAction) : (fmap Just <$> acts)
441+
res <- try (restore $ shakeRunDatabaseProfile shakeProfileDir shakeDb acts')
442+
runTime <- start
443+
let res' = case res of
444+
Left e -> "exception: " <> displayException e
445+
Right _ -> "completed"
446+
profile = case res of
447+
Right (_, Just fp) ->
448+
let link = case filePathToUri' $ toNormalizedFilePath' fp of
449+
NormalizedUri _ x -> x
450+
in ", profile saved at " <> T.unpack link
451+
_ -> ""
452+
-- Wrap up in a thread to avoid calling interruptible
453+
-- operations inside the masked section
454+
let wrapUp = do
455+
logDebug logger $ T.pack $
456+
"Finishing shakeRun (took " ++ showDuration runTime ++ ", " ++ res' ++ profile ++ ")"
457+
return (fst <$> res, wrapUp)
458+
-- Do the work in a background thread
459+
aThread <- asyncWithUnmask workThread
460+
-- run the wrap up unmasked
461+
_ <- async $ do
462+
(_, wrapUp) <- wait aThread
463+
wrapUp
464+
-- 'runInShakeSession' is used to append work in this Shake session
465+
-- The session stays open until 'cancelShakeSession' is called
466+
-- This should only be necessary iff the (virtual) filesystem has changed
467+
let runInShakeSession :: forall a . Action a -> IO (IO a)
468+
runInShakeSession act = do
469+
res <- newEmptyMVar
470+
atomically $ writeTQueue actionQueue (act >>= liftIO . putMVar res)
471+
return (takeMVar res)
472+
cancelShakeSession = cancel aThread
473+
initialResult = do
474+
(res,_) <- wait aThread
475+
either (throwIO @SomeException) (return . catMaybes) res
476+
pure (ShakeSession{..}, initialResult)
474477

475478
getDiagnostics :: IdeState -> IO [FileDiagnostic]
476479
getDiagnostics IdeState{shakeExtras = ShakeExtras{diagnostics}} = do

0 commit comments

Comments
 (0)