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  }