add util.CancellationChild() and run gofmt

This commit is contained in:
Arceliar 2019-07-17 21:15:02 -05:00
parent 06e8403aaf
commit 6bf182e341

View File

@ -1,83 +1,94 @@
package util package util
import ( import (
"errors" "errors"
"sync" "runtime"
"time" "sync"
"runtime" "time"
) )
type Cancellation interface { type Cancellation interface {
Finished() <-chan struct{} Finished() <-chan struct{}
Cancel(error) error Cancel(error) error
Error() error Error() error
} }
func CancellationFinalizer(c Cancellation) { func CancellationFinalizer(c Cancellation) {
c.Cancel(errors.New("finalizer called")) c.Cancel(errors.New("finalizer called"))
} }
type cancellation struct { type cancellation struct {
signal chan error signal chan error
cancel chan struct{} cancel chan struct{}
errMtx sync.RWMutex errMtx sync.RWMutex
err error err error
} }
func (c *cancellation) worker() { func (c *cancellation) worker() {
// Launch this in a separate goroutine when creating a cancellation // Launch this in a separate goroutine when creating a cancellation
err := <-c.signal err := <-c.signal
c.errMtx.Lock() c.errMtx.Lock()
c.err = err c.err = err
c.errMtx.Unlock() c.errMtx.Unlock()
close(c.cancel) close(c.cancel)
} }
func NewCancellation() Cancellation { func NewCancellation() Cancellation {
c := cancellation{ c := cancellation{
signal: make(chan error), signal: make(chan error),
cancel: make(chan struct{}), cancel: make(chan struct{}),
} }
runtime.SetFinalizer(&c, CancellationFinalizer) runtime.SetFinalizer(&c, CancellationFinalizer)
go c.worker() go c.worker()
return &c return &c
} }
func (c *cancellation) Finished() <-chan struct{} { func (c *cancellation) Finished() <-chan struct{} {
return c.cancel return c.cancel
} }
func (c *cancellation) Cancel(err error) error { func (c *cancellation) Cancel(err error) error {
select { select {
case c.signal<-err: case c.signal <- err:
return nil return nil
case <-c.cancel: case <-c.cancel:
return c.Error() return c.Error()
} }
} }
func (c *cancellation) Error() error { func (c *cancellation) Error() error {
c.errMtx.RLock() c.errMtx.RLock()
err := c.err err := c.err
c.errMtx.RUnlock() c.errMtx.RUnlock()
return err return err
}
func CancellationChild(parent Cancellation) Cancellation {
child := NewCancellation()
go func() {
select {
case <-child.Finished():
case <-parent.Finished():
child.Cancel(parent.Error())
}
}()
return child
} }
func CancellationWithTimeout(parent Cancellation, timeout time.Duration) Cancellation { func CancellationWithTimeout(parent Cancellation, timeout time.Duration) Cancellation {
child := NewCancellation() child := CancellationChild(parent)
go func() { go func() {
timer := time.NewTimer(timeout) timer := time.NewTimer(timeout)
defer TimerStop(timer) defer TimerStop(timer)
select { select {
case <-parent.Finished(): case <-child.Finished():
child.Cancel(parent.Error()) case <-timer.C:
case <-timer.C: child.Cancel(errors.New("timeout"))
child.Cancel(errors.New("timeout")) }
} }()
}() return child
return child
} }
func CancellationWithDeadline(parent Cancellation, deadline time.Time) Cancellation { func CancellationWithDeadline(parent Cancellation, deadline time.Time) Cancellation {
return CancellationWithTimeout(parent, deadline.Sub(time.Now())) return CancellationWithTimeout(parent, deadline.Sub(time.Now()))
} }