package expirelock import ( "context" "errors" "expirelock/os" "sync" "sync/atomic" "time" ) type Block struct { token atomic.Value mu sync.Mutex dmu sync.Mutex celFu context.CancelFunc } func NewBlock() *Block { return &Block{} } func (b *Block) Lock(ttl time.Duration) error { b.mu.Lock() b.dmu.Lock() defer b.dmu.Unlock() token := os.GetCurrentProcessAndGogroutineIDStr() b.token.Store(token) b.celFu = nil if ttl <= 0 { return nil } var ctx context.Context ctx, b.celFu = context.WithCancel(context.Background()) go func() { select { case <-ctx.Done(): return case <-time.After(ttl): _ = b.unlock(token) } }() return nil } func (b *Block) unlock(token string) error { b.dmu.Lock() defer b.dmu.Unlock() id, _ := b.token.Load().(string) if id != token { return errors.New("invalid token") } if b.celFu != nil { b.celFu() } b.token.Store("") b.mu.Unlock() return nil } func (b *Block) Unlock() error { token := os.GetCurrentProcessAndGogroutineIDStr() return b.unlock(token) }