@@ -239,6 +239,7 @@ module Control.Distributed.Process.Platform.Supervisor
239239 , RestartMode (.. )
240240 , RestartOrder (.. )
241241 , RestartStrategy (.. )
242+ , ShutdownMode (.. )
242243 , restartOne
243244 , restartAll
244245 , restartLeft
@@ -430,14 +431,21 @@ data RestartMode =
430431instance Binary RestartMode where
431432instance NFData RestartMode where
432433
434+ data ShutdownMode = SequentialShutdown ! RestartOrder
435+ | ParallelShutdown
436+ deriving (Typeable , Generic , Show , Eq )
437+ instance Binary ShutdownMode where
438+ instance NFData ShutdownMode where
439+
433440-- | Strategy used by a supervisor to handle child restarts, whether due to
434441-- unexpected child failure or explicit restart requests from a client.
435442--
436443-- Some terminology: We refer to child processes managed by the same supervisor
437444-- as /siblings/. When restarting a child process, the 'RestartNone' policy
438445-- indicates that sibling processes should be left alone, whilst the 'RestartAll'
439446-- policy will cause /all/ children to be restarted (in the same order they were
440- -- started). ************************************************************************
447+ -- started).
448+ --
441449-- The other two restart strategies refer to /prior/ and /subsequent/
442450-- siblings, which describe's those children's configured position
443451-- (i.e., insertion order). These latter modes allow one to control the order
@@ -446,18 +454,19 @@ instance NFData RestartMode where
446454--
447455data RestartStrategy =
448456 RestartOne
449- { intensity :: ! RestartLimit } -- ^ restart only the failed child process
457+ { intensity :: ! RestartLimit
458+ } -- ^ restart only the failed child process
450459 | RestartAll
451- { intensity :: ! RestartLimit
452- , mode :: ! RestartMode
460+ { intensity :: ! RestartLimit
461+ , mode :: ! RestartMode
453462 } -- ^ also restart all siblings
454463 | RestartLeft
455- { intensity :: ! RestartLimit
456- , mode :: ! RestartMode
464+ { intensity :: ! RestartLimit
465+ , mode :: ! RestartMode
457466 } -- ^ restart prior siblings (i.e., prior /start order/)
458467 | RestartRight
459- { intensity :: ! RestartLimit
460- , mode :: ! RestartMode
468+ { intensity :: ! RestartLimit
469+ , mode :: ! RestartMode
461470 } -- ^ restart subsequent siblings (i.e., subsequent /start order/)
462471 deriving (Typeable , Generic , Show )
463472instance Binary RestartStrategy where
@@ -542,7 +551,7 @@ instance NFData ChildType where
542551data RestartPolicy =
543552 Permanent -- ^ a permanent child will always be restarted
544553 | Temporary -- ^ a temporary child will /never/ be restarted
545- | Transient -- ^ a transient child will be restarted only if it terminates abnormally
554+ | Transient -- ^ A transient child will be restarted only if it terminates abnormally
546555 | Intrinsic -- ^ as 'Transient', but if the child exits normally, the supervisor also exits normally
547556 deriving (Typeable , Generic , Eq , Show )
548557instance Binary RestartPolicy where
@@ -819,13 +828,14 @@ instance Logger LogSink where
819828 logMessage (LogProcess client') = logMessage client'
820829
821830data State = State {
822- _specs :: ChildSpecs
823- , _active :: Map ProcessId ChildKey
824- , _strategy :: RestartStrategy
825- , _restartPeriod :: NominalDiffTime
826- , _restarts :: [UTCTime ]
827- , _stats :: SupervisorStats
828- , _logger :: LogSink
831+ _specs :: ChildSpecs
832+ , _active :: Map ProcessId ChildKey
833+ , _strategy :: RestartStrategy
834+ , _restartPeriod :: NominalDiffTime
835+ , _restarts :: [UTCTime ]
836+ , _stats :: SupervisorStats
837+ , _logger :: LogSink
838+ , shutdownStrategy :: ShutdownMode
829839 }
830840
831841--------------------------------------------------------------------------------
@@ -837,13 +847,13 @@ data State = State {
837847--
838848-- > start = spawnLocal . run
839849--
840- start :: RestartStrategy -> [ChildSpec ] -> Process ProcessId
841- start s cs = spawnLocal $ run s cs
850+ start :: RestartStrategy -> ShutdownMode -> [ChildSpec ] -> Process ProcessId
851+ start rs ss cs = spawnLocal $ run rs ss cs
842852
843853-- | Run the supplied children using the provided restart strategy.
844854--
845- run :: RestartStrategy -> [ChildSpec ] -> Process ()
846- run strategy' specs' = MP. pserve (strategy' , specs') supInit serverDefinition
855+ run :: RestartStrategy -> ShutdownMode -> [ChildSpec ] -> Process ()
856+ run rs ss specs' = MP. pserve (rs, ss , specs') supInit serverDefinition
847857
848858--------------------------------------------------------------------------------
849859-- Client Facing API --
@@ -935,8 +945,8 @@ shutdownAndWait sid = do
935945-- Server Initialisation/Startup --
936946--------------------------------------------------------------------------------
937947
938- supInit :: InitHandler (RestartStrategy , [ChildSpec ]) State
939- supInit (strategy', specs') = do
948+ supInit :: InitHandler (RestartStrategy , ShutdownMode , [ChildSpec ]) State
949+ supInit (strategy', shutdown', specs') = do
940950 logClient <- Log. client
941951 let client' = case logClient of
942952 Nothing -> LogChan
@@ -946,7 +956,7 @@ supInit (strategy', specs') = do
946956 )
947957 . (strategy ^= strategy')
948958 . (logger ^= client')
949- $ emptyState
959+ $ emptyState shutdown'
950960 )
951961 -- TODO: should we return Ignore, as per OTP's supervisor, if no child starts?
952962 (foldlM initChild initState specs' >>= return . (flip InitOk ) Infinity )
@@ -982,15 +992,16 @@ initialised state spec (Right ref) = do
982992-- Server Definition/State --
983993--------------------------------------------------------------------------------
984994
985- emptyState :: State
986- emptyState = State {
987- _specs = Seq. empty
988- , _active = Map. empty
989- , _strategy = restartAll
990- , _restartPeriod = (fromIntegral (0 :: Integer )) :: NominalDiffTime
991- , _restarts = []
992- , _stats = emptyStats
993- , _logger = LogChan
995+ emptyState :: ShutdownMode -> State
996+ emptyState strat = State {
997+ _specs = Seq. empty
998+ , _active = Map. empty
999+ , _strategy = restartAll
1000+ , _restartPeriod = (fromIntegral (0 :: Integer )) :: NominalDiffTime
1001+ , _restarts = []
1002+ , _stats = emptyStats
1003+ , _logger = LogChan
1004+ , shutdownStrategy = strat
9941005 }
9951006
9961007emptyStats :: SupervisorStats
@@ -1218,7 +1229,6 @@ handleMonitorSignal state (ProcessMonitorNotification _ pid reason) = do
12181229--------------------------------------------------------------------------------
12191230
12201231handleShutdown :: State -> ExitReason -> Process ()
1221- -- TODO: stop all our children from left to right...
12221232handleShutdown state (ExitOther reason) = terminateChildren state >> die reason
12231233handleShutdown state _ = terminateChildren state
12241234
@@ -1260,7 +1270,7 @@ tryRestartBranch rs sp dr st = -- TODO: use DiedReason for logging...
12601270 RestartAll _ _ -> childSpecs
12611271 RestartLeft _ _ -> subTreeL
12621272 RestartRight _ _ -> subTreeR
1263- _ -> error " IllegalState"
1273+ _ -> error " IllegalState"
12641274 proc = case mode' of
12651275 RestartEach _ -> stopStart
12661276 RestartInOrder _ -> restartL
@@ -1628,17 +1638,24 @@ filterInitFailures sup pid ex = do
16281638
16291639terminateChildren :: State -> Process ()
16301640terminateChildren state = do
1631- let allChildren = toList $ state ^. specs
1632- pids <- forM allChildren $ \ ch -> do
1633- pid <- spawnLocal $ asyncTerminate ch $ (active ^= Map. empty) state
1634- void $ monitor pid
1635- return pid
1636- _ <- collectExits [] pids
1637- -- TODO: report errs???
1638- return ()
1641+ case (shutdownStrategy state) of
1642+ ParallelShutdown -> do
1643+ let allChildren = toList $ state ^. specs
1644+ pids <- forM allChildren $ \ ch -> do
1645+ pid <- spawnLocal $ void $ syncTerminate ch $ (active ^= Map. empty) state
1646+ void $ monitor pid
1647+ return pid
1648+ void $ collectExits [] pids
1649+ -- TODO: report errs???
1650+ SequentialShutdown ord -> do
1651+ let specs' = state ^. specs
1652+ let allChildren = case ord of
1653+ RightToLeft -> Seq. reverse specs'
1654+ LeftToRight -> specs'
1655+ void $ foldlM (flip syncTerminate) state (toList allChildren)
16391656 where
1640- asyncTerminate :: Child -> State -> Process ()
1641- asyncTerminate (cr, cs) state' = void $ doTerminateChild cr cs state'
1657+ syncTerminate :: Child -> State -> Process State
1658+ syncTerminate (cr, cs) state' = doTerminateChild cr cs state'
16421659
16431660 collectExits :: [DiedReason ]
16441661 -> [ProcessId ]
@@ -1667,7 +1684,7 @@ doTerminateChild ref spec state = do
16671684 )
16681685 where
16691686 shutdownComplete :: State -> ProcessId -> DiedReason -> Process State
1670- shutdownComplete _ _ DiedNormal = return $ updateStopped
1687+ shutdownComplete _ _ DiedNormal = return $ updateStopped
16711688 shutdownComplete state' pid (r :: DiedReason ) = do
16721689 logShutdown (state' ^. logger) chKey pid r >> return state'
16731690
0 commit comments