Files
phishingclub/backend/vendor/github.com/go-rod/rod/lib/utils/sleeper.go
T
Ronni Skansing f5e6f53d75 update vendor
Signed-off-by: Ronni Skansing <rskansing@gmail.com>
2026-05-24 09:57:07 +02:00

150 lines
3.2 KiB
Go

package utils
import (
"context"
"fmt"
mr "math/rand"
"sync"
"time"
)
// Sleep the goroutine for specified seconds, such as 2.3 seconds.
func Sleep(seconds float64) {
d := time.Duration(seconds * float64(time.Second))
time.Sleep(d)
}
// Sleeper sleeps the current goroutine for sometime, returns the reason to wake, if ctx is done release resource.
type Sleeper func(context.Context) error
// MaxSleepCountError type.
type MaxSleepCountError struct {
// Max count
Max int
}
// Error interface.
func (e *MaxSleepCountError) Error() string {
return fmt.Sprintf("max sleep count %d exceeded", e.Max)
}
// Is interface.
func (e *MaxSleepCountError) Is(err error) bool { _, ok := err.(*MaxSleepCountError); return ok }
// CountSleeper wakes immediately. When counts to the max returns *ErrMaxSleepCount.
func CountSleeper(max int) Sleeper {
l := sync.Mutex{}
count := 0
return func(ctx context.Context) error {
l.Lock()
defer l.Unlock()
if ctx.Err() != nil {
return ctx.Err()
}
if count == max {
return &MaxSleepCountError{max}
}
count++
return nil
}
}
// DefaultBackoff algorithm: A(n) = A(n-1) * random[1.9, 2.1).
func DefaultBackoff(interval time.Duration) time.Duration {
scale := 2 + (mr.Float64()-0.5)*0.2
return time.Duration(float64(interval) * scale)
}
// BackoffSleeper returns a sleeper that sleeps in a backoff manner every time get called.
// The sleep interval of the sleeper will grow from initInterval to maxInterval by the specified algorithm,
// then use maxInterval as the interval.
// If maxInterval is not greater than 0, the sleeper will wake immediately.
// If algorithm is nil, DefaultBackoff will be used.
func BackoffSleeper(initInterval, maxInterval time.Duration, algorithm func(time.Duration) time.Duration) Sleeper {
l := sync.Mutex{}
if algorithm == nil {
algorithm = DefaultBackoff
}
return func(ctx context.Context) error {
l.Lock()
defer l.Unlock()
// wake immediately
if maxInterval <= 0 {
return nil
}
var interval time.Duration
if initInterval < maxInterval {
interval = algorithm(initInterval)
} else {
interval = maxInterval
}
t := time.NewTimer(interval)
defer t.Stop()
select {
case <-ctx.Done():
return ctx.Err()
case <-t.C:
initInterval = interval
}
return nil
}
}
// EachSleepers returns a sleeper wakes up when each sleeper is awake.
// If a sleeper returns error, it will wake up immediately.
func EachSleepers(list ...Sleeper) Sleeper {
return func(ctx context.Context) (err error) {
for _, s := range list {
err = s(ctx)
if err != nil {
break
}
}
return
}
}
// RaceSleepers returns a sleeper wakes up when one of the sleepers wakes.
func RaceSleepers(list ...Sleeper) Sleeper {
return func(ctx context.Context) error {
ctx, cancel := context.WithCancel(ctx)
done := make(chan error, len(list))
sleep := func(s Sleeper) {
done <- s(ctx)
cancel()
}
for _, s := range list {
go sleep(s)
}
return <-done
}
}
// Retry fn and sleeper until fn returns true or s returns error.
func Retry(ctx context.Context, s Sleeper, fn func() (stop bool, err error)) error {
for {
stop, err := fn()
if stop {
return err
}
err = s(ctx)
if err != nil {
return err
}
}
}