1// Package deadline implements the deadline (also known as "timeout") resiliency pattern for Go. 2package deadline 3 4import ( 5 "errors" 6 "time" 7) 8 9// ErrTimedOut is the error returned from Run when the deadline expires. 10var ErrTimedOut = errors.New("timed out waiting for function to finish") 11 12// Deadline implements the deadline/timeout resiliency pattern. 13type Deadline struct { 14 timeout time.Duration 15} 16 17// New constructs a new Deadline with the given timeout. 18func New(timeout time.Duration) *Deadline { 19 return &Deadline{ 20 timeout: timeout, 21 } 22} 23 24// Run runs the given function, passing it a stopper channel. If the deadline passes before 25// the function finishes executing, Run returns ErrTimeOut to the caller and closes the stopper 26// channel so that the work function can attempt to exit gracefully. It does not (and cannot) 27// simply kill the running function, so if it doesn't respect the stopper channel then it may 28// keep running after the deadline passes. If the function finishes before the deadline, then 29// the return value of the function is returned from Run. 30func (d *Deadline) Run(work func(<-chan struct{}) error) error { 31 result := make(chan error) 32 stopper := make(chan struct{}) 33 34 go func() { 35 result <- work(stopper) 36 }() 37 38 select { 39 case ret := <-result: 40 return ret 41 case <-time.After(d.timeout): 42 close(stopper) 43 return ErrTimedOut 44 } 45} 46