aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Kiryukhin <a.kiryukhin@mail.ru>2019-06-29 01:19:21 +0300
committerAlexander Kiryukhin <a.kiryukhin@mail.ru>2019-06-29 01:19:21 +0300
commit01eeeaf5e136928abe75f95d58f3f9cce11c6fe6 (patch)
tree700f9a5106a51ce1f8b35475c84a7bafac162313
parent741cf397a9150a8f76b8f74289b1dde8aaa43d02 (diff)
Release.v1.0.0
Auto listen OS signal (with mixin) Policies: ...Fail -> ...Error
-rwxr-xr-xREADME.md36
-rw-r--r--event_string.go8
-rw-r--r--events.go4
-rw-r--r--example/policies.go10
-rwxr-xr-xmixins.go16
-rw-r--r--options.go12
-rwxr-xr-xrutina.go30
7 files changed, 76 insertions, 40 deletions
diff --git a/README.md b/README.md
index c6eceab..5b1cde6 100755
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ Package Rutina (russian "рутина" - ordinary boring everyday work) is routi
It seems like https://godoc.org/golang.org/x/sync/errgroup with some different:
1) propagates context to every routines. So routine can check if context stopped (`ctx.Done()`).
-2) has flexible run/stop policy. i.e. one routine restarts when it fails (useful on daemons) but if fails another - all routines will be cancelled
+2) has flexible run/stop policy. i.e. one routine restarts when it errors (useful on daemons) but if errors another - all routines will be cancelled
3) already has optional signal handler `ListenOsSignals()`
## When it need?
@@ -42,16 +42,16 @@ r.Go(func (ctx context.Context) error {
Available options of run policy:
-* `ShutdownIfFail` - Shutdown all routines if this routine fails
-* `RestartIfFail` - Restart this routine if it fail
-* `DoNothingIfFail` - Do nothing just stop this routine if it fail
+* `ShutdownIfError` - Shutdown all routines if this routine returns error
+* `RestartIfError` - Restart this routine if this routine returns error
+* `DoNothingIfError` - Do nothing just stop this routine if this routine returns error
* `ShutdownIfDone` - Shutdown all routines if this routine done without errors
* `RestartIfDone` - Restart if this routine done without errors
* `DoNothingIfDone` - Do nothing if this routine done without errors
Default policy:
-`ShutdownIfFail` && `ShutdownIfDone`
+`ShutdownIfError` && `ShutdownIfDone`
(just like [errgroup](https://godoc.org/golang.org/x/sync/errgroup))
@@ -61,17 +61,17 @@ Default policy:
r.Go(func(ctx context.Context) error {
// If this routine produce no error - it just restarts
// If it returns error - all other routines will shutdown (because context cancels)
-}, rutina.RestartIfDone, rutina.ShutdownIfFail)
+}, rutina.RestartIfDone, rutina.ShutdownIfError)
r.Go(func(ctx context.Context) error {
// If this routine produce no error - it just completes
// If it returns error - all other routines will shutdown (because context cancels)
-}, rutina.DoNothingIfDone, rutina.ShutdownIfFail)
+}, rutina.DoNothingIfDone, rutina.ShutdownIfError)
r.Go(func(ctx context.Context) error {
// If this routine produce no error - all other routines will shutdown (because context cancels)
// If it returns error - it will be restarted
-}, rutina.RestartIfFail)
+}, rutina.RestartIfError)
r.Go(func(ctx context.Context) error {
// If this routine stopped by any case - all other routines will shutdown (because context cancels)
@@ -103,10 +103,10 @@ Rutina has own simple lifecycle events:
* `EventRoutineStart` - Fires when starts new routine
* `EventRoutineStop` - Fires when routine stopped with any result
* `EventRoutineComplete` - Fires when routine stopped without errors
-* `EventRoutineFail` - Fires when routine stopped with error
+* `EventRoutineError` - Fires when routine stopped with error
* `EventAppStop` - Fires when all routines stopped with any result
* `EventAppComplete` - Fires when all routines stopped with no errors
-* `EventAppFail` - Fires when all routines stopped with error
+* `EventAppError` - Fires when all routines stopped with error
## Mixins
@@ -151,6 +151,22 @@ err := <- r.Errors()
Turn on errors channel
+### Lifecycle listener
+
+```go
+r = r.With(rutina.WithLifecycleListener(func (event rutina.Event, rid int) { ... }))
+```
+
+Simple lifecycle listener
+
+### Auto listen OS signals
+
+```go
+r = r.With(rutina.WithListenOsSignals())
+```
+
+Automatically listen OS signals. There is no `r.ListenOsSignals()` needed.
+
## Example
HTTP server with graceful shutdown [`example/http_server.go`](https://github.com/NeonXP/rutina/blob/master/example/http_server.go)
diff --git a/event_string.go b/event_string.go
index 05f1b7a..6501cad 100644
--- a/event_string.go
+++ b/event_string.go
@@ -11,15 +11,15 @@ func _() {
_ = x[EventRoutineStart-0]
_ = x[EventRoutineStop-1]
_ = x[EventRoutineComplete-2]
- _ = x[EventRoutineFail-3]
+ _ = x[EventRoutineError-3]
_ = x[EventAppStop-4]
_ = x[EventAppComplete-5]
- _ = x[EventAppFail-6]
+ _ = x[EventAppError-6]
}
-const _Event_name = "EventRoutineStartEventRoutineStopEventRoutineCompleteEventRoutineFailEventAppStopEventAppCompleteEventAppFail"
+const _Event_name = "EventRoutineStartEventRoutineStopEventRoutineCompleteEventRoutineErrorEventAppStopEventAppCompleteEventAppError"
-var _Event_index = [...]uint8{0, 17, 33, 53, 69, 81, 97, 109}
+var _Event_index = [...]uint8{0, 17, 33, 53, 70, 82, 98, 111}
func (i Event) String() string {
if i < 0 || i >= Event(len(_Event_index)-1) {
diff --git a/events.go b/events.go
index d435e62..6159343 100644
--- a/events.go
+++ b/events.go
@@ -8,10 +8,10 @@ const (
EventRoutineStart Event = iota
EventRoutineStop
EventRoutineComplete
- EventRoutineFail
+ EventRoutineError
EventAppStop
EventAppComplete
- EventAppFail
+ EventAppError
)
// Hook is function that calls when event fired
diff --git a/example/policies.go b/example/policies.go
index 3e14f5a..6addea7 100644
--- a/example/policies.go
+++ b/example/policies.go
@@ -21,31 +21,31 @@ func main() {
<-time.After(1 * time.Second)
log.Println("Do something 1 second without errors and restart")
return nil
- }, rutina.RestartIfDone, rutina.ShutdownIfFail)
+ }, rutina.RestartIfDone, rutina.ShutdownIfError)
r.Go(func(ctx context.Context) error {
<-time.After(2 * time.Second)
log.Println("Do something 2 seconds without errors and do nothing")
return nil
- }, rutina.DoNothingIfDone, rutina.ShutdownIfFail)
+ }, rutina.DoNothingIfDone, rutina.ShutdownIfError)
r.Go(func(ctx context.Context) error {
<-time.After(3 * time.Second)
log.Println("Do something 3 seconds with error and restart")
return errors.New("Error #1!")
- }, rutina.RestartIfFail)
+ }, rutina.RestartIfError)
r.Go(func(ctx context.Context) error {
<-time.After(4 * time.Second)
log.Println("Do something 4 seconds with error and do nothing")
return errors.New("Error #2!")
- }, rutina.DoNothingIfFail)
+ }, rutina.DoNothingIfError)
r.Go(func(ctx context.Context) error {
<-time.After(10 * time.Second)
log.Println("Do something 10 seconds with error and close context")
return errors.New("Successfully shutdown at proper place")
- }, rutina.ShutdownIfFail)
+ }, rutina.ShutdownIfError)
r.Go(func(ctx context.Context) error {
for {
diff --git a/mixins.go b/mixins.go
index 1344d07..aea08ef 100755
--- a/mixins.go
+++ b/mixins.go
@@ -4,6 +4,7 @@ import (
"context"
"log"
"os"
+ "syscall"
)
// Mixin interface
@@ -72,3 +73,18 @@ func (l LifecycleMixin) apply(r *Rutina) {
func WithLifecycleListener(listener LifecycleListener) *LifecycleMixin {
return &LifecycleMixin{Listener: listener}
}
+
+type ListenOsSignalsMixin struct {
+ Signals []os.Signal
+}
+
+func (l ListenOsSignalsMixin) apply(r *Rutina) {
+ r.autoListenSignals = l.Signals
+}
+
+func WithListenOsSignals(signals ...os.Signal) *ListenOsSignalsMixin {
+ if len(signals) == 0 {
+ signals = []os.Signal{syscall.SIGINT, syscall.SIGTERM}
+ }
+ return &ListenOsSignalsMixin{Signals: signals}
+}
diff --git a/options.go b/options.go
index 16c72e8..be91068 100644
--- a/options.go
+++ b/options.go
@@ -4,10 +4,10 @@ package rutina
type Options int
const (
- ShutdownIfFail Options = iota // Shutdown all routines if fail
- RestartIfFail // Restart this routine if fail
- DoNothingIfFail // Do nothing on fail
- ShutdownIfDone // Shutdown all routines if this routine done without errors
- RestartIfDone // Restart if this routine done without errors
- DoNothingIfDone // Do nothing if this routine done without errors
+ ShutdownIfError Options = iota // Shutdown all routines if fail
+ RestartIfError // Restart this routine if fail
+ DoNothingIfError // Do nothing on fail
+ ShutdownIfDone // Shutdown all routines if this routine done without errors
+ RestartIfDone // Restart if this routine done without errors
+ DoNothingIfDone // Do nothing if this routine done without errors
)
diff --git a/rutina.go b/rutina.go
index dccce9e..34d7723 100755
--- a/rutina.go
+++ b/rutina.go
@@ -20,8 +20,9 @@ type Rutina struct {
err error // First error that shutdowns all routines
logger *log.Logger // Optional logger
counter *uint64 // Optional counter that names routines with increment ids for debug purposes at logger
- errCh chan error // Optional channel for errors when RestartIfFail and DoNothingIfFail
+ errCh chan error // Optional channel for errors when RestartIfError and DoNothingIfError
lifecycleListener LifecycleListener // Optional listener for events
+ autoListenSignals []os.Signal // Optional listening os signals, default disabled
}
// New instance with builtin context
@@ -37,6 +38,9 @@ func (r *Rutina) With(mixins ...Mixin) *Rutina {
for _, m := range mixins {
m.apply(r)
}
+ if r.autoListenSignals != nil {
+ r.ListenOsSignals(r.autoListenSignals...)
+ }
return r
}
@@ -46,15 +50,15 @@ func (r *Rutina) Go(doer func(ctx context.Context) error, opts ...Options) {
if r.ctx.Err() != nil {
return
}
- onFail := ShutdownIfFail
+ onFail := ShutdownIfError
for _, o := range opts {
switch o {
- case ShutdownIfFail:
- onFail = ShutdownIfFail
- case RestartIfFail:
- onFail = RestartIfFail
- case DoNothingIfFail:
- onFail = DoNothingIfFail
+ case ShutdownIfError:
+ onFail = ShutdownIfError
+ case RestartIfError:
+ onFail = RestartIfError
+ case DoNothingIfError:
+ onFail = DoNothingIfError
}
}
onDone := ShutdownIfDone
@@ -75,7 +79,7 @@ func (r *Rutina) Go(doer func(ctx context.Context) error, opts ...Options) {
id := atomic.AddUint64(r.counter, 1)
r.lifecycleEvent(EventRoutineStart, int(id))
if err := doer(r.ctx); err != nil {
- r.lifecycleEvent(EventRoutineFail, int(id))
+ r.lifecycleEvent(EventRoutineError, int(id))
r.lifecycleEvent(EventRoutineStop, int(id))
// errors history
if r.errCh != nil {
@@ -83,13 +87,13 @@ func (r *Rutina) Go(doer func(ctx context.Context) error, opts ...Options) {
}
// region routine failed
switch onFail {
- case ShutdownIfFail:
+ case ShutdownIfError:
// Save error only if shutdown all routines
r.onceErr.Do(func() {
r.err = err
})
r.Cancel()
- case RestartIfFail:
+ case RestartIfError:
r.Go(doer, opts...)
}
// endregion
@@ -108,7 +112,7 @@ func (r *Rutina) Go(doer func(ctx context.Context) error, opts ...Options) {
}()
}
-// Errors returns chan for all errors, event if DoNothingIfFail or RestartIfFail set.
+// Errors returns chan for all errors, event if DoNothingIfError or RestartIfError set.
// By default it nil. Use MixinErrChan to turn it on
func (r *Rutina) Errors() <-chan error {
return r.errCh
@@ -140,7 +144,7 @@ func (r *Rutina) Wait() error {
if r.err == nil {
r.lifecycleEvent(EventAppComplete, 0)
} else {
- r.lifecycleEvent(EventAppFail, 0)
+ r.lifecycleEvent(EventAppError, 0)
}
if r.errCh != nil {
close(r.errCh)