code.gitea.io/gitea@v1.22.3/modules/queue/backoff.go (about) 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package queue 5 6 import ( 7 "context" 8 "time" 9 ) 10 11 var ( 12 backoffBegin = 50 * time.Millisecond 13 backoffUpper = 2 * time.Second 14 ) 15 16 type ( 17 backoffFuncRetErr[T any] func() (retry bool, ret T, err error) 18 backoffFuncErr func() (retry bool, err error) 19 ) 20 21 func mockBackoffDuration(d time.Duration) func() { 22 oldBegin, oldUpper := backoffBegin, backoffUpper 23 backoffBegin, backoffUpper = d, d 24 return func() { 25 backoffBegin, backoffUpper = oldBegin, oldUpper 26 } 27 } 28 29 func backoffRetErr[T any](ctx context.Context, begin, upper time.Duration, end <-chan time.Time, fn backoffFuncRetErr[T]) (ret T, err error) { 30 d := begin 31 for { 32 // check whether the context has been cancelled or has reached the deadline, return early 33 select { 34 case <-ctx.Done(): 35 return ret, ctx.Err() 36 case <-end: 37 return ret, context.DeadlineExceeded 38 default: 39 } 40 41 // call the target function 42 retry, ret, err := fn() 43 if err != nil { 44 return ret, err 45 } 46 if !retry { 47 return ret, nil 48 } 49 50 // wait for a while before retrying, and also respect the context & deadline 51 select { 52 case <-ctx.Done(): 53 return ret, ctx.Err() 54 case <-time.After(d): 55 d *= 2 56 if d > upper { 57 d = upper 58 } 59 case <-end: 60 return ret, context.DeadlineExceeded 61 } 62 } 63 } 64 65 func backoffErr(ctx context.Context, begin, upper time.Duration, end <-chan time.Time, fn backoffFuncErr) error { 66 _, err := backoffRetErr(ctx, begin, upper, end, func() (retry bool, ret any, err error) { 67 retry, err = fn() 68 return retry, nil, err 69 }) 70 return err 71 }