Added package documentation and removed sync creation in New since it is not needed unless the stopper is initialized with NewDebug
This commit is contained in:
parent
2268aecd68
commit
b0beb12fcf
1 changed files with 54 additions and 25 deletions
37
stop/stop.go
37
stop/stop.go
|
@ -1,3 +1,17 @@
|
||||||
|
/*Package stop implements the stopper pattern for golang concurrency management. The main use case is to gracefully
|
||||||
|
exit an application. The pattern allows for a hierarchy of stoppers. Each package should have its own unexported stopper.
|
||||||
|
The package should maintain startup and shutdown exported methods. If the stopper should stop when another stopper for
|
||||||
|
a different package stops, the parent argument for the initialization of a stopper be used to create the dependency.
|
||||||
|
|
||||||
|
The package also comes with a debugging tool to help in determining why and where a stopper is not stopping as expected.
|
||||||
|
If a more complex concurrency is used, it is recommended to implement the library using `DoneNamed` and `AddNamed`.
|
||||||
|
In addition to the standard `Done` functionality, it allows a functional named representation of the type of go routine
|
||||||
|
being completed. This works in conjunction with `AddNamed`. If the init of the stopper Group happens with `NewDebug`
|
||||||
|
instead of `New` special tracking and logging is enabled where it will print out the remaining routines' functional name
|
||||||
|
and how many it is waiting on to complete the stop after each call to `DoneNamed`. This allows easy debugging by just
|
||||||
|
changing the init of the stopper instead of all the references as long as the library is implemented with `DoneNamed`
|
||||||
|
and `AddNamed`. For simple uses of the stopper pattern this is not needed and the standard `Add` and `Done` should be used.
|
||||||
|
*/
|
||||||
package stop
|
package stop
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -22,7 +36,7 @@ type Stopper = Group
|
||||||
|
|
||||||
// New allocates and returns a new instance. Use New(parent) to create an instance that is stopped when parent is stopped.
|
// New allocates and returns a new instance. Use New(parent) to create an instance that is stopped when parent is stopped.
|
||||||
func New(parent ...*Group) *Group {
|
func New(parent ...*Group) *Group {
|
||||||
s := &Group{mu: &sync.Mutex{}}
|
s := &Group{}
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
if len(parent) > 0 && parent[0] != nil {
|
if len(parent) > 0 && parent[0] != nil {
|
||||||
ctx = parent[0].ctx
|
ctx = parent[0].ctx
|
||||||
|
@ -31,6 +45,15 @@ func New(parent ...*Group) *Group {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDebug allows you to debug the go routines the group waits on. In order to leverage this, AddNamed and DoneNamed should be used.
|
||||||
|
func NewDebug(parent ...*Group) *Group {
|
||||||
|
s := New(parent...)
|
||||||
|
s.waitingOn = make(map[string]int)
|
||||||
|
s.mu = &sync.Mutex{}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// Ch returns a channel that will be closed when Stop is called.
|
// Ch returns a channel that will be closed when Stop is called.
|
||||||
func (s *Group) Ch() Chan {
|
func (s *Group) Ch() Chan {
|
||||||
return s.ctx.Done()
|
return s.ctx.Done()
|
||||||
|
@ -52,9 +75,11 @@ func (s *Group) Child() *Group {
|
||||||
return New(s)
|
return New(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Group) DebugAdd(delta int, name string) {
|
//AddNamed is the same as Add but will register the functional name of the routine for later output. See `DoneNamed`.
|
||||||
|
func (s *Group) AddNamed(delta int, name string) {
|
||||||
s.Add(delta)
|
s.Add(delta)
|
||||||
|
|
||||||
|
if s.waitingOn != nil {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
@ -67,11 +92,14 @@ func (s *Group) DebugAdd(delta int, name string) {
|
||||||
} else {
|
} else {
|
||||||
s.waitingOn[name] = 1
|
s.waitingOn[name] = 1
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Group) DebugDone(name string) {
|
//DoneNamed is the same as `Done` but will output the functional name of all remaining named routines and the waiting on count.
|
||||||
|
func (s *Group) DoneNamed(name string) {
|
||||||
defer s.Done()
|
defer s.Done()
|
||||||
|
|
||||||
|
if s.waitingOn != nil {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
@ -85,11 +113,12 @@ func (s *Group) DebugDone(name string) {
|
||||||
log.Printf("%s is not recorded in stop group map", name)
|
log.Printf("%s is not recorded in stop group map", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("-->> LIST WAITING ON")
|
log.Printf("-->> LIST ROUTINES WAITING ON")
|
||||||
|
|
||||||
for k, v := range s.waitingOn {
|
for k, v := range s.waitingOn {
|
||||||
if v > 0 {
|
if v > 0 {
|
||||||
log.Printf("waiting on %d %s routines...", v, k)
|
log.Printf("waiting on %d %s routines...", v, k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue