2019-07-18 02:09:22 +00:00
|
|
|
package util
|
|
|
|
|
|
|
|
import (
|
2019-07-18 02:15:02 +00:00
|
|
|
"errors"
|
|
|
|
"runtime"
|
|
|
|
"sync"
|
|
|
|
"time"
|
2019-07-18 02:09:22 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Cancellation interface {
|
2019-07-18 02:15:02 +00:00
|
|
|
Finished() <-chan struct{}
|
|
|
|
Cancel(error) error
|
|
|
|
Error() error
|
2019-07-18 02:09:22 +00:00
|
|
|
}
|
|
|
|
|
2019-07-27 23:10:32 +00:00
|
|
|
var CancellationFinalized = errors.New("finalizer called")
|
|
|
|
var CancellationTimeoutError = errors.New("timeout")
|
|
|
|
|
2019-07-18 02:09:22 +00:00
|
|
|
func CancellationFinalizer(c Cancellation) {
|
2019-07-27 23:10:32 +00:00
|
|
|
c.Cancel(CancellationFinalized)
|
2019-07-18 02:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type cancellation struct {
|
2019-07-18 02:15:02 +00:00
|
|
|
cancel chan struct{}
|
2019-07-27 23:10:32 +00:00
|
|
|
mutex sync.RWMutex
|
2019-07-18 02:15:02 +00:00
|
|
|
err error
|
2019-07-27 23:10:32 +00:00
|
|
|
done bool
|
2019-07-18 02:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewCancellation() Cancellation {
|
2019-07-18 02:15:02 +00:00
|
|
|
c := cancellation{
|
|
|
|
cancel: make(chan struct{}),
|
|
|
|
}
|
|
|
|
runtime.SetFinalizer(&c, CancellationFinalizer)
|
|
|
|
return &c
|
2019-07-18 02:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *cancellation) Finished() <-chan struct{} {
|
2019-07-18 02:15:02 +00:00
|
|
|
return c.cancel
|
2019-07-18 02:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *cancellation) Cancel(err error) error {
|
2019-07-27 23:10:32 +00:00
|
|
|
c.mutex.Lock()
|
|
|
|
defer c.mutex.Unlock()
|
|
|
|
if c.done {
|
|
|
|
return c.err
|
|
|
|
} else {
|
|
|
|
c.err = err
|
|
|
|
c.done = true
|
|
|
|
close(c.cancel)
|
2019-07-18 02:15:02 +00:00
|
|
|
return nil
|
|
|
|
}
|
2019-07-18 02:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *cancellation) Error() error {
|
2019-07-27 23:10:32 +00:00
|
|
|
c.mutex.RLock()
|
2019-07-18 02:15:02 +00:00
|
|
|
err := c.err
|
2019-07-27 23:10:32 +00:00
|
|
|
c.mutex.RUnlock()
|
2019-07-18 02:15:02 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func CancellationChild(parent Cancellation) Cancellation {
|
|
|
|
child := NewCancellation()
|
|
|
|
go func() {
|
|
|
|
select {
|
|
|
|
case <-child.Finished():
|
|
|
|
case <-parent.Finished():
|
|
|
|
child.Cancel(parent.Error())
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return child
|
2019-07-18 02:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func CancellationWithTimeout(parent Cancellation, timeout time.Duration) Cancellation {
|
2019-07-18 02:15:02 +00:00
|
|
|
child := CancellationChild(parent)
|
|
|
|
go func() {
|
|
|
|
timer := time.NewTimer(timeout)
|
|
|
|
defer TimerStop(timer)
|
|
|
|
select {
|
|
|
|
case <-child.Finished():
|
|
|
|
case <-timer.C:
|
2019-07-18 02:37:45 +00:00
|
|
|
child.Cancel(CancellationTimeoutError)
|
2019-07-18 02:15:02 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
return child
|
2019-07-18 02:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func CancellationWithDeadline(parent Cancellation, deadline time.Time) Cancellation {
|
2019-07-18 02:15:02 +00:00
|
|
|
return CancellationWithTimeout(parent, deadline.Sub(time.Now()))
|
2019-07-18 02:09:22 +00:00
|
|
|
}
|