commit
e289af53b6
167
os.go
167
os.go
|
@ -7,19 +7,12 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
GoPath = os.Getenv("GOPATH")
|
GoPath = os.Getenv("GOPATH")
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
initAFSIGHUPWatcher()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TrapSignal(cb func()) {
|
func TrapSignal(cb func()) {
|
||||||
c := make(chan os.Signal, 1)
|
c := make(chan os.Signal, 1)
|
||||||
signal.Notify(c, os.Interrupt)
|
signal.Notify(c, os.Interrupt)
|
||||||
|
@ -112,153 +105,6 @@ func WriteFileAtomic(filePath string, newBytes []byte, mode os.FileMode) error {
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
/* AutoFile usage
|
|
||||||
|
|
||||||
// Create/Append to ./autofile_test
|
|
||||||
af, err := OpenAutoFile("autofile_test")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stream of writes.
|
|
||||||
// During this time, the file may be moved e.g. by logRotate.
|
|
||||||
for i := 0; i < 60; i++ {
|
|
||||||
af.Write([]byte(Fmt("LOOP(%v)", i)))
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the AutoFile
|
|
||||||
err = af.Close()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const autoFileOpenDuration = 1000 * time.Millisecond
|
|
||||||
|
|
||||||
// Automatically closes and re-opens file for writing.
|
|
||||||
// This is useful for using a log file with the logrotate tool.
|
|
||||||
type AutoFile struct {
|
|
||||||
ID string
|
|
||||||
Path string
|
|
||||||
ticker *time.Ticker
|
|
||||||
mtx sync.Mutex
|
|
||||||
file *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func OpenAutoFile(path string) (af *AutoFile, err error) {
|
|
||||||
af = &AutoFile{
|
|
||||||
ID: RandStr(12) + ":" + path,
|
|
||||||
Path: path,
|
|
||||||
ticker: time.NewTicker(autoFileOpenDuration),
|
|
||||||
}
|
|
||||||
if err = af.openFile(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go af.processTicks()
|
|
||||||
autoFileWatchers.addAutoFile(af)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (af *AutoFile) Close() error {
|
|
||||||
af.ticker.Stop()
|
|
||||||
err := af.closeFile()
|
|
||||||
autoFileWatchers.removeAutoFile(af)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (af *AutoFile) processTicks() {
|
|
||||||
for {
|
|
||||||
_, ok := <-af.ticker.C
|
|
||||||
if !ok {
|
|
||||||
return // Done.
|
|
||||||
}
|
|
||||||
af.closeFile()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (af *AutoFile) closeFile() (err error) {
|
|
||||||
af.mtx.Lock()
|
|
||||||
defer af.mtx.Unlock()
|
|
||||||
|
|
||||||
file := af.file
|
|
||||||
if file == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
af.file = nil
|
|
||||||
return file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (af *AutoFile) Write(b []byte) (n int, err error) {
|
|
||||||
af.mtx.Lock()
|
|
||||||
defer af.mtx.Unlock()
|
|
||||||
if af.file == nil {
|
|
||||||
if err = af.openFile(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return af.file.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (af *AutoFile) openFile() error {
|
|
||||||
file, err := os.OpenFile(af.Path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
af.file = file
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
var autoFileWatchers *afSIGHUPWatcher
|
|
||||||
|
|
||||||
func initAFSIGHUPWatcher() {
|
|
||||||
autoFileWatchers = newAFSIGHUPWatcher()
|
|
||||||
|
|
||||||
c := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(c, syscall.SIGHUP)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for _ = range c {
|
|
||||||
autoFileWatchers.closeAll()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
type afSIGHUPWatcher struct {
|
|
||||||
mtx sync.Mutex
|
|
||||||
autoFiles map[string]*AutoFile
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAFSIGHUPWatcher() *afSIGHUPWatcher {
|
|
||||||
return &afSIGHUPWatcher{
|
|
||||||
autoFiles: make(map[string]*AutoFile, 10),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (afw *afSIGHUPWatcher) addAutoFile(af *AutoFile) {
|
|
||||||
afw.mtx.Lock()
|
|
||||||
afw.autoFiles[af.ID] = af
|
|
||||||
afw.mtx.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (afw *afSIGHUPWatcher) removeAutoFile(af *AutoFile) {
|
|
||||||
afw.mtx.Lock()
|
|
||||||
delete(afw.autoFiles, af.ID)
|
|
||||||
afw.mtx.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (afw *afSIGHUPWatcher) closeAll() {
|
|
||||||
afw.mtx.Lock()
|
|
||||||
for _, af := range afw.autoFiles {
|
|
||||||
af.closeFile()
|
|
||||||
}
|
|
||||||
afw.mtx.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
func Tempfile(prefix string) (*os.File, string) {
|
func Tempfile(prefix string) (*os.File, string) {
|
||||||
file, err := ioutil.TempFile("", prefix)
|
file, err := ioutil.TempFile("", prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -267,6 +113,19 @@ func Tempfile(prefix string) (*os.File, string) {
|
||||||
return file, file.Name()
|
return file, file.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Tempdir(prefix string) (*os.File, string) {
|
||||||
|
tempDir := os.TempDir() + "/" + prefix + RandStr(12)
|
||||||
|
err := EnsureDir(tempDir, 0700)
|
||||||
|
if err != nil {
|
||||||
|
panic(Fmt("Error creating temp dir: %v", err))
|
||||||
|
}
|
||||||
|
dir, err := os.Open(tempDir)
|
||||||
|
if err != nil {
|
||||||
|
panic(Fmt("Error opening temp dir: %v", err))
|
||||||
|
}
|
||||||
|
return dir, tempDir
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
func Prompt(prompt string, defaultValue string) (string, error) {
|
func Prompt(prompt string, defaultValue string) (string, error) {
|
||||||
|
|
64
os_test.go
64
os_test.go
|
@ -1,64 +0,0 @@
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSIGHUP(t *testing.T) {
|
|
||||||
|
|
||||||
// First, create an AutoFile writing to a tempfile dir
|
|
||||||
file, name := Tempfile("sighup_test")
|
|
||||||
err := file.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error creating tempfile: %v", err)
|
|
||||||
}
|
|
||||||
// Here is the actual AutoFile
|
|
||||||
af, err := OpenAutoFile(name)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error creating autofile: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write to the file.
|
|
||||||
_, err = af.Write([]byte("Line 1\n"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error writing to autofile: %v", err)
|
|
||||||
}
|
|
||||||
_, err = af.Write([]byte("Line 2\n"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error writing to autofile: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send SIGHUP to self.
|
|
||||||
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
|
|
||||||
|
|
||||||
// Move the file over
|
|
||||||
err = os.Rename(name, name+"_old")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error moving autofile: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write more to the file.
|
|
||||||
_, err = af.Write([]byte("Line 3\n"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error writing to autofile: %v", err)
|
|
||||||
}
|
|
||||||
_, err = af.Write([]byte("Line 4\n"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error writing to autofile: %v", err)
|
|
||||||
}
|
|
||||||
err = af.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error closing autofile")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both files should exist
|
|
||||||
if body := MustReadFile(name + "_old"); string(body) != "Line 1\nLine 2\n" {
|
|
||||||
t.Errorf("Unexpected body %s", body)
|
|
||||||
}
|
|
||||||
if body := MustReadFile(name); string(body) != "Line 3\nLine 4\n" {
|
|
||||||
t.Errorf("Unexpected body %s", body)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
29
service.go
29
service.go
|
@ -65,6 +65,7 @@ type BaseService struct {
|
||||||
name string
|
name string
|
||||||
started uint32 // atomic
|
started uint32 // atomic
|
||||||
stopped uint32 // atomic
|
stopped uint32 // atomic
|
||||||
|
Quit chan struct{}
|
||||||
|
|
||||||
// The "subclass" of BaseService
|
// The "subclass" of BaseService
|
||||||
impl Service
|
impl Service
|
||||||
|
@ -74,6 +75,7 @@ func NewBaseService(log log15.Logger, name string, impl Service) *BaseService {
|
||||||
return &BaseService{
|
return &BaseService{
|
||||||
log: log,
|
log: log,
|
||||||
name: name,
|
name: name,
|
||||||
|
Quit: make(chan struct{}),
|
||||||
impl: impl,
|
impl: impl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,6 +104,8 @@ func (bs *BaseService) Start() (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements Service
|
// Implements Service
|
||||||
|
// NOTE: Do not put anything in here,
|
||||||
|
// that way users don't need to call BaseService.OnStart()
|
||||||
func (bs *BaseService) OnStart() error { return nil }
|
func (bs *BaseService) OnStart() error { return nil }
|
||||||
|
|
||||||
// Implements Service
|
// Implements Service
|
||||||
|
@ -111,6 +115,7 @@ func (bs *BaseService) Stop() bool {
|
||||||
bs.log.Info(Fmt("Stopping %v", bs.name), "impl", bs.impl)
|
bs.log.Info(Fmt("Stopping %v", bs.name), "impl", bs.impl)
|
||||||
}
|
}
|
||||||
bs.impl.OnStop()
|
bs.impl.OnStop()
|
||||||
|
close(bs.Quit)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
if bs.log != nil {
|
if bs.log != nil {
|
||||||
|
@ -121,6 +126,8 @@ func (bs *BaseService) Stop() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements Service
|
// Implements Service
|
||||||
|
// NOTE: Do not put anything in here,
|
||||||
|
// that way users don't need to call BaseService.OnStop()
|
||||||
func (bs *BaseService) OnStop() {}
|
func (bs *BaseService) OnStop() {}
|
||||||
|
|
||||||
// Implements Service
|
// Implements Service
|
||||||
|
@ -151,6 +158,10 @@ func (bs *BaseService) IsRunning() bool {
|
||||||
return atomic.LoadUint32(&bs.started) == 1 && atomic.LoadUint32(&bs.stopped) == 0
|
return atomic.LoadUint32(&bs.started) == 1 && atomic.LoadUint32(&bs.stopped) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bs *BaseService) Wait() {
|
||||||
|
<-bs.Quit
|
||||||
|
}
|
||||||
|
|
||||||
// Implements Servce
|
// Implements Servce
|
||||||
func (bs *BaseService) String() string {
|
func (bs *BaseService) String() string {
|
||||||
return bs.name
|
return bs.name
|
||||||
|
@ -160,25 +171,13 @@ func (bs *BaseService) String() string {
|
||||||
|
|
||||||
type QuitService struct {
|
type QuitService struct {
|
||||||
BaseService
|
BaseService
|
||||||
Quit chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewQuitService(log log15.Logger, name string, impl Service) *QuitService {
|
func NewQuitService(log log15.Logger, name string, impl Service) *QuitService {
|
||||||
|
if log != nil {
|
||||||
|
log.Warn("QuitService is deprecated, use BaseService instead")
|
||||||
|
}
|
||||||
return &QuitService{
|
return &QuitService{
|
||||||
BaseService: *NewBaseService(log, name, impl),
|
BaseService: *NewBaseService(log, name, impl),
|
||||||
Quit: nil,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: when overriding OnStart, must call .QuitService.OnStart().
|
|
||||||
func (qs *QuitService) OnStart() error {
|
|
||||||
qs.Quit = make(chan struct{})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: when overriding OnStop, must call .QuitService.OnStop().
|
|
||||||
func (qs *QuitService) OnStop() {
|
|
||||||
if qs.Quit != nil {
|
|
||||||
close(qs.Quit)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBaseServiceWait(t *testing.T) {
|
||||||
|
|
||||||
|
type TestService struct {
|
||||||
|
BaseService
|
||||||
|
}
|
||||||
|
ts := &TestService{}
|
||||||
|
ts.BaseService = *NewBaseService(nil, "TestService", ts)
|
||||||
|
ts.Start()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ts.Stop()
|
||||||
|
}()
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
ts.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -26,7 +26,9 @@ func NewThrottleTimer(name string, dur time.Duration) *ThrottleTimer {
|
||||||
var ch = make(chan struct{})
|
var ch = make(chan struct{})
|
||||||
var quit = make(chan struct{})
|
var quit = make(chan struct{})
|
||||||
var t = &ThrottleTimer{Name: name, Ch: ch, dur: dur, quit: quit}
|
var t = &ThrottleTimer{Name: name, Ch: ch, dur: dur, quit: quit}
|
||||||
|
t.mtx.Lock()
|
||||||
t.timer = time.AfterFunc(dur, t.fireRoutine)
|
t.timer = time.AfterFunc(dur, t.fireRoutine)
|
||||||
|
t.mtx.Unlock()
|
||||||
t.timer.Stop()
|
t.timer.Stop()
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue