Skip to content

Commit e123bab

Browse files
authored
Introduce individual methods to break complex waitShutdown() termination logic (#110)
* introduced individual methods to break complex termination logic * add missing godoc for private methods
1 parent 1df5ace commit e123bab

File tree

1 file changed

+49
-36
lines changed

1 file changed

+49
-36
lines changed

terminator.go

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func (t *Terminator) Wait(appCtx context.Context, shutdownTimeout time.Duration)
105105
}
106106
}
107107

108-
// waitShutdown waits for the context to be done and then sequentially notifies existing shutdown hooks.
108+
// waitShutdown waits for the context to be canceled and then executes the registered shutdown hooks sequentially.
109109
func (t *Terminator) waitShutdown(appCtx context.Context) {
110110
defer t.wg.Done()
111111

@@ -114,54 +114,67 @@ func (t *Terminator) waitShutdown(appCtx context.Context) {
114114
t.hooksMx.Lock()
115115
defer t.hooksMx.Unlock()
116116

117-
order := make([]int, 0, len(t.hooks))
118-
for k := range t.hooks {
119-
order = append(order, int(k))
117+
for _, order := range t.getSortedOrders() {
118+
t.executeHooksWithOrder(order)
120119
}
121-
sort.Ints(order)
120+
}
122121

123-
for _, o := range order {
124-
o := o
122+
// getSortedOrders returns a slice of hook orders sorted in ascending order.
123+
func (t *Terminator) getSortedOrders() []Order {
124+
orders := make([]Order, 0, len(t.hooks))
125+
for order := range t.hooks {
126+
orders = append(orders, order)
127+
}
128+
sort.Slice(orders, func(i, j int) bool {
129+
return orders[i] < orders[j]
130+
})
131+
return orders
132+
}
125133

126-
runWg := sync.WaitGroup{}
134+
// executeHooksWithOrder executes all hooks associated with the given order concurrently and waits for all to finish.
135+
func (t *Terminator) executeHooksWithOrder(order Order) {
136+
var wg sync.WaitGroup
137+
for _, hook := range t.hooks[order] {
138+
wg.Add(1)
127139

128-
for _, c := range t.hooks[Order(o)] {
129-
runWg.Add(1)
140+
go func(h Hook) {
141+
defer wg.Done()
130142

131-
go func(f Hook) {
132-
defer runWg.Done()
143+
t.executeHook(h)
144+
}(hook)
145+
}
146+
wg.Wait()
147+
}
133148

134-
ctx, cancel := context.WithTimeout(context.Background(), f.timeout)
135-
defer cancel()
149+
// executeHook runs a single hook with a timeout, recovers from panics, and logs the outcome.
150+
func (t *Terminator) executeHook(hook Hook) {
151+
ctx, cancel := context.WithTimeout(context.Background(), hook.timeout)
152+
defer cancel()
136153

137-
var execDuration time.Duration
154+
var duration time.Duration
138155

139-
go func() {
140-
currentTime := time.Now()
156+
start := time.Now()
141157

142-
defer func() {
143-
defer cancel()
158+
go func() {
159+
defer func() {
160+
defer cancel()
144161

145-
execDuration = time.Since(currentTime)
146-
if err := recover(); err != nil {
147-
t.log.Printf("registered hook panicked after %v for %v, recovered: %+v", execDuration, &f, err)
148-
}
149-
}()
162+
duration = time.Since(start)
150163

151-
f.hookFunc(ctx)
152-
}()
164+
if r := recover(); r != nil {
165+
t.log.Printf("registered hook panicked after %v for %v, recovered: %+v", duration, &hook, r)
166+
}
167+
}()
153168

154-
<-ctx.Done() // block until the hookFunc is over OR timeout has been expired
169+
hook.hookFunc(ctx)
170+
}()
155171

156-
switch err := ctx.Err(); {
157-
case errors.Is(err, context.DeadlineExceeded):
158-
t.log.Printf("registered hook timed out after %v for %v", f.timeout, &f)
159-
case errors.Is(err, context.Canceled):
160-
t.log.Printf("registered hook finished termination in %v (out of maximum %v) for %v", execDuration, f.timeout, &f)
161-
}
162-
}(c)
163-
}
172+
<-ctx.Done() // block until the hookFunc is over OR timeout has been expired
164173

165-
runWg.Wait()
174+
switch err := ctx.Err(); {
175+
case errors.Is(err, context.DeadlineExceeded):
176+
t.log.Printf("registered hook timed out after %v for %v", hook.timeout, &hook)
177+
case errors.Is(err, context.Canceled):
178+
t.log.Printf("registered hook finished termination in %v (out of maximum %v) for %v", duration, hook.timeout, &hook)
166179
}
167180
}

0 commit comments

Comments
 (0)