Example #1
0
func (rwm *RWMutex) Lock() error {
	rk, err := NewUniqueEphemeralKey(rwm.client, rwm.key+"/write")
	if err != nil {
		return err
	}
	rwm.myKey = rk

	for {
		// find any key of lower rev number blocks the write lock
		opts := append(v3.WithLastRev(), v3.WithRev(rk.Revision()-1))
		resp, err := rwm.client.Get(rwm.ctx, rwm.key, opts...)
		if err != nil {
			return err
		}
		if len(resp.Kvs) == 0 {
			// no matching for revision before myKey; acquired
			break
		}
		if err := rwm.waitOnLowest(); err != nil {
			return err
		}
		//  get the new lowest, etc until this is the only one left
	}

	return nil
}
Example #2
0
func (rwm *RWMutex) waitOnLowest() error {
	// must block; get key before ek for waiting
	opts := append(v3.WithLastRev(), v3.WithRev(rwm.myKey.Revision()-1))
	lastKey, err := rwm.client.Get(rwm.ctx, rwm.key, opts...)
	if err != nil {
		return err
	}
	// wait for release on prior key
	_, err = WaitEvents(
		rwm.client,
		string(lastKey.Kvs[0].Key),
		rwm.myKey.Revision(),
		[]storagepb.Event_EventType{storagepb.DELETE})
	return err
}
Example #3
0
// waitOnLowest will wait on the last key with a revision < rwm.myKey.Revision with a
// given prefix. If there are no keys left to wait on, return true.
func (rwm *RWMutex) waitOnLastRev(pfx string) (bool, error) {
	client := rwm.s.Client()
	// get key that's blocking myKey
	opts := append(v3.WithLastRev(), v3.WithMaxModRev(rwm.myKey.Revision()-1))
	lastKey, err := client.Get(rwm.ctx, pfx, opts...)
	if err != nil {
		return false, err
	}
	if len(lastKey.Kvs) == 0 {
		return true, nil
	}
	// wait for release on blocking key
	_, err = WaitEvents(
		client,
		string(lastKey.Kvs[0].Key),
		rwm.myKey.Revision(),
		[]mvccpb.Event_EventType{mvccpb.DELETE})
	return false, err
}
Example #4
0
File: mutex.go Project: vsayer/etcd
// Lock locks the mutex with a cancellable context. If the context is cancelled
// while trying to acquire the lock, the mutex tries to clean its stale lock entry.
func (m *Mutex) Lock(ctx context.Context) error {
	s, err := NewSession(m.client)
	if err != nil {
		return err
	}
	// put self in lock waiters via myKey; oldest waiter holds lock
	m.myKey, m.myRev, err = NewUniqueKey(ctx, m.kv, m.pfx, v3.WithLease(s.Lease()))
	// wait for lock to become available
	for err == nil {
		// find oldest element in waiters via revision of insertion
		var resp *v3.GetResponse
		resp, err = m.kv.Get(ctx, m.pfx, v3.WithFirstRev()...)
		if err != nil {
			break
		}
		if m.myRev == resp.Kvs[0].CreateRevision {
			// myKey is oldest in waiters; myKey holds the lock now
			return nil
		}
		// otherwise myKey isn't lowest, so there must be a pfx prior to myKey
		opts := append(v3.WithLastRev(), v3.WithRev(m.myRev-1))
		resp, err = m.kv.Get(ctx, m.pfx, opts...)
		if err != nil {
			break
		}
		lastKey := string(resp.Kvs[0].Key)
		// wait for release on prior pfx
		err = waitUpdate(ctx, m.client, lastKey, v3.WithRev(m.myRev))
		// try again in case lastKey left the wait list before acquiring the lock;
		// myKey can only hold the lock if it's the oldest in the list
	}

	// release lock key if cancelled
	select {
	case <-ctx.Done():
		m.Unlock()
	default:
	}
	return err
}