Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0

properly handle os.Interrupt Signal

This makes systemd not put unit into fail mode when stopping

INFO:

* make sure to use signals.RegisterCloser for everything that should be closed on interrupt

* for any net.Listeners created make sure to wrap them with network.WrapListener and register with signals.RegisterCloser
Cette révision appartient à :
Jeff Becker 2017-05-10 08:23:29 -04:00
Parent 252f6a14b4
révision 73f77f1624
9 fichiers modifiés avec 162 ajouts et 3 suppressions

4
.gitignore externe
Voir le fichier

@ -14,3 +14,7 @@ templates/*.html.go
*.backup
tags
*.retry
# emacs temp files
*\#*
*~

Voir le fichier

@ -40,10 +40,18 @@ func RunServer(conf *config.Config) {
l, err := network.CreateHTTPListener(conf)
log.CheckError(err)
if err == nil {
// add http server to be closed gracefully
signals.RegisterCloser(&network.GracefulHttpCloser{
Server: srv,
Listener: l,
})
log.Infof("listening on %s", l.Addr())
err := srv.Serve(l)
if err != nil && err != network.ErrListenerStopped {
log.CheckError(err)
}
}
}
func main() {

17
network/closer.go Fichier normal
Voir le fichier

@ -0,0 +1,17 @@
package network
import (
"net"
"net/http"
)
// implements io.Closer that gracefully closes an http server
type GracefulHttpCloser struct {
Server *http.Server
Listener net.Listener
}
func (c *GracefulHttpCloser) Close() error {
c.Listener.Close()
return c.Server.Shutdown(nil)
}

58
network/graceful.go Fichier normal
Voir le fichier

@ -0,0 +1,58 @@
package network
import (
"errors"
"net"
)
var ErrListenerStopped = errors.New("listener was stopped")
// GracefulListener provides safe and graceful net.Listener wrapper that prevents error on graceful shutdown
type GracefulListener struct {
listener net.Listener
stop chan int
}
func (l *GracefulListener) Accept() (net.Conn, error) {
for {
c, err := l.listener.Accept()
select {
case <-l.stop:
if c != nil {
c.Close()
}
close(l.stop)
l.stop = nil
return nil, ErrListenerStopped
default:
}
if err != nil {
neterr, ok := err.(net.Error)
if ok && neterr.Timeout() && neterr.Temporary() {
continue
}
}
return c, err
}
}
func (l *GracefulListener) Close() (err error) {
l.listener.Close()
if l.stop != nil {
l.stop <- 0
}
return
}
func (l *GracefulListener) Addr() net.Addr {
return l.listener.Addr()
}
// WrapListener wraps a net.Listener such that it can be closed gracefully
func WrapListener(l net.Listener) net.Listener {
return &GracefulListener{
listener: l,
stop: make(chan int),
}
}

Voir le fichier

@ -20,5 +20,8 @@ func CreateHTTPListener(conf *config.Config) (l net.Listener, err error) {
l = s
}
}
if l != nil {
l = WrapListener(l)
}
return
}

26
util/signals/closers.go Fichier normal
Voir le fichier

@ -0,0 +1,26 @@
package signals
import (
"io"
"sync"
)
var (
closeAccess sync.Mutex
closers []io.Closer
)
// RegisterCloser adds an io.Closer to close on interrupt
func RegisterCloser(c io.Closer) {
closeAccess.Lock()
closers = append(closers, c)
closeAccess.Unlock()
}
func closeClosers() {
closeAccess.Lock()
for idx := range closers {
closers[idx].Close()
}
closeAccess.Unlock()
}

7
util/signals/interrupt.go Fichier normal
Voir le fichier

@ -0,0 +1,7 @@
package signals
// handle interrupt signal, platform independent
func interrupted() {
closeClosers()
handleInterrupt()
}

Voir le fichier

@ -21,7 +21,7 @@ func handleReload() {
// returns when done
func Handle() {
chnl := make(chan os.Signal)
signal.Notify(chnl, syscall.SIGHUP)
signal.Notify(chnl, syscall.SIGHUP, os.Interrupt)
for {
sig, ok := <-chnl
if !ok {
@ -31,8 +31,17 @@ func Handle() {
case syscall.SIGHUP:
handleReload()
break
case os.Interrupt:
interrupted()
return
default:
break
}
}
}
// unix implementation of interrupt
// called in interrupted()
func handleInterrupt() {
// XXX: put unix specific cleanup here as needed
}

Voir le fichier

@ -2,7 +2,34 @@
package signals
import (
"os"
"os/signal"
)
func Handle() {
// windows has no sighup LOOOOL, this does nothing
// TODO: Something about SIGHUP for Windows
chnl := make(chan os.Signal)
signal.Notify(chnl, os.Interrupt)
for {
sig, ok := <-chnl
if !ok {
break
}
switch sig {
case os.Interrupt:
// this also closes listeners
interrupted()
return
default:
break
}
}
}
// win32 interrupt handler
// called in interrupted()
func handleInterrupt() {
// XXX: put any win32 specific cleanup here as needed
}