forked from commercialhaskell/stack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExecute.hs
More file actions
114 lines (107 loc) · 3.81 KB
/
Execute.hs
File metadata and controls
114 lines (107 loc) · 3.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RecordWildCards #-}
-- Concurrent execution with dependencies. Types currently hard-coded for needs
-- of stack, but could be generalized easily.
module Control.Concurrent.Execute
( ActionType (..)
, ActionId (..)
, ActionContext (..)
, Action (..)
, runActions
) where
import Control.Applicative
import Control.Concurrent.Async (Concurrently (..))
import Control.Concurrent.STM
import Control.Exception
import Control.Monad (join)
import Data.Foldable (sequenceA_)
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Typeable (Typeable)
import Prelude -- Fix AMP warning
import Stack.Types
data ActionType
= ATBuild
| ATInstall
| ATWanted
deriving (Show, Eq, Ord)
data ActionId = ActionId !PackageIdentifier !ActionType
deriving (Show, Eq, Ord)
data Action = Action
{ actionId :: !ActionId
, actionDeps :: !(Set ActionId)
, actionDo :: !(ActionContext -> IO ())
}
data ActionContext = ActionContext
{ acRemaining :: !Int
-- ^ Does not include the current action
}
deriving Show
data ExecuteState = ExecuteState
{ esActions :: TVar [Action]
, esExceptions :: TVar [SomeException]
, esInAction :: TVar Int
}
data ExecuteException
= InconsistentDependencies
| ExecutionFailure [SomeException]
deriving (Show, Typeable)
instance Exception ExecuteException
runActions :: Int -- ^ threads
-> [Action]
-> IO ()
runActions threads actions0 = do
es <- ExecuteState
<$> newTVarIO actions0
<*> newTVarIO []
<*> newTVarIO 0
if threads <= 1
then runActions' es
else runConcurrently $ sequenceA_ $ replicate threads $ Concurrently $ runActions' es
errs <- readTVarIO $ esExceptions es
if null errs
then return ()
else throwIO $ ExecutionFailure errs
runActions' :: ExecuteState -> IO ()
runActions' ExecuteState {..} =
loop
where
breakOnErrs inner = do
errs <- readTVar esExceptions
if null errs
then inner
else return $ return ()
withActions inner = do
as <- readTVar esActions
if null as
then return $ return ()
else inner as
loop = join $ atomically $ breakOnErrs $ withActions $ \as -> do
case break (Set.null . actionDeps) as of
(_, []) -> do
inAction <- readTVar esInAction
if inAction == 0
then do
modifyTVar esExceptions (toException InconsistentDependencies:)
return $ return ()
else retry
(xs, action:ys) -> do
let as' = xs ++ ys
inAction <- readTVar esInAction
let remaining = length as' + inAction
writeTVar esActions as'
modifyTVar esInAction (+ 1)
return $ mask $ \restore -> do
eres <- try $ restore $ actionDo action ActionContext
{ acRemaining = remaining
}
case eres of
Left err -> atomically $ do
modifyTVar esExceptions (err:)
modifyTVar esInAction (subtract 1)
Right () -> do
atomically $ do
modifyTVar esInAction (subtract 1)
let dropDep a = a { actionDeps = Set.delete (actionId action) $ actionDeps a }
modifyTVar esActions $ map dropDep
restore loop