package util import ( "fmt" "time" ) // TimeoutError is error returned after timeout occurred. type TimeoutError struct { after time.Duration message string } // Error implements the Go error interface. func (t *TimeoutError) Error() string { if len(t.message) > 0 { return fmt.Sprintf("%s timed out after %v", t.message, t.after) } return fmt.Sprintf("function timed out after %v", t.after) } // TimeoutAfter executes the provided function and returns TimeoutError in the // case that the execution time of the function exceeded the provided duration. // The provided function is passed the timer in case it wishes to reset it. If // so, the following pattern must be used: // if !timer.Stop() { // return &TimeoutError{} // } // timer.Reset(timeout) func TimeoutAfter(t time.Duration, errorMsg string, f func(*time.Timer) error) error { c := make(chan error, 1) timer := time.NewTimer(t) go func() { err := f(timer) if !IsTimeoutError(err) { c <- err } }() select { case err := <-c: timer.Stop() return err case <-timer.C: return &TimeoutError{after: t, message: errorMsg} } } // IsTimeoutError checks if the provided error is a TimeoutError. func IsTimeoutError(e error) bool { _, ok := e.(*TimeoutError) return ok }