gitee.com/liuxuezhan/go-micro-v1.18.0@v1.0.0/sync/lock/memory/memory.go (about)

     1  // Package memory provides a sync.Mutex implementation of the lock for local use
     2  package memory
     3  
     4  import (
     5  	"sync"
     6  	"time"
     7  
     8  	lock "gitee.com/liuxuezhan/go-micro-v1.18.0/sync/lock"
     9  )
    10  
    11  type memoryLock struct {
    12  	sync.RWMutex
    13  	locks map[string]*mlock
    14  }
    15  
    16  type mlock struct {
    17  	id      string
    18  	time    time.Time
    19  	ttl     time.Duration
    20  	release chan bool
    21  }
    22  
    23  func (m *memoryLock) Acquire(id string, opts ...lock.AcquireOption) error {
    24  	// lock our access
    25  	m.Lock()
    26  
    27  	var options lock.AcquireOptions
    28  	for _, o := range opts {
    29  		o(&options)
    30  	}
    31  
    32  	lk, ok := m.locks[id]
    33  	if !ok {
    34  		m.locks[id] = &mlock{
    35  			id:      id,
    36  			time:    time.Now(),
    37  			ttl:     options.TTL,
    38  			release: make(chan bool),
    39  		}
    40  		// unlock
    41  		m.Unlock()
    42  		return nil
    43  	}
    44  
    45  	m.Unlock()
    46  
    47  	// set wait time
    48  	var wait <-chan time.Time
    49  	var ttl <-chan time.Time
    50  
    51  	// decide if we should wait
    52  	if options.Wait > time.Duration(0) {
    53  		wait = time.After(options.Wait)
    54  	}
    55  
    56  	// check the ttl of the lock
    57  	if lk.ttl > time.Duration(0) {
    58  		// time lived for the lock
    59  		live := time.Since(lk.time)
    60  
    61  		// set a timer for the leftover ttl
    62  		if live > lk.ttl {
    63  			// release the lock if it expired
    64  			_ = m.Release(id)
    65  		} else {
    66  			ttl = time.After(live)
    67  		}
    68  	}
    69  
    70  lockLoop:
    71  	for {
    72  		// wait for the lock to be released
    73  		select {
    74  		case <-lk.release:
    75  			m.Lock()
    76  
    77  			// someone locked before us
    78  			lk, ok = m.locks[id]
    79  			if ok {
    80  				m.Unlock()
    81  				continue
    82  			}
    83  
    84  			// got chance to lock
    85  			m.locks[id] = &mlock{
    86  				id:      id,
    87  				time:    time.Now(),
    88  				ttl:     options.TTL,
    89  				release: make(chan bool),
    90  			}
    91  
    92  			m.Unlock()
    93  
    94  			break lockLoop
    95  		case <-ttl:
    96  			// ttl exceeded
    97  			_ = m.Release(id)
    98  			// TODO: check the ttl again above
    99  			ttl = nil
   100  			// try acquire
   101  			continue
   102  		case <-wait:
   103  			return lock.ErrLockTimeout
   104  		}
   105  	}
   106  
   107  	return nil
   108  }
   109  
   110  func (m *memoryLock) Release(id string) error {
   111  	m.Lock()
   112  	defer m.Unlock()
   113  
   114  	lk, ok := m.locks[id]
   115  	// no lock exists
   116  	if !ok {
   117  		return nil
   118  	}
   119  
   120  	// delete the lock
   121  	delete(m.locks, id)
   122  
   123  	select {
   124  	case <-lk.release:
   125  		return nil
   126  	default:
   127  		close(lk.release)
   128  	}
   129  
   130  	return nil
   131  }
   132  
   133  func NewLock(opts ...lock.Option) lock.Lock {
   134  	var options lock.Options
   135  	for _, o := range opts {
   136  		o(&options)
   137  	}
   138  
   139  	return &memoryLock{
   140  		locks: make(map[string]*mlock),
   141  	}
   142  }