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