micro/sync/lock/memory/memory.go

143 lines
2.2 KiB
Go
Raw Normal View History

// Package memory provides a sync.Mutex implementation of the lock for local use
2019-10-14 22:38:22 +01:00
package memory
2019-10-14 15:17:25 +01:00
import (
"sync"
"time"
lock "github.com/micro/go-micro/v2/sync/lock"
2019-10-14 15:17:25 +01:00
)
2019-10-14 22:38:22 +01:00
type memoryLock struct {
2019-10-14 15:17:25 +01:00
sync.RWMutex
locks map[string]*mlock
}
type mlock struct {
id string
time time.Time
ttl time.Duration
release chan bool
}
2019-10-14 22:38:22 +01:00
func (m *memoryLock) Acquire(id string, opts ...lock.AcquireOption) error {
2019-10-14 15:17:25 +01:00
// lock our access
m.Lock()
var options lock.AcquireOptions
for _, o := range opts {
o(&options)
}
lk, ok := m.locks[id]
if !ok {
m.locks[id] = &mlock{
id: id,
time: time.Now(),
ttl: options.TTL,
release: make(chan bool),
}
// unlock
m.Unlock()
return nil
}
m.Unlock()
// set wait time
var wait <-chan time.Time
var ttl <-chan time.Time
// decide if we should wait
if options.Wait > time.Duration(0) {
wait = time.After(options.Wait)
}
// check the ttl of the lock
if lk.ttl > time.Duration(0) {
// time lived for the lock
live := time.Since(lk.time)
// set a timer for the leftover ttl
if live > lk.ttl {
// release the lock if it expired
_ = m.Release(id)
2019-10-14 15:17:25 +01:00
} else {
ttl = time.After(live)
}
}
lockLoop:
for {
// wait for the lock to be released
select {
case <-lk.release:
m.Lock()
// someone locked before us
lk, ok = m.locks[id]
if ok {
m.Unlock()
continue
}
// got chance to lock
m.locks[id] = &mlock{
id: id,
time: time.Now(),
ttl: options.TTL,
release: make(chan bool),
}
m.Unlock()
break lockLoop
case <-ttl:
// ttl exceeded
_ = m.Release(id)
2019-10-14 15:17:25 +01:00
// TODO: check the ttl again above
ttl = nil
// try acquire
continue
case <-wait:
return lock.ErrLockTimeout
}
}
return nil
}
2019-10-14 22:38:22 +01:00
func (m *memoryLock) Release(id string) error {
2019-10-14 15:17:25 +01:00
m.Lock()
defer m.Unlock()
lk, ok := m.locks[id]
// no lock exists
if !ok {
return nil
}
// delete the lock
delete(m.locks, id)
select {
case <-lk.release:
return nil
default:
close(lk.release)
}
return nil
}
func NewLock(opts ...lock.Option) lock.Lock {
var options lock.Options
for _, o := range opts {
o(&options)
}
2019-10-14 22:38:22 +01:00
return &memoryLock{
2019-10-14 15:17:25 +01:00
locks: make(map[string]*mlock),
}
}