func (l *Lock) Lock(stopCh <-chan struct{}) (<-chan struct{}, error) {
	l.l.Lock()
	defer l.l.Unlock()
	if l.isHeld {
		return nil, ErrLockHeld
	}
	l.lockSession = l.opts.Session
	if l.lockSession == "" {
		if s, err := l.createSession(); err != nil {
			return nil, fmt.Errorf("failed to create session: %v", err)
		} else {
			l.sessionRenew = make(chan struct{})
			l.lockSession = s
			session := l.c.Session()
			go session.RenewPeriodic(l.opts.SessionTTL, s, nil, l.sessionRenew)
			defer func() {
				if !l.isHeld {
					close(l.sessionRenew)
					l.sessionRenew = nil
				}
			}()
		}
	}
	kv := l.c.KV()
	qOpts := &QueryOptions{
		WaitTime: l.opts.LockWaitTime,
	}
	start := time.Now()
	attempts := 0
WAIT:
	select {
	case <-stopCh:
		return nil, nil
	default:
	}
	if l.opts.LockTryOnce && attempts > 0 {
		elapsed := time.Now().Sub(start)
		if elapsed > qOpts.WaitTime {
			return nil, nil
		}
		qOpts.WaitTime -= elapsed
	}
	attempts++
	pair, meta, err := kv.Get(l.opts.Key, qOpts)
	if err != nil {
		return nil, fmt.Errorf("failed to read lock: %v", err)
	}
	if pair != nil && pair.Flags != LockFlagValue {
		return nil, ErrLockConflict
	}
	locked := false
	if pair != nil && pair.Session == l.lockSession {
		goto HELD
	}
	if pair != nil && pair.Session != "" {
		qOpts.WaitIndex = meta.LastIndex
		goto WAIT
	}
	pair = l.lockEntry(l.lockSession)
	locked, _, err = kv.Acquire(pair, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to acquire lock: %v", err)
	}
	if !locked {
		qOpts.WaitIndex = 0
		pair, meta, err = kv.Get(l.opts.Key, qOpts)
		if pair != nil && pair.Session != "" {
			qOpts.WaitIndex = meta.LastIndex
			goto WAIT
		} else {
			select {
			case <-time.After(DefaultLockRetryTime):
				goto WAIT
			case <-stopCh:
				return nil, nil
			}
		}
	}
HELD:
	leaderCh := make(chan struct{})
	go l.monitorLock(l.lockSession, leaderCh)
	l.isHeld = true
	return leaderCh, nil
}
Lock attempts to acquire the lock and blocks while doing so.
Providing a non-nil stopCh can be used to abort the lock attempt.
Returns a channel that is closed if our lock is lost or an error.
This channel could be closed at any time due to session invalidation,
communication errors, operator intervention, etc. It is NOT safe to
assume that the lock is held until Unlock() unless the Session is specifically
created without any associated health checks. By default Consul sessions
prefer liveness over safety and an application must be able to handle
the lock being lost.
package api
import (
	"fmt"
	"sync"
	"time"
)
const (
	DefaultLockSessionName = "Consul API Lock"
	DefaultLockSessionTTL = "15s"
	DefaultLockWaitTime = 15 * time.Second
	DefaultLockRetryTime = 5 * time.Second
	DefaultMonitorRetryTime = 2 * time.Second
	LockFlagValue = 0x2ddccbc058a50c18
)
var (
	ErrLockHeld = fmt.Errorf("Lock already held")
	ErrLockNotHeld = fmt.Errorf("Lock not held")
	ErrLockInUse = fmt.Errorf("Lock in use")
	ErrLockConflict = fmt.Errorf("Existing key does not match lock use")
)
type Lock struct {
	c    *Client
	opts *LockOptions
	isHeld       bool
	sessionRenew chan struct{}
	lockSession  string
	l            sync.Mutex
}
type LockOptions struct {
	Key              string         
	Value            []byte         
	Session          string         
	SessionName      string         
	SessionTTL       string         
	MonitorRetries   int            
	MonitorRetryTime time.Duration  
	LockWaitTime     time.Duration  
	LockTryOnce      bool           
}
func (c *Client) LockKey(key string) (*Lock, error) {
	opts := &LockOptions{
		Key: key,
	}
	return c.LockOpts(opts)
}
func (c *Client) LockOpts(opts *LockOptions) (*Lock, error) {
	if opts.Key == "" {
		return nil, fmt.Errorf("missing key")
	}
	if opts.SessionName == "" {
		opts.SessionName = DefaultLockSessionName
	}
	if opts.SessionTTL == "" {
		opts.SessionTTL = DefaultLockSessionTTL
	} else {
		if _, err := time.ParseDuration(opts.SessionTTL); err != nil {
			return nil, fmt.Errorf("invalid SessionTTL: %v", err)
		}
	}
	if opts.MonitorRetryTime == 0 {
		opts.MonitorRetryTime = DefaultMonitorRetryTime
	}
	if opts.LockWaitTime == 0 {
		opts.LockWaitTime = DefaultLockWaitTime
	}
	l := &Lock{
		c:    c,
		opts: opts,
	}
	return l, nil
}