OLD | NEW |
1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
3 // that can be found in the LICENSE file. | 3 // that can be 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 |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
64 // Note that the lock provided by TryWithLock is a best-effort lock... some | 64 // Note that the lock provided by TryWithLock is a best-effort lock... some |
65 // other form of locking or synchronization should be used inside of f (such as | 65 // other form of locking or synchronization should be used inside of f (such as |
66 // Datastore transactions) to ensure that f is, in fact, operating exclusively. | 66 // Datastore transactions) to ensure that f is, in fact, operating exclusively. |
67 // The purpose of TryWithLock is to have a cheap filter to prevent unnecessary | 67 // The purpose of TryWithLock is to have a cheap filter to prevent unnecessary |
68 // contention on heavier synchronization primitives like transactions. | 68 // contention on heavier synchronization primitives like transactions. |
69 func TryWithLock(ctx context.Context, key, clientID string, f func(context.Conte
xt) error) error { | 69 func TryWithLock(ctx context.Context, key, clientID string, f func(context.Conte
xt) error) error { |
70 if len(clientID) == 0 { | 70 if len(clientID) == 0 { |
71 return ErrEmptyClientID | 71 return ErrEmptyClientID |
72 } | 72 } |
73 | 73 |
74 » ctx = logging.SetField(ctx, "key", key) | 74 » log := logging.Get( |
75 » ctx = logging.SetField(ctx, "clientID", clientID) | 75 » » logging.SetFields(ctx, logging.Fields{ |
76 » log := logging.Get(ctx) | 76 » » » "key": key, |
| 77 » » » "clientID": clientID, |
| 78 » » })) |
77 mc := memcache.Get(ctx) | 79 mc := memcache.Get(ctx) |
78 | 80 |
79 key = memlockKeyPrefix + key | 81 key = memlockKeyPrefix + key |
80 cid := []byte(clientID) | 82 cid := []byte(clientID) |
81 | 83 |
82 // checkAnd gets the current value from memcache, and then attempts to d
o the | 84 // checkAnd gets the current value from memcache, and then attempts to d
o the |
83 // checkOp (which can either be `refresh` or `release`). These pieces of | 85 // checkOp (which can either be `refresh` or `release`). These pieces of |
84 // functionality are necessarially intertwined, because CAS only works w
ith | 86 // functionality are necessarially intertwined, because CAS only works w
ith |
85 // the exact-same *Item which was returned from a Get. | 87 // the exact-same *Item which was returned from a Get. |
86 // | 88 // |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
164 } | 166 } |
165 | 167 |
166 if testStopCB != nil { | 168 if testStopCB != nil { |
167 testStopCB() | 169 testStopCB() |
168 } | 170 } |
169 checkAnd(release) | 171 checkAnd(release) |
170 }() | 172 }() |
171 | 173 |
172 return f(subCtx) | 174 return f(subCtx) |
173 } | 175 } |
OLD | NEW |