package local import ( "context" "errors" "git.zhangshuocauc.cn/redhat/goconsistencehash/local/os" "sync" "time" ) type SpinLock struct { mu sync.Mutex token string locked bool expire time.Time } func NewSpinLock() *SpinLock { return &SpinLock{} } func (s *SpinLock) tryLock(expire int) bool { s.mu.Lock() defer s.mu.Unlock() now := time.Now() if !s.locked || now.After(s.expire) { s.locked = true s.token = os.GetCurrentProcessAndGoroutineIDStr() s.expire = now.Add(time.Duration(expire) * time.Second) return true } return false } func (s *SpinLock) Lock(ctx context.Context, expire int) error { ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() for range ticker.C { select { case <-ctx.Done(): return ctx.Err() default: } if s.tryLock(expire) { return nil } } return errors.New("try lock error") } func (s *SpinLock) UnLock(ctx context.Context) error { s.mu.Lock() defer s.mu.Unlock() if s.token != os.GetCurrentProcessAndGoroutineIDStr() { return errors.New("not my lock") } if !s.locked || time.Now().After(s.expire) { return errors.New("try unlock an unlocked lock") } s.locked = false s.token = "" return nil }