@@ -57,10 +57,11 @@ type Module struct { //nolint:maligned
5757 // start
5858 startComplete chan struct {}
5959 // stop
60- Ctx context.Context
61- cancelCtx func ()
62- stopFlag * abool.AtomicBool
63- stopComplete chan struct {}
60+ Ctx context.Context
61+ cancelCtx func ()
62+ stopFlag * abool.AtomicBool
63+ stopCompleted * abool.AtomicBool
64+ stopComplete chan struct {}
6465
6566 // workers/tasks
6667 ctrlFuncRunning * abool.AtomicBool
@@ -255,12 +256,10 @@ func (m *Module) checkIfStopComplete() {
255256 atomic .LoadInt32 (m .taskCnt ) == 0 &&
256257 atomic .LoadInt32 (m .microTaskCnt ) == 0 {
257258
258- m .Lock ()
259- defer m .Unlock ()
260-
261- if m .stopComplete != nil {
259+ if m .stopCompleted .SetToIf (false , true ) {
260+ m .Lock ()
261+ defer m .Unlock ()
262262 close (m .stopComplete )
263- m .stopComplete = nil
264263 }
265264 }
266265}
@@ -283,60 +282,56 @@ func (m *Module) stop(reports chan *report) {
283282 // Reset start/stop signal channels.
284283 m .startComplete = make (chan struct {})
285284 m .stopComplete = make (chan struct {})
285+ m .stopCompleted .SetTo (false )
286286
287- // Make a copy of the stop channel.
288- stopComplete := m .stopComplete
289-
290- // Set status and cancel context.
287+ // Set status.
291288 m .status = StatusStopping
292- m .stopFlag .Set ()
293- m .cancelCtx ()
294289
295- go m .stopAllTasks (reports , stopComplete )
290+ go m .stopAllTasks (reports )
296291}
297292
298- func (m * Module ) stopAllTasks (reports chan * report , stopComplete chan struct {}) {
299- // start shutdown function
300- var stopFnError error
301- stopFuncRunning := abool .New ()
302- if m .stopFn != nil {
303- stopFuncRunning .Set ()
304- go func () {
305- stopFnError = m .runCtrlFn ("stop module" , m .stopFn )
306- stopFuncRunning .UnSet ()
307- m .checkIfStopComplete ()
308- }()
309- } else {
310- m .checkIfStopComplete ()
311- }
293+ func (m * Module ) stopAllTasks (reports chan * report ) {
294+ // Manually set the control function flag in order to stop completion by race
295+ // condition before stop function has even started.
296+ m .ctrlFuncRunning .Set ()
297+
298+ // Set stop flag for everyone checking this flag before we activate any stop trigger.
299+ m .stopFlag .Set ()
300+
301+ // Cancel the context to notify all workers and tasks.
302+ m .cancelCtx ()
303+
304+ // Start stop function.
305+ stopFnError := m .startCtrlFn ("stop module" , m .stopFn )
312306
313307 // wait for results
314308 select {
315- case <- stopComplete :
316- // case <-time.After(moduleStopTimeout):
309+ case <- m . stopComplete :
310+ // Complete!
317311 case <- time .After (moduleStopTimeout ):
318312 log .Warningf (
319- "%s: timed out while waiting for stopfn/workers/tasks to finish: stopFn=%v/%v workers=%d tasks=%d microtasks=%d, continuing shutdown..." ,
313+ "%s: timed out while waiting for stopfn/workers/tasks to finish: stopFn=%v workers=%d tasks=%d microtasks=%d, continuing shutdown..." ,
320314 m .Name ,
321- stopFuncRunning . IsSet (), m .ctrlFuncRunning .IsSet (),
315+ m .ctrlFuncRunning .IsSet (),
322316 atomic .LoadInt32 (m .workerCnt ),
323317 atomic .LoadInt32 (m .taskCnt ),
324318 atomic .LoadInt32 (m .microTaskCnt ),
325319 )
326320 }
327321
328- // collect error
322+ // Check for stop fn status.
329323 var err error
330- if stopFuncRunning .IsNotSet () && stopFnError != nil {
331- err = stopFnError
332- }
333- // set status
334- if err != nil {
335- m .Error (
336- fmt .Sprintf ("%s:stop-failed" , m .Name ),
337- fmt .Sprintf ("Stopping module %s failed" , m .Name ),
338- fmt .Sprintf ("Failed to stop module: %s" , err .Error ()),
339- )
324+ select {
325+ case err = <- stopFnError :
326+ if err != nil {
327+ // Set error as module error.
328+ m .Error (
329+ fmt .Sprintf ("%s:stop-failed" , m .Name ),
330+ fmt .Sprintf ("Stopping module %s failed" , m .Name ),
331+ fmt .Sprintf ("Failed to stop module: %s" , err .Error ()),
332+ )
333+ }
334+ default :
340335 }
341336
342337 // Always set to offline in order to let other modules shutdown in order.
@@ -384,7 +379,7 @@ func initNewModule(name string, prep, start, stop func() error, dependencies ...
384379 Name : name ,
385380 enabled : abool .NewBool (false ),
386381 enabledAsDependency : abool .NewBool (false ),
387- sleepMode : abool .NewBool (true ),
382+ sleepMode : abool .NewBool (true ), // Change (for init) is triggered below.
388383 sleepWaitingChannel : make (chan time.Time ),
389384 prepFn : prep ,
390385 startFn : start ,
@@ -393,6 +388,7 @@ func initNewModule(name string, prep, start, stop func() error, dependencies ...
393388 Ctx : ctx ,
394389 cancelCtx : cancelCtx ,
395390 stopFlag : abool .NewBool (false ),
391+ stopCompleted : abool .NewBool (true ),
396392 ctrlFuncRunning : abool .NewBool (false ),
397393 workerCnt : & workerCnt ,
398394 taskCnt : & taskCnt ,
@@ -401,7 +397,7 @@ func initNewModule(name string, prep, start, stop func() error, dependencies ...
401397 depNames : dependencies ,
402398 }
403399
404- // Sleep mode is disabled by default
400+ // Sleep mode is disabled by default.
405401 newModule .Sleep (false )
406402
407403 return newModule
0 commit comments