| 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 |