github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/utils/filelock/lock_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // +build !windows
     5  
     6  package filelock_test
     7  
     8  import (
     9  	"fmt"
    10  	"os"
    11  	"runtime"
    12  	"sync/atomic"
    13  	"time"
    14  
    15  	jc "github.com/juju/testing/checkers"
    16  	gc "gopkg.in/check.v1"
    17  
    18  	coretesting "github.com/juju/juju/testing"
    19  	"github.com/juju/juju/utils/filelock"
    20  )
    21  
    22  type flockSuite struct{}
    23  
    24  var _ = gc.Suite(&flockSuite{})
    25  
    26  // This test also happens to test that locks can get created when the
    27  // lock directory doesn't exist.
    28  func (s *flockSuite) TestValidNamesLockDir(c *gc.C) {
    29  
    30  	for _, name := range []string{
    31  		"a",
    32  		"longer",
    33  		"longer-with.special-characters",
    34  	} {
    35  		dir := c.MkDir()
    36  		_, err := filelock.NewLock(dir, name)
    37  		c.Assert(err, jc.ErrorIsNil)
    38  	}
    39  }
    40  
    41  func (s *flockSuite) TestInvalidNames(c *gc.C) {
    42  
    43  	for _, name := range []string{
    44  		".start",
    45  		"-start",
    46  		"NoCapitals",
    47  		"no+plus",
    48  		"no/slash",
    49  		"no\\backslash",
    50  		"no$dollar",
    51  		"no:colon",
    52  	} {
    53  		dir := c.MkDir()
    54  		_, err := filelock.NewLock(dir, name)
    55  		c.Assert(err, gc.ErrorMatches, "Invalid lock name .*")
    56  	}
    57  }
    58  
    59  func (s *flockSuite) TestNewLockWithExistingDir(c *gc.C) {
    60  	dir := c.MkDir()
    61  	err := os.MkdirAll(dir, 0755)
    62  	c.Assert(err, jc.ErrorIsNil)
    63  	_, err = filelock.NewLock(dir, "special")
    64  	c.Assert(err, jc.ErrorIsNil)
    65  }
    66  
    67  func (s *flockSuite) TestLockBlocks(c *gc.C) {
    68  
    69  	dir := c.MkDir()
    70  	lock1, err := filelock.NewLock(dir, "testing")
    71  	c.Assert(err, jc.ErrorIsNil)
    72  	c.Assert(filelock.IsLocked(lock1), jc.IsFalse)
    73  	lock2, err := filelock.NewLock(dir, "testing")
    74  	c.Assert(err, jc.ErrorIsNil)
    75  	c.Assert(filelock.IsLocked(lock2), jc.IsFalse)
    76  
    77  	acquired := make(chan struct{})
    78  	err = lock1.Lock("")
    79  	c.Assert(err, jc.ErrorIsNil)
    80  	c.Assert(filelock.IsLocked(lock1), jc.IsTrue)
    81  
    82  	go func() {
    83  		lock2.Lock("")
    84  		c.Assert(filelock.IsLocked(lock2), jc.IsTrue)
    85  		acquired <- struct{}{}
    86  		close(acquired)
    87  	}()
    88  
    89  	// Waiting for something not to happen is inherently hard...
    90  	select {
    91  	case <-acquired:
    92  		c.Fatalf("Unexpected lock acquisition")
    93  	case <-time.After(coretesting.ShortWait):
    94  		// all good
    95  	}
    96  
    97  	err = lock1.Unlock()
    98  	c.Assert(err, jc.ErrorIsNil)
    99  	c.Assert(filelock.IsLocked(lock1), jc.IsFalse)
   100  
   101  	select {
   102  	case <-acquired:
   103  		// all good
   104  	case <-time.After(coretesting.LongWait):
   105  		c.Fatalf("Expected lock acquisition")
   106  	}
   107  }
   108  
   109  func (s *flockSuite) TestUnlock(c *gc.C) {
   110  	dir := c.MkDir()
   111  	lock, err := filelock.NewLock(dir, "testing")
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	err = lock.Lock("test")
   114  	c.Assert(err, jc.ErrorIsNil)
   115  	c.Assert(filelock.IsLocked(lock), jc.IsTrue)
   116  
   117  	err = lock.Unlock()
   118  	c.Assert(err, jc.ErrorIsNil)
   119  	c.Assert(filelock.IsLocked(lock), jc.IsFalse)
   120  }
   121  
   122  func (s *flockSuite) TestStress(c *gc.C) {
   123  	const lockAttempts = 200
   124  	const concurrentLocks = 10
   125  
   126  	var counter = new(int64)
   127  	// Use atomics to update lockState to make sure the lock isn't held by
   128  	// someone else. A value of 1 means locked, 0 means unlocked.
   129  	var lockState = new(int32)
   130  	var done = make(chan struct{})
   131  	defer close(done)
   132  
   133  	dir := c.MkDir()
   134  
   135  	var stress = func(name string) {
   136  		defer func() { done <- struct{}{} }()
   137  		lock, err := filelock.NewLock(dir, "testing")
   138  		if err != nil {
   139  			c.Errorf("Failed to create a new lock")
   140  			return
   141  		}
   142  		for i := 0; i < lockAttempts; i++ {
   143  			err = lock.Lock(name)
   144  			c.Assert(err, jc.ErrorIsNil)
   145  			state := atomic.AddInt32(lockState, 1)
   146  			c.Assert(state, gc.Equals, int32(1))
   147  			// Tell the go routine scheduler to give a slice to someone else
   148  			// while we have this locked.
   149  			runtime.Gosched()
   150  			// need to decrement prior to unlock to avoid the race of someone
   151  			// else grabbing the lock before we decrement the state.
   152  			atomic.AddInt32(lockState, -1)
   153  			err = lock.Unlock()
   154  			c.Assert(err, jc.ErrorIsNil)
   155  			// increment the general counter
   156  			atomic.AddInt64(counter, 1)
   157  		}
   158  	}
   159  
   160  	for i := 0; i < concurrentLocks; i++ {
   161  		go stress(fmt.Sprintf("Lock %d", i))
   162  	}
   163  	for i := 0; i < concurrentLocks; i++ {
   164  		<-done
   165  	}
   166  	c.Assert(*counter, gc.Equals, int64(lockAttempts*concurrentLocks))
   167  }