@ -6,6 +6,7 @@ package graceful
import (
import (
"context"
"context"
"runtime/pprof"
"sync"
"sync"
"time"
"time"
@ -62,7 +63,6 @@ type WithCallback func(callback func())
// Similarly the callback function provided to atTerminate must return once termination is complete.
// Similarly the callback function provided to atTerminate must return once termination is complete.
// Please note that use of the atShutdown and atTerminate callbacks will create go-routines that will wait till their respective signals
// Please note that use of the atShutdown and atTerminate callbacks will create go-routines that will wait till their respective signals
// - users must therefore be careful to only call these as necessary.
// - users must therefore be careful to only call these as necessary.
// If run is not expected to run indefinitely RunWithShutdownChan is likely to be more appropriate.
type RunnableWithShutdownFns func ( atShutdown , atTerminate func ( func ( ) ) )
type RunnableWithShutdownFns func ( atShutdown , atTerminate func ( func ( ) ) )
// RunWithShutdownFns takes a function that has both atShutdown and atTerminate callbacks
// RunWithShutdownFns takes a function that has both atShutdown and atTerminate callbacks
@ -70,7 +70,6 @@ type RunnableWithShutdownFns func(atShutdown, atTerminate func(func()))
// Similarly the callback function provided to atTerminate must return once termination is complete.
// Similarly the callback function provided to atTerminate must return once termination is complete.
// Please note that use of the atShutdown and atTerminate callbacks will create go-routines that will wait till their respective signals
// Please note that use of the atShutdown and atTerminate callbacks will create go-routines that will wait till their respective signals
// - users must therefore be careful to only call these as necessary.
// - users must therefore be careful to only call these as necessary.
// If run is not expected to run indefinitely RunWithShutdownChan is likely to be more appropriate.
func ( g * Manager ) RunWithShutdownFns ( run RunnableWithShutdownFns ) {
func ( g * Manager ) RunWithShutdownFns ( run RunnableWithShutdownFns ) {
g . runningServerWaitGroup . Add ( 1 )
g . runningServerWaitGroup . Add ( 1 )
defer g . runningServerWaitGroup . Done ( )
defer g . runningServerWaitGroup . Done ( )
@ -98,32 +97,6 @@ func (g *Manager) RunWithShutdownFns(run RunnableWithShutdownFns) {
} )
} )
}
}
// RunnableWithShutdownChan is a runnable with functions to run at shutdown and terminate.
// After the atShutdown channel is closed, the main function must return once shutdown is complete.
// (Optionally IsHammer may be waited for instead however, this should be avoided if possible.)
// The callback function provided to atTerminate must return once termination is complete.
// Please note that use of the atTerminate function will create a go-routine that will wait till terminate - users must therefore be careful to only call this as necessary.
type RunnableWithShutdownChan func ( atShutdown <- chan struct { } , atTerminate WithCallback )
// RunWithShutdownChan takes a function that has channel to watch for shutdown and atTerminate callbacks
// After the atShutdown channel is closed, the main function must return once shutdown is complete.
// (Optionally IsHammer may be waited for instead however, this should be avoided if possible.)
// The callback function provided to atTerminate must return once termination is complete.
// Please note that use of the atTerminate function will create a go-routine that will wait till terminate - users must therefore be careful to only call this as necessary.
func ( g * Manager ) RunWithShutdownChan ( run RunnableWithShutdownChan ) {
g . runningServerWaitGroup . Add ( 1 )
defer g . runningServerWaitGroup . Done ( )
defer func ( ) {
if err := recover ( ) ; err != nil {
log . Critical ( "PANIC during RunWithShutdownChan: %v\nStacktrace: %s" , err , log . Stack ( 2 ) )
g . doShutdown ( )
}
} ( )
run ( g . IsShutdown ( ) , func ( atTerminate func ( ) ) {
g . RunAtTerminate ( atTerminate )
} )
}
// RunWithShutdownContext takes a function that has a context to watch for shutdown.
// RunWithShutdownContext takes a function that has a context to watch for shutdown.
// After the provided context is Done(), the main function must return once shutdown is complete.
// After the provided context is Done(), the main function must return once shutdown is complete.
// (Optionally the HammerContext may be obtained and waited for however, this should be avoided if possible.)
// (Optionally the HammerContext may be obtained and waited for however, this should be avoided if possible.)
@ -136,7 +109,9 @@ func (g *Manager) RunWithShutdownContext(run func(context.Context)) {
g . doShutdown ( )
g . doShutdown ( )
}
}
} ( )
} ( )
run ( g . ShutdownContext ( ) )
ctx := g . ShutdownContext ( )
pprof . SetGoroutineLabels ( ctx ) // We don't have a label to restore back to but I think this is fine
run ( ctx )
}
}
// RunAtTerminate adds to the terminate wait group and creates a go-routine to run the provided function at termination
// RunAtTerminate adds to the terminate wait group and creates a go-routine to run the provided function at termination
@ -198,6 +173,8 @@ func (g *Manager) doShutdown() {
}
}
g . lock . Lock ( )
g . lock . Lock ( )
g . shutdownCtxCancel ( )
g . shutdownCtxCancel ( )
atShutdownCtx := pprof . WithLabels ( g . hammerCtx , pprof . Labels ( "graceful-lifecycle" , "post-shutdown" ) )
pprof . SetGoroutineLabels ( atShutdownCtx )
for _ , fn := range g . toRunAtShutdown {
for _ , fn := range g . toRunAtShutdown {
go fn ( )
go fn ( )
}
}
@ -214,7 +191,7 @@ func (g *Manager) doShutdown() {
g . doTerminate ( )
g . doTerminate ( )
g . WaitForTerminate ( )
g . WaitForTerminate ( )
g . lock . Lock ( )
g . lock . Lock ( )
g . done CtxCancel( )
g . manager CtxCancel( )
g . lock . Unlock ( )
g . lock . Unlock ( )
} ( )
} ( )
}
}
@ -227,6 +204,8 @@ func (g *Manager) doHammerTime(d time.Duration) {
default :
default :
log . Warn ( "Setting Hammer condition" )
log . Warn ( "Setting Hammer condition" )
g . hammerCtxCancel ( )
g . hammerCtxCancel ( )
atHammerCtx := pprof . WithLabels ( g . terminateCtx , pprof . Labels ( "graceful-lifecycle" , "post-hammer" ) )
pprof . SetGoroutineLabels ( atHammerCtx )
for _ , fn := range g . toRunAtHammer {
for _ , fn := range g . toRunAtHammer {
go fn ( )
go fn ( )
}
}
@ -244,6 +223,9 @@ func (g *Manager) doTerminate() {
default :
default :
log . Warn ( "Terminating" )
log . Warn ( "Terminating" )
g . terminateCtxCancel ( )
g . terminateCtxCancel ( )
atTerminateCtx := pprof . WithLabels ( g . managerCtx , pprof . Labels ( "graceful-lifecycle" , "post-terminate" ) )
pprof . SetGoroutineLabels ( atTerminateCtx )
for _ , fn := range g . toRunAtTerminate {
for _ , fn := range g . toRunAtTerminate {
go fn ( )
go fn ( )
}
}
@ -331,20 +313,20 @@ func (g *Manager) InformCleanup() {
// Done allows the manager to be viewed as a context.Context, it returns a channel that is closed when the server is finished terminating
// Done allows the manager to be viewed as a context.Context, it returns a channel that is closed when the server is finished terminating
func ( g * Manager ) Done ( ) <- chan struct { } {
func ( g * Manager ) Done ( ) <- chan struct { } {
return g . done Ctx. Done ( )
return g . manager Ctx. Done ( )
}
}
// Err allows the manager to be viewed as a context.Context done at Terminate
// Err allows the manager to be viewed as a context.Context done at Terminate
func ( g * Manager ) Err ( ) error {
func ( g * Manager ) Err ( ) error {
return g . done Ctx. Err ( )
return g . manager Ctx. Err ( )
}
}
// Value allows the manager to be viewed as a context.Context done at Terminate
// Value allows the manager to be viewed as a context.Context done at Terminate
func ( g * Manager ) Value ( key interface { } ) interface { } {
func ( g * Manager ) Value ( key interface { } ) interface { } {
return g . done Ctx. Value ( key )
return g . manager Ctx. Value ( key )
}
}
// Deadline returns nil as there is no fixed Deadline for the manager, it allows the manager to be viewed as a context.Context
// Deadline returns nil as there is no fixed Deadline for the manager, it allows the manager to be viewed as a context.Context
func ( g * Manager ) Deadline ( ) ( deadline time . Time , ok bool ) {
func ( g * Manager ) Deadline ( ) ( deadline time . Time , ok bool ) {
return g . done Ctx. Deadline ( )
return g . manager Ctx. Deadline ( )
}
}