github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/container/lock.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package container 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 "regexp" 11 ) 12 13 const ( 14 // NameRegexp specifies the regular expression used to identify valid lock names. 15 NameRegexp = "^[a-z]+[a-z0-9.-]*$" 16 ) 17 18 var ( 19 validName = regexp.MustCompile(NameRegexp) 20 ) 21 22 type Lock struct { 23 name string 24 lockDir string 25 lockFile *os.File 26 } 27 28 // NewLock returns a new lock with the given name, using the given lock 29 // directory, without acquiring it. The lock name must match the regular 30 // expression defined by NameRegexp. 31 func NewLock(dir, name string) (*Lock, error) { 32 if !validName.MatchString(name) { 33 return nil, fmt.Errorf("Invalid lock name %q. Names must match %q", name, NameRegexp) 34 } 35 lockDir := filepath.Join(dir, name) 36 lock := &Lock{ 37 name: name, 38 lockDir: lockDir, 39 } 40 // Ensure the lockDir exists. 41 if err := os.MkdirAll(lockDir, 0755); err != nil { 42 return nil, err 43 } 44 return lock, nil 45 } 46 47 // Lock blocks until it is able to acquire the lock. It is good behaviour to 48 // provide a message that is output in debugging information. 49 func (lock *Lock) Lock(message string) (err error) { 50 logger.Infof("acquire lock %q, %s", lock.name, message) 51 lock.lockFile, err = os.Open(lock.lockDir) 52 if err != nil { 53 return err 54 } 55 fd := int(lock.lockFile.Fd()) 56 err = flockLock(fd) 57 if err != nil { 58 lock.lockFile.Close() 59 lock.lockFile = nil 60 } 61 return err 62 } 63 64 // Unlock releases a held lock. 65 func (lock *Lock) Unlock() error { 66 logger.Infof("release lock %q", lock.name) 67 if lock.lockFile == nil { 68 return nil 69 } 70 fd := int(lock.lockFile.Fd()) 71 err := flockUnlock(fd) 72 if err == nil { 73 lock.lockFile.Close() 74 lock.lockFile = nil 75 } 76 return err 77 }