@@ -12,7 +12,7 @@ module Control.Concurrent.Execute
1212 , runActions
1313 ) where
1414
15- import Control.Concurrent.STM ( retry )
15+ import Control.Concurrent.STM ( check )
1616import Stack.Prelude
1717import Data.List ( sortBy )
1818import qualified Data.Set as Set
@@ -134,49 +134,56 @@ runActions' ExecuteState {..} = loop
134134 else inner actions
135135
136136 loop :: IO ()
137- loop = join $ atomically $ breakOnErrs $ withActions $ \ as ->
138- case break (Set. null . actionDeps) as of
137+ loop = join $ atomically $ breakOnErrs $ withActions processActions
138+
139+ processActions :: [Action ] -> STM (IO () )
140+ processActions actions = do
141+ inAction <- readTVar esInAction
142+ case break (Set. null . actionDeps) actions of
139143 (_, [] ) -> do
140- inAction <- readTVar esInAction
141- if Set. null inAction
142- then do
143- unless esKeepGoing $
144- modifyTVar esExceptions (toException InconsistentDependenciesBug : )
145- doNothing
146- else retry
147- (xs, action: ys) -> do
148- inAction <- readTVar esInAction
149- case actionConcurrency action of
150- ConcurrencyAllowed -> pure ()
151- ConcurrencyDisallowed -> unless (Set. null inAction) retry
152- let as' = xs ++ ys
153- remaining = Set. union
154- (Set. fromList $ map actionId as')
155- inAction
156- writeTVar esActions as'
157- modifyTVar esInAction (Set. insert $ actionId action)
158- pure $ mask $ \ restore -> do
159- eres <- try $ restore $ actionDo action ActionContext
160- { acRemaining = remaining
161- , acDownstream = downstreamActions (actionId action) as'
162- , acConcurrency = actionConcurrency action
163- }
164- atomically $ do
165- modifyTVar esInAction (Set. delete $ actionId action)
166- modifyTVar esCompleted (+ 1 )
167- case eres of
168- Left err -> modifyTVar esExceptions (err: )
169- Right () ->
170- let dropDep a =
171- a { actionDeps = Set. delete (actionId action) $ actionDeps a }
172- in modifyTVar esActions $ map dropDep
173- restore loop
144+ check (Set. null inAction)
145+ unless esKeepGoing $
146+ modifyTVar esExceptions (toException InconsistentDependenciesBug : )
147+ doNothing
148+ (xs, action: ys) -> processAction inAction (xs ++ ys) action
149+
150+ processAction :: Set ActionId -> [Action ] -> Action -> STM (IO () )
151+ processAction inAction otherActions action = do
152+ let concurrency = actionConcurrency action
153+ unless (concurrency == ConcurrencyAllowed ) $
154+ check (Set. null inAction)
155+ let action' = actionId action
156+ otherActions' = Set. fromList $ map actionId otherActions
157+ remaining = Set. union otherActions' inAction
158+ actionContext = ActionContext
159+ { acRemaining = remaining
160+ , acDownstream = downstreamActions action' otherActions
161+ , acConcurrency = concurrency
162+ }
163+ writeTVar esActions otherActions
164+ modifyTVar esInAction (Set. insert action')
165+ pure $ do
166+ mask $ \ restore -> do
167+ eres <- try $ restore $ actionDo action actionContext
168+ atomically $ do
169+ modifyTVar esInAction (Set. delete action')
170+ modifyTVar esCompleted (+ 1 )
171+ case eres of
172+ Left err -> modifyTVar esExceptions (err: )
173+ Right () -> modifyTVar esActions $ map (dropDep action')
174+ loop
174175
175176 -- | Filter a list of actions to include only those that depend on the given
176177 -- action.
177178 downstreamActions :: ActionId -> [Action ] -> [Action ]
178179 downstreamActions aid = filter (\ a -> aid `Set.member` actionDeps a)
179180
181+ -- | Given two actions (the first specified by its id) yield an action
182+ -- equivalent to the second but excluding any dependency on the first action.
183+ dropDep :: ActionId -> Action -> Action
184+ dropDep action' action =
185+ action { actionDeps = Set. delete action' $ actionDeps action }
186+
180187 -- | @IO ()@ lifted into 'STM'.
181188 doNothing :: STM (IO () )
182189 doNothing = pure $ pure ()
0 commit comments