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

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

Issue 1159173004: Non racy tests for memlock. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Created 5 years, 6 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 | « no previous file | go/src/infra/gae/libs/memlock/memlock.infra_testing » ('j') | 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 allows multiple appengine handlers to coordinate best-effort 5 // Package memlock allows multiple appengine handlers to coordinate best-effort
6 // mutual execution via memcache. "best-effort" here means "best-effort"... 6 // mutual execution via memcache. "best-effort" here means "best-effort"...
7 // memcache is not reliable. However, colliding on memcache is a lot cheaper 7 // memcache is not reliable. However, colliding on memcache is a lot cheaper
8 // than, for example, colliding with datastore transactions. 8 // than, for example, colliding with datastore transactions.
9 package memlock 9 package memlock
10 10
11 import ( 11 import (
12 "bytes" 12 "bytes"
13 "errors" 13 "errors"
14 "infra/gae/libs/wrapper" 14 "infra/gae/libs/wrapper"
15 "infra/libs/clock"
15 "sync/atomic" 16 "sync/atomic"
16 "time" 17 "time"
17 18
18 "github.com/luci/luci-go/common/logging" 19 "github.com/luci/luci-go/common/logging"
19 "golang.org/x/net/context" 20 "golang.org/x/net/context"
20 21
21 "appengine/memcache" 22 "appengine/memcache"
22 ) 23 )
23 24
24 // ErrFailedToLock is returned from TryWithLock when it fails to obtain a lock 25 // ErrFailedToLock is returned from TryWithLock when it fails to obtain a lock
(...skipping 26 matching lines...) Expand all
51 // sucessful. The `check` function can be used within f to see if the lock is 52 // sucessful. The `check` function can be used within f to see if the lock is
52 // still held. 53 // still held.
53 // 54 //
54 // TryWithLock function returns ErrFailedToLock if it fails to obtain the lock, 55 // TryWithLock function returns ErrFailedToLock if it fails to obtain the lock,
55 // otherwise returns the error that f returns. 56 // otherwise returns the error that f returns.
56 // 57 //
57 // `key` is the memcache key to use (i.e. the name of the lock). Clients locking 58 // `key` is the memcache key to use (i.e. the name of the lock). Clients locking
58 // the same data must use the same key. clientID is the unique identifier for 59 // the same data must use the same key. clientID is the unique identifier for
59 // this client (lock-holder). If it's empty then TryWithLock() will return 60 // this client (lock-holder). If it's empty then TryWithLock() will return
60 // ErrEmptyClientID. 61 // ErrEmptyClientID.
61 func TryWithLock(c context.Context, key, clientID string, f func(check func() bo ol) error) error { 62 func TryWithLock(ctx context.Context, key, clientID string, f func(check func() bool) error) error {
62 if len(clientID) == 0 { 63 if len(clientID) == 0 {
63 return ErrEmptyClientID 64 return ErrEmptyClientID
64 } 65 }
65 66
66 » c = logging.SetField(c, "key", key) 67 » ctx = logging.SetField(ctx, "key", key)
67 » c = logging.SetField(c, "clientID", clientID) 68 » ctx = logging.SetField(ctx, "clientID", clientID)
68 » log := logging.Get(c) 69 » log := logging.Get(ctx)
69 » mc := wrapper.GetMC(c) 70 » mc := wrapper.GetMC(ctx)
70 71
71 key = memlockKeyPrefix + key 72 key = memlockKeyPrefix + key
72 cid := []byte(clientID) 73 cid := []byte(clientID)
73 74
74 // checkAnd gets the current value from memcache, and then attempts to d o the 75 // checkAnd gets the current value from memcache, and then attempts to d o the
75 // checkOp (which can either be `refresh` or `release`). These pieces of 76 // checkOp (which can either be `refresh` or `release`). These pieces of
76 // functionality are necessarially intertwined, because CAS only works w ith 77 // functionality are necessarially intertwined, because CAS only works w ith
77 // the exact-same *Item which was returned from a Get. 78 // the exact-same *Item which was returned from a Get.
78 // 79 //
79 // refresh will attempt to CAS the item with the same content to reset i t's 80 // refresh will attempt to CAS the item with the same content to reset i t's
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 // if we do. It will stop doing this when either stopChan is activated ( e.g. 145 // if we do. It will stop doing this when either stopChan is activated ( e.g.
145 // the user's function returns) or we lose the lock (memcache flake, etc .). 146 // the user's function returns) or we lose the lock (memcache flake, etc .).
146 go func() { 147 go func() {
147 defer close(stoppedChan) 148 defer close(stoppedChan)
148 149
149 checkLoop: 150 checkLoop:
150 for { 151 for {
151 select { 152 select {
152 case <-stopChan: 153 case <-stopChan:
153 break checkLoop 154 break checkLoop
154 » » » case <-time.After(delay): 155 » » » case <-clock.Get(ctx).After(delay):
155 } 156 }
156 if !checkAnd(refresh) { 157 if !checkAnd(refresh) {
157 atomic.StoreUint32(&held, 0) 158 atomic.StoreUint32(&held, 0)
158 log.Warningf("lost lock: %s", err) 159 log.Warningf("lost lock: %s", err)
159 » » » » break 160 » » » » return
160 } 161 }
161 } 162 }
162 163
163 checkAnd(release) 164 checkAnd(release)
164 atomic.StoreUint32(&held, 0) 165 atomic.StoreUint32(&held, 0)
165 }() 166 }()
166 167
167 return f(func() bool { return atomic.LoadUint32(&held) == 1 }) 168 return f(func() bool { return atomic.LoadUint32(&held) == 1 })
168 } 169 }
OLDNEW
« no previous file with comments | « no previous file | go/src/infra/gae/libs/memlock/memlock.infra_testing » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698