github.com/wfusion/gofusion@v1.1.14/lock/candy.go (about) 1 package lock 2 3 import ( 4 "context" 5 "time" 6 7 "go.uber.org/multierr" 8 9 "github.com/wfusion/gofusion/common/utils" 10 "github.com/wfusion/gofusion/routine" 11 ) 12 13 func Within(ctx context.Context, locker Lockable, key string, 14 expired, timeout time.Duration, cb func() error, opts ...utils.OptionExtender) (err error) { 15 const ( 16 reLockWaitTime = time.Duration(200) * time.Millisecond 17 ) 18 opt := utils.ApplyOptions[useOption](opts...) 19 optL := utils.ApplyOptions[lockOption](opts...) 20 if optL.reentrantKey == "" { 21 optL.reentrantKey = utils.ULID() 22 } 23 optionals := []utils.OptionExtender{ReentrantKey(optL.reentrantKey)} 24 if expired > 0 { 25 optionals = append(optionals, Expire(expired)) 26 } 27 28 done := make(chan struct{}, 1) 29 timeFault := make(chan struct{}, 1) 30 routine.Goc(ctx, func() { 31 defer func() { done <- struct{}{} }() 32 33 var e error 34 rLocker, ok := locker.(ReentrantLockable) 35 for { 36 select { 37 case <-timeFault: // timeout exit 38 return 39 default: 40 if ok { 41 if e = rLocker.ReentrantLock(ctx, key, optL.reentrantKey, optionals...); e == nil { 42 return 43 } 44 } else { 45 if e = locker.Lock(ctx, key, optionals...); e == nil { 46 return 47 } 48 } 49 50 // relock after 200 milliseconds 51 time.Sleep(reLockWaitTime) 52 } 53 } 54 }, routine.AppName(opt.appName)) 55 56 timer := time.NewTimer(timeout) 57 select { 58 // success 59 case <-done: 60 61 // context done 62 case <-ctx.Done(): 63 timeFault <- struct{}{} 64 return ErrContextDone 65 66 // timeout 67 case <-timer.C: 68 timeFault <- struct{}{} 69 return ErrTimeout 70 } 71 72 defer func() { err = multierr.Append(err, locker.Unlock(ctx, key, optionals...)) }() 73 74 _, err = utils.Catch(cb) 75 return 76 }