// Dequeue returns Enqueue()'d elements in FIFO order. If the // queue is empty, Dequeue blocks until elements are available. func (q *Queue) Dequeue() (string, error) { // TODO: fewer round trips by fetching more than one key resp, err := q.client.Get(q.ctx, q.keyPrefix, v3.WithFirstRev()...) if err != nil { return "", err } kv, err := claimFirstKey(q.client, resp.Kvs) if err != nil { return "", err } else if kv != nil { return string(kv.Value), nil } else if resp.More { // missed some items, retry to read in more return q.Dequeue() } // nothing yet; wait on elements ev, err := WaitPrefixEvents( q.client, q.keyPrefix, resp.Header.Revision, []storagepb.Event_EventType{storagepb.PUT}) if err != nil { return "", err } ok, err := deleteRevKey(q.client, string(ev.Kv.Key), ev.Kv.ModRevision) if err != nil { return "", err } else if !ok { return q.Dequeue() } return string(ev.Kv.Value), err }
func (rwm *RWMutex) RLock() error { rk, err := NewUniqueEphemeralKey(rwm.client, rwm.key+"/read") if err != nil { return err } rwm.myKey = rk // if there are nodes with "write-" and a lower // revision number than us we must wait resp, err := rwm.client.Get(rwm.ctx, rwm.key+"/write", v3.WithFirstRev()...) if err != nil { return err } if len(resp.Kvs) == 0 || resp.Kvs[0].ModRevision > rk.Revision() { // no blocking since no write key return nil } return rwm.waitOnLowest() }
// 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 }