github.com/annwntech/go-micro/v2@v2.9.5/sync/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  	gosync "sync"
     6  	"time"
     7  
     8  	"github.com/annwntech/go-micro/v2/sync"
     9  )
    10  
    11  type memorySync struct {
    12  	options sync.Options
    13  
    14  	mtx   gosync.RWMutex
    15  	locks map[string]*memoryLock
    16  }
    17  
    18  type memoryLock struct {
    19  	id      string
    20  	time    time.Time
    21  	ttl     time.Duration
    22  	release chan bool
    23  }
    24  
    25  type memoryLeader struct {
    26  	opts   sync.LeaderOptions
    27  	id     string
    28  	resign func(id string) error
    29  	status chan bool
    30  }
    31  
    32  func (m *memoryLeader) Resign() error {
    33  	return m.resign(m.id)
    34  }
    35  
    36  func (m *memoryLeader) Status() chan bool {
    37  	return m.status
    38  }
    39  
    40  func (m *memorySync) Leader(id string, opts ...sync.LeaderOption) (sync.Leader, error) {
    41  	var once gosync.Once
    42  	var options sync.LeaderOptions
    43  	for _, o := range opts {
    44  		o(&options)
    45  	}
    46  
    47  	// acquire a lock for the id
    48  	if err := m.Lock(id); err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	// return the leader
    53  	return &memoryLeader{
    54  		opts: options,
    55  		id:   id,
    56  		resign: func(id string) error {
    57  			once.Do(func() {
    58  				m.Unlock(id)
    59  			})
    60  			return nil
    61  		},
    62  		// TODO: signal when Unlock is called
    63  		status: make(chan bool, 1),
    64  	}, nil
    65  }
    66  
    67  func (m *memorySync) Init(opts ...sync.Option) error {
    68  	for _, o := range opts {
    69  		o(&m.options)
    70  	}
    71  	return nil
    72  }
    73  
    74  func (m *memorySync) Options() sync.Options {
    75  	return m.options
    76  }
    77  
    78  func (m *memorySync) Lock(id string, opts ...sync.LockOption) error {
    79  	// lock our access
    80  	m.mtx.Lock()
    81  
    82  	var options sync.LockOptions
    83  	for _, o := range opts {
    84  		o(&options)
    85  	}
    86  
    87  	lk, ok := m.locks[id]
    88  	if !ok {
    89  		m.locks[id] = &memoryLock{
    90  			id:      id,
    91  			time:    time.Now(),
    92  			ttl:     options.TTL,
    93  			release: make(chan bool),
    94  		}
    95  		// unlock
    96  		m.mtx.Unlock()
    97  		return nil
    98  	}
    99  
   100  	m.mtx.Unlock()
   101  
   102  	// set wait time
   103  	var wait <-chan time.Time
   104  	var ttl <-chan time.Time
   105  
   106  	// decide if we should wait
   107  	if options.Wait > time.Duration(0) {
   108  		wait = time.After(options.Wait)
   109  	}
   110  
   111  	// check the ttl of the lock
   112  	if lk.ttl > time.Duration(0) {
   113  		// time lived for the lock
   114  		live := time.Since(lk.time)
   115  
   116  		// set a timer for the leftover ttl
   117  		if live > lk.ttl {
   118  			// release the lock if it expired
   119  			_ = m.Unlock(id)
   120  		} else {
   121  			ttl = time.After(live)
   122  		}
   123  	}
   124  
   125  lockLoop:
   126  	for {
   127  		// wait for the lock to be released
   128  		select {
   129  		case <-lk.release:
   130  			m.mtx.Lock()
   131  
   132  			// someone locked before us
   133  			lk, ok = m.locks[id]
   134  			if ok {
   135  				m.mtx.Unlock()
   136  				continue
   137  			}
   138  
   139  			// got chance to lock
   140  			m.locks[id] = &memoryLock{
   141  				id:      id,
   142  				time:    time.Now(),
   143  				ttl:     options.TTL,
   144  				release: make(chan bool),
   145  			}
   146  
   147  			m.mtx.Unlock()
   148  
   149  			break lockLoop
   150  		case <-ttl:
   151  			// ttl exceeded
   152  			_ = m.Unlock(id)
   153  			// TODO: check the ttl again above
   154  			ttl = nil
   155  			// try acquire
   156  			continue
   157  		case <-wait:
   158  			return sync.ErrLockTimeout
   159  		}
   160  	}
   161  
   162  	return nil
   163  }
   164  
   165  func (m *memorySync) Unlock(id string) error {
   166  	m.mtx.Lock()
   167  	defer m.mtx.Unlock()
   168  
   169  	lk, ok := m.locks[id]
   170  	// no lock exists
   171  	if !ok {
   172  		return nil
   173  	}
   174  
   175  	// delete the lock
   176  	delete(m.locks, id)
   177  
   178  	select {
   179  	case <-lk.release:
   180  		return nil
   181  	default:
   182  		close(lk.release)
   183  	}
   184  
   185  	return nil
   186  }
   187  
   188  func (m *memorySync) String() string {
   189  	return "memory"
   190  }
   191  
   192  func NewSync(opts ...sync.Option) sync.Sync {
   193  	var options sync.Options
   194  	for _, o := range opts {
   195  		o(&options)
   196  	}
   197  
   198  	return &memorySync{
   199  		options: options,
   200  		locks:   make(map[string]*memoryLock),
   201  	}
   202  }