@@ -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 .
109109func (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