package zk import ( "errors" "fmt" "strconv" "strings" ) var ( ErrDeadlock = errors.New("zk: trying to acquire a lock twice") ErrNotLocked = errors.New("zk: not locked") ) type Lock struct { c *Conn path string acl []ACL lockPath string seq int } func NewLock(c *Conn, path string, acl []ACL) *Lock { return &Lock{ c: c, path: path, acl: acl, } } func parseSeq(path string) (int, error) { parts := strings.Split(path, "-") return strconv.Atoi(parts[len(parts)-1]) } func (l *Lock) Lock() error { if l.lockPath != "" { return ErrDeadlock } prefix := fmt.Sprintf("%s/lock-", l.path) path := "" var err error for i := 0; i < 3; i++ { path, err = l.c.CreateProtectedEphemeralSequential(prefix, []byte{}, l.acl) if err == ErrNoNode { // Create parent node. parts := strings.Split(l.path, "/") pth := "" for _, p := range parts[1:] { pth += "/" + p _, err := l.c.Create(pth, []byte{}, 0, l.acl) if err != nil && err != ErrNodeExists { return err } } } else if err == nil { break } else { return err } } if err != nil { return err } seq, err := parseSeq(path) if err != nil { return err } for { children, _, err := l.c.Children(l.path) if err != nil { return err } lowestSeq := seq prevSeq := 0 prevSeqPath := "" for _, p := range children { s, err := parseSeq(p) if err != nil { return err } if s < lowestSeq { lowestSeq = s } if s < seq && s > prevSeq { prevSeq = s prevSeqPath = p } } if seq == lowestSeq { // Acquired the lock break } // Wait on the node next in line for the lock _, _, ch, err := l.c.GetW(l.path + "/" + prevSeqPath) if err != nil && err != ErrNoNode { return err } else if err != nil && err == ErrNoNode { // try again continue } ev := <-ch if ev.Err != nil { return ev.Err } } l.seq = seq l.lockPath = path return nil } func (l *Lock) Unlock() error { if l.lockPath == "" { return ErrNotLocked } if err := l.c.Delete(l.lockPath, -1); err != nil { return err } l.lockPath = "" l.seq = 0 return nil }