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  }