Initial commit
This commit is contained in:
parent
4e78a4cf3c
commit
b824b89965
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) <year> <copyright holders>
|
||||
Copyright (c) 2021 Jan-Lukas Else
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# go-shutdowner
|
||||
|
||||
Simple library for graceful shutdowns
|
||||
A simple library for graceful shutdowns
|
||||
|
||||
100% covered with unit tests.
|
|
@ -0,0 +1,9 @@
|
|||
module git.jlel.se/jlelse/go-shutdowner
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/stretchr/testify v1.7.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
|
@ -0,0 +1,13 @@
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,80 @@
|
|||
package goshutdowner
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Simple struct, use like a sync.Mutex
|
||||
//
|
||||
// var s goshutdowner.Shutdowner
|
||||
// s.Add(func() {
|
||||
// log.Println("Shutting down")
|
||||
// })
|
||||
type Shutdowner struct {
|
||||
initialized bool
|
||||
quit chan os.Signal
|
||||
funcs []ShutdownFunc
|
||||
wg sync.WaitGroup
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
type ShutdownFunc func()
|
||||
|
||||
// Internal method
|
||||
func (s *Shutdowner) init() {
|
||||
if s.initialized {
|
||||
return
|
||||
}
|
||||
s.quit = make(chan os.Signal, 1)
|
||||
signal.Notify(s.quit,
|
||||
os.Interrupt,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM, // e.g. Docker stop
|
||||
)
|
||||
go func() {
|
||||
<-s.quit
|
||||
s.Shutdown()
|
||||
}()
|
||||
s.initialized = true
|
||||
}
|
||||
|
||||
// Add a func, that should be called with s.Shutdown() or when receiving a shutdown signal
|
||||
func (s *Shutdowner) Add(f ShutdownFunc) {
|
||||
s.init()
|
||||
s.mutex.Lock()
|
||||
s.wg.Add(1)
|
||||
s.funcs = append(s.funcs, f)
|
||||
s.mutex.Unlock()
|
||||
}
|
||||
|
||||
// Trigger shutdown directly
|
||||
func (s *Shutdowner) Shutdown() {
|
||||
s.init()
|
||||
s.mutex.RLock()
|
||||
for _, f := range s.funcs {
|
||||
go func(f func()) {
|
||||
defer s.wg.Done()
|
||||
f()
|
||||
}(f)
|
||||
}
|
||||
s.mutex.RUnlock()
|
||||
s.wg.Wait()
|
||||
}
|
||||
|
||||
// Wait till all functions finished
|
||||
func (s *Shutdowner) Wait() {
|
||||
s.init()
|
||||
s.wg.Wait()
|
||||
}
|
||||
|
||||
// Shutdown and wait till shutdown finished. Shorthand for:
|
||||
//
|
||||
// s.Shutdown()
|
||||
// s.Wait()
|
||||
func (s *Shutdowner) ShutdownAndWait() {
|
||||
s.Shutdown()
|
||||
s.Wait()
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package goshutdowner
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_shutdowner(t *testing.T) {
|
||||
t.Run("Simple test", func(t *testing.T) {
|
||||
var s Shutdowner
|
||||
var testBool1, testBool2, testBool3 bool
|
||||
s.Add(func() {
|
||||
testBool1 = true
|
||||
})
|
||||
s.Add(func() {
|
||||
testBool2 = true
|
||||
})
|
||||
s.ShutdownAndWait()
|
||||
assert.True(t, testBool1)
|
||||
assert.True(t, testBool2)
|
||||
assert.False(t, testBool3)
|
||||
})
|
||||
t.Run("Signal test", func(t *testing.T) {
|
||||
var s Shutdowner
|
||||
var testBool1 bool
|
||||
s.Add(func() {
|
||||
testBool1 = true
|
||||
})
|
||||
s.quit <- os.Interrupt
|
||||
s.Wait()
|
||||
assert.True(t, testBool1)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue