github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/util/sync/memory/memory.go (about)

     1  // Licensed under the Apache License, Version 2.0 (the "License");
     2  // you may not use this file except in compliance with the License.
     3  // You may obtain a copy of the License at
     4  //
     5  //     https://www.apache.org/licenses/LICENSE-2.0
     6  //
     7  // Unless required by applicable law or agreed to in writing, software
     8  // distributed under the License is distributed on an "AS IS" BASIS,
     9  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10  // See the License for the specific language governing permissions and
    11  // limitations under the License.
    12  //
    13  // Original source: github.com/micro/go-micro/v3/sync/memory/memory.go
    14  
    15  // Package memory provides a sync.Mutex implementation of the lock for local use
    16  package memory
    17  
    18  import (
    19  	gosync "sync"
    20  	"time"
    21  
    22  	"github.com/tickoalcantara12/micro/v3/util/sync"
    23  )
    24  
    25  type memorySync struct {
    26  	options sync.Options
    27  
    28  	mtx   gosync.RWMutex
    29  	locks map[string]*memoryLock
    30  }
    31  
    32  type memoryLock struct {
    33  	id      string
    34  	time    time.Time
    35  	ttl     time.Duration
    36  	release chan bool
    37  }
    38  
    39  type memoryLeader struct {
    40  	opts   sync.LeaderOptions
    41  	id     string
    42  	resign func(id string) error
    43  	status chan bool
    44  }
    45  
    46  func (m *memoryLeader) Resign() error {
    47  	return m.resign(m.id)
    48  }
    49  
    50  func (m *memoryLeader) Status() chan bool {
    51  	return m.status
    52  }
    53  
    54  func (m *memorySync) Leader(id string, opts ...sync.LeaderOption) (sync.Leader, error) {
    55  	var once gosync.Once
    56  	var options sync.LeaderOptions
    57  	for _, o := range opts {
    58  		o(&options)
    59  	}
    60  
    61  	// acquire a lock for the id
    62  	if err := m.Lock(id); err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	// return the leader
    67  	return &memoryLeader{
    68  		opts: options,
    69  		id:   id,
    70  		resign: func(id string) error {
    71  			once.Do(func() {
    72  				m.Unlock(id)
    73  			})
    74  			return nil
    75  		},
    76  		// TODO: signal when Unlock is called
    77  		status: make(chan bool, 1),
    78  	}, nil
    79  }
    80  
    81  func (m *memorySync) Init(opts ...sync.Option) error {
    82  	for _, o := range opts {
    83  		o(&m.options)
    84  	}
    85  	return nil
    86  }
    87  
    88  func (m *memorySync) Options() sync.Options {
    89  	return m.options
    90  }
    91  
    92  func (m *memorySync) Lock(id string, opts ...sync.LockOption) error {
    93  	// lock our access
    94  	m.mtx.Lock()
    95  
    96  	var options sync.LockOptions
    97  	for _, o := range opts {
    98  		o(&options)
    99  	}
   100  
   101  	lk, ok := m.locks[id]
   102  	if !ok {
   103  		m.locks[id] = &memoryLock{
   104  			id:      id,
   105  			time:    time.Now(),
   106  			ttl:     options.TTL,
   107  			release: make(chan bool),
   108  		}
   109  		// unlock
   110  		m.mtx.Unlock()
   111  		return nil
   112  	}
   113  
   114  	m.mtx.Unlock()
   115  
   116  	// set wait time
   117  	var wait <-chan time.Time
   118  	var ttl <-chan time.Time
   119  
   120  	// decide if we should wait
   121  	if options.Wait > time.Duration(0) {
   122  		wait = time.After(options.Wait)
   123  	}
   124  
   125  	// check the ttl of the lock
   126  	if lk.ttl > time.Duration(0) {
   127  		// time lived for the lock
   128  		live := time.Since(lk.time)
   129  
   130  		// set a timer for the leftover ttl
   131  		if live > lk.ttl {
   132  			// release the lock if it expired
   133  			_ = m.Unlock(id)
   134  		} else {
   135  			ttl = time.After(live)
   136  		}
   137  	}
   138  
   139  lockLoop:
   140  	for {
   141  		// wait for the lock to be released
   142  		select {
   143  		case <-lk.release:
   144  			m.mtx.Lock()
   145  
   146  			// someone locked before us
   147  			lk, ok = m.locks[id]
   148  			if ok {
   149  				m.mtx.Unlock()
   150  				continue
   151  			}
   152  
   153  			// got chance to lock
   154  			m.locks[id] = &memoryLock{
   155  				id:      id,
   156  				time:    time.Now(),
   157  				ttl:     options.TTL,
   158  				release: make(chan bool),
   159  			}
   160  
   161  			m.mtx.Unlock()
   162  
   163  			break lockLoop
   164  		case <-ttl:
   165  			// ttl exceeded
   166  			_ = m.Unlock(id)
   167  			// TODO: check the ttl again above
   168  			ttl = nil
   169  			// try acquire
   170  			continue
   171  		case <-wait:
   172  			return sync.ErrLockTimeout
   173  		}
   174  	}
   175  
   176  	return nil
   177  }
   178  
   179  func (m *memorySync) Unlock(id string) error {
   180  	m.mtx.Lock()
   181  	defer m.mtx.Unlock()
   182  
   183  	lk, ok := m.locks[id]
   184  	// no lock exists
   185  	if !ok {
   186  		return nil
   187  	}
   188  
   189  	// delete the lock
   190  	delete(m.locks, id)
   191  
   192  	select {
   193  	case <-lk.release:
   194  		return nil
   195  	default:
   196  		close(lk.release)
   197  	}
   198  
   199  	return nil
   200  }
   201  
   202  func (m *memorySync) String() string {
   203  	return "memory"
   204  }
   205  
   206  func NewSync(opts ...sync.Option) sync.Sync {
   207  	var options sync.Options
   208  	for _, o := range opts {
   209  		o(&options)
   210  	}
   211  
   212  	return &memorySync{
   213  		options: options,
   214  		locks:   make(map[string]*memoryLock),
   215  	}
   216  }