Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(306)

Side by Side Diff: go/src/infra/gae/libs/memlock/memlock_test.go

Issue 1233413002: Simplify memlock and make it less racy. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: docs Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « go/src/infra/gae/libs/memlock/memlock.go ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package memlock 5 package memlock
6 6
7 import ( 7 import (
8 "fmt" 8 "fmt"
9 » "runtime" 9 » "sync"
10 "testing" 10 "testing"
11 "time" 11 "time"
12 12
13 "infra/gae/libs/gae" 13 "infra/gae/libs/gae"
14 "infra/gae/libs/gae/filters/featureBreaker" 14 "infra/gae/libs/gae/filters/featureBreaker"
15 "infra/gae/libs/gae/memory" 15 "infra/gae/libs/gae/memory"
16 16
17 "github.com/luci/luci-go/common/clock" 17 "github.com/luci/luci-go/common/clock"
18 "github.com/luci/luci-go/common/clock/testclock" 18 "github.com/luci/luci-go/common/clock/testclock"
19 . "github.com/smartystreets/goconvey/convey" 19 . "github.com/smartystreets/goconvey/convey"
20 "golang.org/x/net/context" 20 "golang.org/x/net/context"
21 ) 21 )
22 22
23 func init() { 23 func init() {
24 delay = time.Millisecond 24 delay = time.Millisecond
25 memcacheLockTime = time.Millisecond * 4 25 memcacheLockTime = time.Millisecond * 4
26 } 26 }
27 27
28 type getBlockerFilter struct {
29 gae.Memcache
30 sync.Mutex
31 }
32
33 func (f *getBlockerFilter) Get(key string) (gae.MCItem, error) {
34 f.Lock()
35 defer f.Unlock()
36 return f.Memcache.Get(key)
37 }
38
28 func TestSimple(t *testing.T) { 39 func TestSimple(t *testing.T) {
29 // TODO(riannucci): Mock time.After so that we don't have to delay for r eal. 40 // TODO(riannucci): Mock time.After so that we don't have to delay for r eal.
30 41
31 const key = memlockKeyPrefix + "testkey" 42 const key = memlockKeyPrefix + "testkey"
32 43
33 Convey("basic locking", t, func() { 44 Convey("basic locking", t, func() {
34 start := time.Date(1986, time.October, 26, 1, 20, 00, 00, time.U TC) 45 start := time.Date(1986, time.October, 26, 1, 20, 00, 00, time.U TC)
35 ctx, clk := testclock.UseTime(context.Background(), start) 46 ctx, clk := testclock.UseTime(context.Background(), start)
36 blocker := make(chan struct{}) 47 blocker := make(chan struct{})
37 clk.SetTimerCallback(func(clock.Timer) { 48 clk.SetTimerCallback(func(clock.Timer) {
38 clk.Add(delay) 49 clk.Add(delay)
39 select { 50 select {
40 case blocker <- struct{}{}: 51 case blocker <- struct{}{}:
41 default: 52 default:
42 } 53 }
43 }) 54 })
55 waitFalse := func(ctx context.Context) {
56 loop:
57 for {
58 select {
59 case <-blocker:
60 continue
61 case <-ctx.Done():
62 break loop
63 }
64 }
65 }
66
44 ctx, fb := featureBreaker.FilterMC(memory.Use(ctx), nil) 67 ctx, fb := featureBreaker.FilterMC(memory.Use(ctx), nil)
45 mc := gae.GetMC(ctx) 68 mc := gae.GetMC(ctx)
46 69
47 Convey("fails to acquire when memcache is down", func() { 70 Convey("fails to acquire when memcache is down", func() {
48 fb.BreakFeatures(nil, "Add") 71 fb.BreakFeatures(nil, "Add")
49 » » » err := TryWithLock(ctx, "testkey", "id", func(check func () bool) error { 72 » » » err := TryWithLock(ctx, "testkey", "id", func(context.Co ntext) error {
50 // should never reach here 73 // should never reach here
51 So(false, ShouldBeTrue) 74 So(false, ShouldBeTrue)
52 return nil 75 return nil
53 }) 76 })
54 So(err, ShouldEqual, ErrFailedToLock) 77 So(err, ShouldEqual, ErrFailedToLock)
55 }) 78 })
56 79
57 Convey("returns the inner error", func() { 80 Convey("returns the inner error", func() {
58 toRet := fmt.Errorf("sup") 81 toRet := fmt.Errorf("sup")
59 » » » err := TryWithLock(ctx, "testkey", "id", func(check func () bool) error { 82 » » » err := TryWithLock(ctx, "testkey", "id", func(context.Co ntext) error {
60 return toRet 83 return toRet
61 }) 84 })
62 So(err, ShouldEqual, toRet) 85 So(err, ShouldEqual, toRet)
63 }) 86 })
64 87
65 Convey("returns the error", func() { 88 Convey("returns the error", func() {
66 toRet := fmt.Errorf("sup") 89 toRet := fmt.Errorf("sup")
67 » » » err := TryWithLock(ctx, "testkey", "id", func(check func () bool) error { 90 » » » err := TryWithLock(ctx, "testkey", "id", func(context.Co ntext) error {
68 return toRet 91 return toRet
69 }) 92 })
70 So(err, ShouldEqual, toRet) 93 So(err, ShouldEqual, toRet)
71 }) 94 })
72 95
73 Convey("can acquire when empty", func() { 96 Convey("can acquire when empty", func() {
74 » » » err := TryWithLock(ctx, "testkey", "id", func(check func () bool) error { 97 » » » err := TryWithLock(ctx, "testkey", "id", func(ctx contex t.Context) error {
75 » » » » So(check(), ShouldBeTrue) 98 » » » » isDone := func() bool {
99 » » » » » select {
100 » » » » » case <-ctx.Done():
101 » » » » » » return true
102 » » » » » default:
103 » » » » » » return false
104 » » » » » }
105 » » » » }
76 106
77 » » » » waitFalse := func() { 107 » » » » So(isDone(), ShouldBeFalse)
78 » » » » » <-blocker
79 » » » » » for i := 0; i < 3; i++ {
80 » » » » » » if check() {
81 » » » » » » » runtime.Gosched()
82 » » » » » » }
83 » » » » » }
84 » » » » » So(check(), ShouldBeFalse)
85 » » » » }
86 108
87 Convey("waiting for a while keeps refreshing the lock", func() { 109 Convey("waiting for a while keeps refreshing the lock", func() {
88 // simulate waiting for 64*delay time, a nd ensuring that checkLoop 110 // simulate waiting for 64*delay time, a nd ensuring that checkLoop
89 // runs that many times. 111 // runs that many times.
90 for i := 0; i < 64; i++ { 112 for i := 0; i < 64; i++ {
91 <-blocker 113 <-blocker
92 clk.Add(delay) 114 clk.Add(delay)
93 } 115 }
94 » » » » » So(check(), ShouldBeTrue) 116 » » » » » So(isDone(), ShouldBeFalse)
95 }) 117 })
96 118
97 Convey("but sometimes we might lose it", func() { 119 Convey("but sometimes we might lose it", func() {
98 Convey("because it was evicted", func() { 120 Convey("because it was evicted", func() {
99 mc.Delete(key) 121 mc.Delete(key)
100 clk.Add(memcacheLockTime) 122 clk.Add(memcacheLockTime)
101 » » » » » » waitFalse() 123 » » » » » » waitFalse(ctx)
102 » » » » » })
103
104 » » » » » Convey("or because it was stolen", func( ) {
105 » » » » » » mc.Set(mc.NewItem(key).SetValue( []byte("wat")))
106 » » » » » » waitFalse()
107 }) 124 })
108 125
109 Convey("or because of service issues", f unc() { 126 Convey("or because of service issues", f unc() {
110 fb.BreakFeatures(nil, "CompareAn dSwap") 127 fb.BreakFeatures(nil, "CompareAn dSwap")
111 » » » » » » waitFalse() 128 » » » » » » waitFalse(ctx)
112 }) 129 })
113 }) 130 })
114 return nil 131 return nil
115 }) 132 })
116 So(err, ShouldBeNil) 133 So(err, ShouldBeNil)
117 }) 134 })
118 135
136 Convey("can lose it when it gets stolen", func() {
137 gbf := &getBlockerFilter{}
138 ctx = gae.AddMCFilters(ctx, func(_ context.Context, mc g ae.Memcache) gae.Memcache {
139 gbf.Memcache = mc
140 return gbf
141 })
142 mc = gae.GetMC(ctx)
143 err := TryWithLock(ctx, "testkey", "id", func(ctx contex t.Context) error {
144 // simulate waiting for 64*delay time, and ensur ing that checkLoop
145 // runs that many times.
146 for i := 0; i < 64; i++ {
147 <-blocker
148 clk.Add(delay)
149 }
150 gbf.Lock()
151 mc.Set(mc.NewItem(key).SetValue([]byte("wat")))
152 gbf.Unlock()
153 waitFalse(ctx)
154 return nil
155 })
156 So(err, ShouldBeNil)
157 })
158
119 Convey("an empty context id is an error", func() { 159 Convey("an empty context id is an error", func() {
120 So(TryWithLock(ctx, "testkey", "", nil), ShouldEqual, Er rEmptyClientID) 160 So(TryWithLock(ctx, "testkey", "", nil), ShouldEqual, Er rEmptyClientID)
121 }) 161 })
122 }) 162 })
123 } 163 }
OLDNEW
« no previous file with comments | « go/src/infra/gae/libs/memlock/memlock.go ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698