Browse Source

libs: Handle SIGHUP explicitly inside autofile (#2480)

* handle SIGHUP explicitly inside autofile
Refs #2260
* libs: Use consistent channel suffix
pull/2486/merge
Anton Kaliaev 6 years ago
committed by Alexander Simmerl
parent
commit
eb0da7f9cb
3 changed files with 52 additions and 105 deletions
  1. +51
    -28
      libs/autofile/autofile.go
  2. +1
    -8
      libs/autofile/autofile_test.go
  3. +0
    -69
      libs/autofile/sighup_watcher.go

+ 51
- 28
libs/autofile/autofile.go View File

@ -2,7 +2,9 @@ package autofile
import ( import (
"os" "os"
"os/signal"
"sync" "sync"
"syscall"
"time" "time"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
@ -32,50 +34,70 @@ if err != nil {
*/ */
const ( const (
autoFileOpenDuration = 1000 * time.Millisecond
autoFilePerms = os.FileMode(0600)
autoFileClosePeriod = 1000 * time.Millisecond
autoFilePerms = os.FileMode(0600)
) )
// Automatically closes and re-opens file for writing.
// AutoFile automatically closes and re-opens file for writing. The file is
// automatically setup to close itself every 1s and upon receiving SIGHUP.
//
// This is useful for using a log file with the logrotate tool. // This is useful for using a log file with the logrotate tool.
type AutoFile struct { type AutoFile struct {
ID string
Path string
ticker *time.Ticker
tickerStopped chan struct{} // closed when ticker is stopped
mtx sync.Mutex
file *os.File
ID string
Path string
closeTicker *time.Ticker
closeTickerStopc chan struct{} // closed when closeTicker is stopped
hupc chan os.Signal
mtx sync.Mutex
file *os.File
} }
func OpenAutoFile(path string) (af *AutoFile, err error) {
af = &AutoFile{
ID: cmn.RandStr(12) + ":" + path,
Path: path,
ticker: time.NewTicker(autoFileOpenDuration),
tickerStopped: make(chan struct{}),
// OpenAutoFile creates an AutoFile in the path (with random ID). If there is
// an error, it will be of type *PathError or *ErrPermissionsChanged (if file's
// permissions got changed (should be 0600)).
func OpenAutoFile(path string) (*AutoFile, error) {
af := &AutoFile{
ID: cmn.RandStr(12) + ":" + path,
Path: path,
closeTicker: time.NewTicker(autoFileClosePeriod),
closeTickerStopc: make(chan struct{}),
} }
if err = af.openFile(); err != nil {
return
if err := af.openFile(); err != nil {
af.Close()
return nil, err
} }
go af.processTicks()
sighupWatchers.addAutoFile(af)
return
// Close file on SIGHUP.
af.hupc = make(chan os.Signal, 1)
signal.Notify(af.hupc, syscall.SIGHUP)
go func() {
for range af.hupc {
af.closeFile()
}
}()
go af.closeFileRoutine()
return af, nil
} }
func (af *AutoFile) Close() error { func (af *AutoFile) Close() error {
af.ticker.Stop()
close(af.tickerStopped)
err := af.closeFile()
sighupWatchers.removeAutoFile(af)
return err
af.closeTicker.Stop()
close(af.closeTickerStopc)
if af.hupc != nil {
close(af.hupc)
}
return af.closeFile()
} }
func (af *AutoFile) processTicks() {
func (af *AutoFile) closeFileRoutine() {
for { for {
select { select {
case <-af.ticker.C:
case <-af.closeTicker.C:
af.closeFile() af.closeFile()
case <-af.tickerStopped:
case <-af.closeTickerStopc:
return return
} }
} }
@ -89,6 +111,7 @@ func (af *AutoFile) closeFile() (err error) {
if file == nil { if file == nil {
return nil return nil
} }
af.file = nil af.file = nil
return file.Close() return file.Close()
} }


+ 1
- 8
libs/autofile/autofile_test.go View File

@ -3,7 +3,6 @@ package autofile
import ( import (
"io/ioutil" "io/ioutil"
"os" "os"
"sync/atomic"
"syscall" "syscall"
"testing" "testing"
"time" "time"
@ -37,13 +36,10 @@ func TestSIGHUP(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// Send SIGHUP to self. // Send SIGHUP to self.
oldSighupCounter := atomic.LoadInt32(&sighupCounter)
syscall.Kill(syscall.Getpid(), syscall.SIGHUP) syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
// Wait a bit... signals are not handled synchronously. // Wait a bit... signals are not handled synchronously.
for atomic.LoadInt32(&sighupCounter) == oldSighupCounter {
time.Sleep(time.Millisecond * 10)
}
time.Sleep(time.Millisecond * 10)
// Write more to the file. // Write more to the file.
_, err = af.Write([]byte("Line 3\n")) _, err = af.Write([]byte("Line 3\n"))
@ -87,7 +83,4 @@ func TestOpenAutoFilePerms(t *testing.T) {
} else { } else {
t.Errorf("unexpected error %v", e) t.Errorf("unexpected error %v", e)
} }
err = af.Close()
require.NoError(t, err)
} }

+ 0
- 69
libs/autofile/sighup_watcher.go View File

@ -1,69 +0,0 @@
package autofile
import (
"os"
"os/signal"
"sync"
"sync/atomic"
"syscall"
)
func init() {
initSighupWatcher()
}
var sighupWatchers *SighupWatcher
var sighupCounter int32 // For testing
func initSighupWatcher() {
sighupWatchers = newSighupWatcher()
hup := make(chan os.Signal, 1)
signal.Notify(hup, syscall.SIGHUP)
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
go func() {
select {
case <-hup:
sighupWatchers.closeAll()
atomic.AddInt32(&sighupCounter, 1)
case <-quit:
return
}
}()
}
// Watchces for SIGHUP events and notifies registered AutoFiles
type SighupWatcher struct {
mtx sync.Mutex
autoFiles map[string]*AutoFile
}
func newSighupWatcher() *SighupWatcher {
return &SighupWatcher{
autoFiles: make(map[string]*AutoFile, 10),
}
}
func (w *SighupWatcher) addAutoFile(af *AutoFile) {
w.mtx.Lock()
w.autoFiles[af.ID] = af
w.mtx.Unlock()
}
// If AutoFile isn't registered or was already removed, does nothing.
func (w *SighupWatcher) removeAutoFile(af *AutoFile) {
w.mtx.Lock()
delete(w.autoFiles, af.ID)
w.mtx.Unlock()
}
func (w *SighupWatcher) closeAll() {
w.mtx.Lock()
for _, af := range w.autoFiles {
af.closeFile()
}
w.mtx.Unlock()
}

Loading…
Cancel
Save