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