vitess.io/vitess@v0.16.2/go/vt/topo/memorytopo/lock.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package memorytopo 18 19 import ( 20 "context" 21 "fmt" 22 23 "vitess.io/vitess/go/vt/topo" 24 ) 25 26 // convertError converts a context error into a topo error. 27 func convertError(err error, nodePath string) error { 28 switch err { 29 case context.Canceled: 30 return topo.NewError(topo.Interrupted, nodePath) 31 case context.DeadlineExceeded: 32 return topo.NewError(topo.Timeout, nodePath) 33 } 34 return err 35 } 36 37 // memoryTopoLockDescriptor implements topo.LockDescriptor. 38 type memoryTopoLockDescriptor struct { 39 c *Conn 40 dirPath string 41 } 42 43 // TryLock is part of the topo.Conn interface. Its implementation is same as Lock 44 func (c *Conn) TryLock(ctx context.Context, dirPath, contents string) (topo.LockDescriptor, error) { 45 return c.Lock(ctx, dirPath, contents) 46 } 47 48 // Lock is part of the topo.Conn interface. 49 func (c *Conn) Lock(ctx context.Context, dirPath, contents string) (topo.LockDescriptor, error) { 50 return c.lock(ctx, dirPath, contents) 51 } 52 53 // Lock is part of the topo.Conn interface. 54 func (c *Conn) lock(ctx context.Context, dirPath, contents string) (topo.LockDescriptor, error) { 55 for { 56 if err := c.dial(ctx); err != nil { 57 return nil, err 58 } 59 60 c.factory.mu.Lock() 61 62 if c.factory.err != nil { 63 c.factory.mu.Unlock() 64 return nil, c.factory.err 65 } 66 67 n := c.factory.nodeByPath(c.cell, dirPath) 68 if n == nil { 69 c.factory.mu.Unlock() 70 return nil, topo.NewError(topo.NoNode, dirPath) 71 } 72 73 if l := n.lock; l != nil { 74 // Someone else has the lock. Just wait for it. 75 c.factory.mu.Unlock() 76 select { 77 case <-l: 78 // Node was unlocked, try again to grab it. 79 continue 80 case <-ctx.Done(): 81 // Done waiting 82 return nil, convertError(ctx.Err(), dirPath) 83 } 84 } 85 86 // No one has the lock, grab it. 87 n.lock = make(chan struct{}) 88 n.lockContents = contents 89 for _, w := range n.watches { 90 if w.lock == nil { 91 continue 92 } 93 w.lock <- contents 94 } 95 c.factory.mu.Unlock() 96 return &memoryTopoLockDescriptor{ 97 c: c, 98 dirPath: dirPath, 99 }, nil 100 } 101 } 102 103 // Check is part of the topo.LockDescriptor interface. 104 // We can never lose a lock in this implementation. 105 func (ld *memoryTopoLockDescriptor) Check(ctx context.Context) error { 106 return nil 107 } 108 109 // Unlock is part of the topo.LockDescriptor interface. 110 func (ld *memoryTopoLockDescriptor) Unlock(ctx context.Context) error { 111 return ld.c.unlock(ctx, ld.dirPath) 112 } 113 114 func (c *Conn) unlock(ctx context.Context, dirPath string) error { 115 if c.closed { 116 return ErrConnectionClosed 117 } 118 119 c.factory.mu.Lock() 120 defer c.factory.mu.Unlock() 121 122 n := c.factory.nodeByPath(c.cell, dirPath) 123 if n == nil { 124 return topo.NewError(topo.NoNode, dirPath) 125 } 126 if n.lock == nil { 127 return fmt.Errorf("node %v is not locked", dirPath) 128 } 129 close(n.lock) 130 n.lock = nil 131 n.lockContents = "" 132 return nil 133 }