Files
2026-08-27 09:44:14 +03:00

88 lines
1.6 KiB
Go

package playback
import (
"context"
"time"
)
// attemptFunc returns whether useful media was received before the attempt ended.
type attemptFunc func(context.Context) (becameStable bool, err error)
type retryDecider func(error) bool
type waitFunc func(context.Context, time.Duration) error
func waitForRetry(ctx context.Context, delay time.Duration) error {
timer := time.NewTimer(delay)
defer timer.Stop()
select {
case <-timer.C:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
func runWithRetry(
ctx context.Context,
policy RetryPolicy,
attempt attemptFunc,
shouldRetry retryDecider,
wait waitFunc,
observer retryObserver,
) error {
failedAttempts := 0
for {
becameStable, err := attempt(ctx)
if err == nil {
return nil
}
if ctx.Err() != nil {
return ctx.Err()
}
if becameStable {
failedAttempts = 0
}
failedAttempts++
willRetry := shouldRetry(err) && policy.canRetry(failedAttempts)
if !willRetry {
if observer != nil {
observer(retryEvent{
FailedAttempts: failedAttempts,
Err: err,
WillRetry: false,
})
}
return err
}
delay := policy.retryDelay(failedAttempts)
if observer != nil {
observer(retryEvent{
FailedAttempts: failedAttempts,
Err: err,
RetryIn: delay,
WillRetry: true,
})
}
if err := wait(ctx, delay); err != nil {
if ctx.Err() != nil {
return ctx.Err()
}
return err
}
}
}
type retryEvent struct {
FailedAttempts int
Err error
RetryIn time.Duration
WillRetry bool
}
type retryObserver func(retryEvent)