Skip to content

Commit f496fff

Browse files
committed
Prioritised process loop exception handling improvements
There were previously numerous places in which the code evaluated during `precvLoop` could be terminated by an asynchronous exception, without attempting to handle exits properly or even call the server's shutdown handler. Fixing this makes the already messy priority code even more complex, but does seem to have done the trick. [ci skip]
1 parent 8e25ab0 commit f496fff

File tree

1 file changed

+46
-41
lines changed
  • src/Control/Distributed/Process/Platform/ManagedProcess/Internal

1 file changed

+46
-41
lines changed

src/Control/Distributed/Process/Platform/ManagedProcess/Internal/GenProcess.hs

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -61,34 +61,54 @@ recvQueue :: PrioritisedProcessDefinition s
6161
-> Queue
6262
-> Process ExitReason
6363
recvQueue p s t q =
64-
let pDef = processDef p
65-
ps = priorities p
66-
handleStop = shutdownHandler pDef
67-
in do
68-
(ac, d, q') <- processNext pDef ps s t q
69-
case ac of
70-
(ProcessContinue s') -> recvQueueAux p ps s' d q'
71-
(ProcessTimeout t' s') -> recvQueueAux p ps s' t' q'
72-
(ProcessHibernate d' s') -> block d' >> recvQueueAux p ps s' d q'
73-
(ProcessStop r) -> handleStop s r >> return (r :: ExitReason)
74-
(ProcessStopping s' r) -> handleStop s' r >> return (r :: ExitReason)
64+
let pDef = processDef p
65+
ps = priorities p
66+
in do (ac, d, q') <- catchExit (processNext pDef ps s t q)
67+
(\_ (r :: ExitReason) ->
68+
return (ProcessStop r, Infinity, q))
69+
nextAction ac d q'
7570
where
76-
recvQueueAux :: PrioritisedProcessDefinition s
77-
-> [DispatchPriority s]
78-
-> s
79-
-> Delay
80-
-> Queue
81-
-> Process ExitReason
82-
recvQueueAux ppDef prioritizers pState delay queue = do
83-
t' <- startTimer delay
84-
drainMessageQueue pState prioritizers queue >>= recvQueue ppDef pState t'
71+
nextAction ac d q'
72+
| ProcessContinue s' <- ac = recvQueueAux p (priorities p) s' d q'
73+
| ProcessTimeout t' s' <- ac = recvQueueAux p (priorities p) s' t' q'
74+
| ProcessHibernate d' s' <- ac = block d' >> recvQueueAux p (priorities p) s' d q'
75+
| ProcessStop r <- ac = (shutdownHandler $ processDef p) s r >> return r
76+
| ProcessStopping s' r <- ac = (shutdownHandler $ processDef p) s' r >> return r
77+
| otherwise {- compiler foo -} = die "IllegalState"
78+
79+
recvQueueAux ppDef prioritizers pState delay queue =
80+
let ex = (trapExit:(exitHandlers $ processDef ppDef))
81+
eh = map (\d' -> (dispatchExit d') pState) ex
82+
in (do t' <- startTimer delay
83+
mq <- drainMessageQueue pState prioritizers queue
84+
recvQueue ppDef pState t' mq)
85+
`catchExit`
86+
(\pid (reason :: ExitReason) -> do
87+
let pd = processDef ppDef
88+
let ps = pState
89+
let pq = queue
90+
let em = unsafeWrapMessage reason
91+
(a, d, q') <- findExitHandlerOrStop pd ps pq eh pid em
92+
nextAction a d q')
93+
94+
findExitHandlerOrStop :: ProcessDefinition s
95+
-> s
96+
-> Queue
97+
-> [ProcessId -> P.Message -> Process (Maybe (ProcessAction s))]
98+
-> ProcessId
99+
-> P.Message
100+
-> Process (ProcessAction s, Delay, Queue)
101+
findExitHandlerOrStop _ _ pq [] _ er = do
102+
mEr <- unwrapMessage er :: Process (Maybe ExitReason)
103+
case mEr of
104+
Nothing -> die "InvalidExitHandler" -- TODO: better error message?
105+
Just er' -> return (ProcessStop er', Infinity, pq)
106+
findExitHandlerOrStop pd ps pq (eh:ehs) pid er = do
107+
mAct <- eh pid er
108+
case mAct of
109+
Nothing -> findExitHandlerOrStop pd ps pq ehs pid er
110+
Just pa -> return (pa, Infinity, pq)
85111

86-
processNext :: ProcessDefinition s
87-
-> [DispatchPriority s]
88-
-> s
89-
-> TimeoutSpec
90-
-> Queue
91-
-> Process (ProcessAction s, Delay, Queue)
92112
processNext def ps' pState tSpec queue =
93113
let ex = (trapExit:(exitHandlers def))
94114
h = timeoutHandler def in do
@@ -109,10 +129,6 @@ recvQueue p s t q =
109129
(map (\d' -> (dispatchExit d') s') ex)
110130
return (act, t', q')
111131

112-
processApply :: ProcessDefinition s
113-
-> s
114-
-> P.Message
115-
-> Process (ProcessAction s)
116132
processApply def pState msg =
117133
let pol = unhandledMessagePolicy def
118134
apiMatchers = map (dynHandleMessage pol pState) (apiHandlers def)
@@ -121,24 +137,13 @@ recvQueue p s t q =
121137
ms' = (shutdown':apiMatchers) ++ infoMatchers
122138
in processApplyAux ms' pol pState msg
123139

124-
processApplyAux :: [(P.Message -> Process (Maybe (ProcessAction s)))]
125-
-> UnhandledMessagePolicy
126-
-> s
127-
-> P.Message
128-
-> Process (ProcessAction s)
129140
processApplyAux [] p' s' m' = applyPolicy p' s' m'
130141
processApplyAux (h:hs) p' s' m' = do
131142
attempt <- h m'
132143
case attempt of
133144
Nothing -> processApplyAux hs p' s' m'
134145
Just act -> return act
135146

136-
drainOrTimeout :: s
137-
-> Delay
138-
-> Queue
139-
-> [DispatchPriority s]
140-
-> TimeoutHandler s
141-
-> Process (ProcessAction s, Delay, Queue)
142147
drainOrTimeout pState delay queue ps' h = do
143148
let matches = [ matchMessage return ]
144149
recv = case delay of

0 commit comments

Comments
 (0)