Skip to content

Instantly share code, notes, and snippets.

@hardeepnarang10
Created September 19, 2023 06:42
Show Gist options
  • Save hardeepnarang10/970ede593ed6d0c4191abdc5ae5fd53f to your computer and use it in GitHub Desktop.
Save hardeepnarang10/970ede593ed6d0c4191abdc5ae5fd53f to your computer and use it in GitHub Desktop.
A practical graceful stop implementation in go, one illustrated with channels, and a second, arguably better with a local context. Channels implementation expects an incoming context; recommended to pass a notify context. Grace duration is independent of the incoming context.
package main
import (
"context"
"fmt"
"log"
"time"
)
func gracefulStop(ctx context.Context, graceDuration time.Duration, stop func() error) error {
errChanIntermediary, errChanFinal := make(chan error), make(chan error)
defer close(errChanFinal)
defer close(errChanIntermediary)
go func(errChan chan<- error) {
if err := stop(); err != nil {
errChan <- fmt.Errorf("stop() call returned an error: %w", err)
}
}(errChanIntermediary)
<-ctx.Done()
preparedCtx, preparedCtxCancel := context.WithTimeout(context.Background(), graceDuration)
go func(prepCtx context.Context, prepCancel func(), errChannelIn <-chan error, errChannelOut chan<- error) {
for {
select {
case <-prepCtx.Done():
return
case err := <-errChannelIn:
errChannelOut <- err
prepCancel()
return
default:
continue
}
}
}(preparedCtx, preparedCtxCancel, errChanIntermediary, errChanFinal)
<-preparedCtx.Done()
if len(errChanFinal) != 0 {
if err := <-errChanFinal; err != nil {
log.Panic(err)
}
}
return nil
}
package main
import (
"context"
"fmt"
"time"
)
func gracefulStop(stop func() error, graceDuration time.Duration, graceTimeoutError error) error {
graceCtx, cancel := context.WithTimeout(context.Background(), graceDuration)
defer cancel()
errChan := make(chan error)
defer close(errChan)
context.AfterFunc(graceCtx, func() {
if err := stop(); err != nil {
errChan <- fmt.Errorf("stop() call returned an error: %w", err)
} else {
errChan <- nil
}
})
for {
select {
case <-graceCtx.Done():
return graceTimeoutError
case err := <-errChan:
if err != nil {
return err
}
return nil
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment