@@ -239,6 +239,7 @@ module Control.Distributed.Process.Platform.Supervisor
239
239
, RestartMode (.. )
240
240
, RestartOrder (.. )
241
241
, RestartStrategy (.. )
242
+ , ShutdownMode (.. )
242
243
, restartOne
243
244
, restartAll
244
245
, restartLeft
@@ -430,14 +431,21 @@ data RestartMode =
430
431
instance Binary RestartMode where
431
432
instance NFData RestartMode where
432
433
434
+ data ShutdownMode = SequentialShutdown ! RestartOrder
435
+ | ParallelShutdown
436
+ deriving (Typeable , Generic , Show , Eq )
437
+ instance Binary ShutdownMode where
438
+ instance NFData ShutdownMode where
439
+
433
440
-- | Strategy used by a supervisor to handle child restarts, whether due to
434
441
-- unexpected child failure or explicit restart requests from a client.
435
442
--
436
443
-- Some terminology: We refer to child processes managed by the same supervisor
437
444
-- as /siblings/. When restarting a child process, the 'RestartNone' policy
438
445
-- indicates that sibling processes should be left alone, whilst the 'RestartAll'
439
446
-- policy will cause /all/ children to be restarted (in the same order they were
440
- -- started). ************************************************************************
447
+ -- started).
448
+ --
441
449
-- The other two restart strategies refer to /prior/ and /subsequent/
442
450
-- siblings, which describe's those children's configured position
443
451
-- (i.e., insertion order). These latter modes allow one to control the order
@@ -446,18 +454,19 @@ instance NFData RestartMode where
446
454
--
447
455
data RestartStrategy =
448
456
RestartOne
449
- { intensity :: ! RestartLimit } -- ^ restart only the failed child process
457
+ { intensity :: ! RestartLimit
458
+ } -- ^ restart only the failed child process
450
459
| RestartAll
451
- { intensity :: ! RestartLimit
452
- , mode :: ! RestartMode
460
+ { intensity :: ! RestartLimit
461
+ , mode :: ! RestartMode
453
462
} -- ^ also restart all siblings
454
463
| RestartLeft
455
- { intensity :: ! RestartLimit
456
- , mode :: ! RestartMode
464
+ { intensity :: ! RestartLimit
465
+ , mode :: ! RestartMode
457
466
} -- ^ restart prior siblings (i.e., prior /start order/)
458
467
| RestartRight
459
- { intensity :: ! RestartLimit
460
- , mode :: ! RestartMode
468
+ { intensity :: ! RestartLimit
469
+ , mode :: ! RestartMode
461
470
} -- ^ restart subsequent siblings (i.e., subsequent /start order/)
462
471
deriving (Typeable , Generic , Show )
463
472
instance Binary RestartStrategy where
@@ -542,7 +551,7 @@ instance NFData ChildType where
542
551
data RestartPolicy =
543
552
Permanent -- ^ a permanent child will always be restarted
544
553
| 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
546
555
| Intrinsic -- ^ as 'Transient', but if the child exits normally, the supervisor also exits normally
547
556
deriving (Typeable , Generic , Eq , Show )
548
557
instance Binary RestartPolicy where
@@ -819,13 +828,14 @@ instance Logger LogSink where
819
828
logMessage (LogProcess client') = logMessage client'
820
829
821
830
data 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
829
839
}
830
840
831
841
--------------------------------------------------------------------------------
@@ -837,13 +847,13 @@ data State = State {
837
847
--
838
848
-- > start = spawnLocal . run
839
849
--
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
842
852
843
853
-- | Run the supplied children using the provided restart strategy.
844
854
--
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
847
857
848
858
--------------------------------------------------------------------------------
849
859
-- Client Facing API --
@@ -935,8 +945,8 @@ shutdownAndWait sid = do
935
945
-- Server Initialisation/Startup --
936
946
--------------------------------------------------------------------------------
937
947
938
- supInit :: InitHandler (RestartStrategy , [ChildSpec ]) State
939
- supInit (strategy', specs') = do
948
+ supInit :: InitHandler (RestartStrategy , ShutdownMode , [ChildSpec ]) State
949
+ supInit (strategy', shutdown', specs') = do
940
950
logClient <- Log. client
941
951
let client' = case logClient of
942
952
Nothing -> LogChan
@@ -946,7 +956,7 @@ supInit (strategy', specs') = do
946
956
)
947
957
. (strategy ^= strategy')
948
958
. (logger ^= client')
949
- $ emptyState
959
+ $ emptyState shutdown'
950
960
)
951
961
-- TODO: should we return Ignore, as per OTP's supervisor, if no child starts?
952
962
(foldlM initChild initState specs' >>= return . (flip InitOk ) Infinity )
@@ -982,15 +992,16 @@ initialised state spec (Right ref) = do
982
992
-- Server Definition/State --
983
993
--------------------------------------------------------------------------------
984
994
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
994
1005
}
995
1006
996
1007
emptyStats :: SupervisorStats
@@ -1218,7 +1229,6 @@ handleMonitorSignal state (ProcessMonitorNotification _ pid reason) = do
1218
1229
--------------------------------------------------------------------------------
1219
1230
1220
1231
handleShutdown :: State -> ExitReason -> Process ()
1221
- -- TODO: stop all our children from left to right...
1222
1232
handleShutdown state (ExitOther reason) = terminateChildren state >> die reason
1223
1233
handleShutdown state _ = terminateChildren state
1224
1234
@@ -1260,7 +1270,7 @@ tryRestartBranch rs sp dr st = -- TODO: use DiedReason for logging...
1260
1270
RestartAll _ _ -> childSpecs
1261
1271
RestartLeft _ _ -> subTreeL
1262
1272
RestartRight _ _ -> subTreeR
1263
- _ -> error " IllegalState"
1273
+ _ -> error " IllegalState"
1264
1274
proc = case mode' of
1265
1275
RestartEach _ -> stopStart
1266
1276
RestartInOrder _ -> restartL
@@ -1628,17 +1638,24 @@ filterInitFailures sup pid ex = do
1628
1638
1629
1639
terminateChildren :: State -> Process ()
1630
1640
terminateChildren 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)
1639
1656
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'
1642
1659
1643
1660
collectExits :: [DiedReason ]
1644
1661
-> [ProcessId ]
@@ -1667,7 +1684,7 @@ doTerminateChild ref spec state = do
1667
1684
)
1668
1685
where
1669
1686
shutdownComplete :: State -> ProcessId -> DiedReason -> Process State
1670
- shutdownComplete _ _ DiedNormal = return $ updateStopped
1687
+ shutdownComplete _ _ DiedNormal = return $ updateStopped
1671
1688
shutdownComplete state' pid (r :: DiedReason ) = do
1672
1689
logShutdown (state' ^. logger) chKey pid r >> return state'
1673
1690
0 commit comments