Skip to content

Commit

Permalink
chore(go): switch to support older go 1.18 (#8)
Browse files Browse the repository at this point in the history
* Switch to support older go 1.18 #6

* Address golangci-lint comment.
  • Loading branch information
cedric-appdirect authored Nov 26, 2023
1 parent b3c2106 commit 3b053b6
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 28 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module github.com/gin-contrib/graceful

go 1.20
go 1.18

require (
github.com/gin-gonic/gin v1.9.1
github.com/stretchr/testify v1.8.4
golang.org/x/sync v0.4.0
)

require (
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
Expand Down
85 changes: 58 additions & 27 deletions graceful.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/gin-gonic/gin"
"golang.org/x/sync/errgroup"
)

// Graceful is a wrapper around a [gin.Engine] that provides graceful shutdown
Expand All @@ -17,6 +18,7 @@ type Graceful struct {

started context.Context
stop context.CancelFunc
err chan error

lock sync.Mutex
servers []*http.Server
Expand Down Expand Up @@ -110,40 +112,37 @@ func (g *Graceful) RunListener(listener net.Listener) error {
// :8080 if none are configured) and starts listening and serving HTTP requests. If the passed
// context is canceled, the server is gracefully shut down
func (g *Graceful) RunWithContext(ctx context.Context) error {
var wg sync.WaitGroup
if err := g.ensureAtLeastDefaultServer(); err != nil {
return err
}

ctx, cancel := context.WithCancelCause(ctx)
ctx, cancel := context.WithCancel(ctx)
go func() {
<-ctx.Done()
_ = g.Shutdown(ctx)
}()
defer cancel(nil)
defer cancel()

g.lock.Lock()
eg := errgroup.Group{}

if len(g.listenAndServe) == 0 {
if err := g.apply(WithAddr(":8080")); err != nil {
return err
}
}
g.lock.Lock()

for _, srv := range g.listenAndServe {
wg.Add(1)
go func(srv listenAndServe) {
defer wg.Done()
if err := srv(); err != nil && err != http.ErrServerClosed {
cancel(err)
_ = g.Shutdown(ctx)
safeCopy := srv
eg.Go(func() error {
if err := safeCopy(); err != nil && err != http.ErrServerClosed {
return err
}
}(srv)
return nil
})
}

g.lock.Unlock()

wg.Wait()
if ctx.Err() != nil {
return context.Cause(ctx)
if err := waitWithContext(ctx, &eg); err != nil {
return err
}
return nil
return g.Shutdown(ctx)
}

// Shutdown gracefully shuts down the server without interrupting any active connections.
Expand Down Expand Up @@ -173,11 +172,13 @@ func (g *Graceful) Start() error {
return ErrAlreadyStarted
}

ctxStarted, cancel := context.WithCancelCause(context.Background())
g.err = make(chan error)
ctxStarted, cancel := context.WithCancel(context.Background())
ctx, cancelStop := context.WithCancel(context.Background())
go func() {
err := g.RunWithContext(ctx)
cancel(err)
cancel()
g.err <- err
}()

g.stop = cancelStop
Expand All @@ -189,30 +190,36 @@ func (g *Graceful) Start() error {
// Stop will stop the Graceful instance previously started with Start. It
// will return once the instance has been stopped.
func (g *Graceful) Stop() error {
resetStartedState := func() (context.Context, context.CancelFunc, error) {
resetStartedState := func() (context.Context, context.CancelFunc, chan error, error) {
g.lock.Lock()
defer g.lock.Unlock()

if g.started == nil {
return nil, nil, ErrNotStarted
return nil, nil, nil, ErrNotStarted
}

stop := g.stop
started := g.started
chErr := g.err
g.stop = nil
g.started = nil

return started, stop, nil
return started, stop, chErr, nil
}
started, stop, err := resetStartedState()
started, stop, chErr, err := resetStartedState()
if err != nil {
return err
}

stop()
err = <-chErr
<-started.Done()

err = context.Cause(started)
if !errors.Is(err, context.Canceled) {
return err
}

err = started.Err()
if errors.Is(err, context.Canceled) {
err = nil
}
Expand Down Expand Up @@ -259,4 +266,28 @@ func (g *Graceful) appendHTTPServer() *http.Server {
return srv
}

func (g *Graceful) ensureAtLeastDefaultServer() error {
g.lock.Lock()
defer g.lock.Unlock()

if len(g.listenAndServe) == 0 {
if err := g.apply(WithAddr(":8080")); err != nil {
return err
}
}
return nil
}

func waitWithContext(ctx context.Context, eg *errgroup.Group) error {
if err := eg.Wait(); err != nil {
return err
}
select {
case <-ctx.Done():
return ctx.Err()
default:
return nil
}
}

func donothing() {}

0 comments on commit 3b053b6

Please sign in to comment.