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 }