| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package memory | |
| 6 | |
| 7 import ( | |
| 8 "sync" | |
| 9 "time" | |
| 10 | |
| 11 "golang.org/x/net/context" | |
| 12 | |
| 13 "github.com/luci/gae" | |
| 14 "github.com/luci/gae/dummy" | |
| 15 | |
| 16 "github.com/luci/luci-go/common/clock" | |
| 17 ) | |
| 18 | |
| 19 type mcItem struct { | |
| 20 key string | |
| 21 value []byte | |
| 22 object interface{} | |
| 23 flags uint32 | |
| 24 expiration time.Duration | |
| 25 | |
| 26 CasID uint64 | |
| 27 } | |
| 28 | |
| 29 var _ gae.MCItem = (*mcItem)(nil) | |
| 30 | |
| 31 func (m *mcItem) Key() string { return m.key } | |
| 32 func (m *mcItem) Value() []byte { return m.value } | |
| 33 func (m *mcItem) Object() interface{} { return m.object } | |
| 34 func (m *mcItem) Flags() uint32 { return m.flags } | |
| 35 func (m *mcItem) Expiration() time.Duration { return m.expiration } | |
| 36 | |
| 37 func (m *mcItem) SetKey(key string) gae.MCItem { | |
| 38 m.key = key | |
| 39 return m | |
| 40 } | |
| 41 func (m *mcItem) SetValue(val []byte) gae.MCItem { | |
| 42 m.value = val | |
| 43 return m | |
| 44 } | |
| 45 func (m *mcItem) SetObject(obj interface{}) gae.MCItem { | |
| 46 m.object = obj | |
| 47 return m | |
| 48 } | |
| 49 func (m *mcItem) SetFlags(flg uint32) gae.MCItem { | |
| 50 m.flags = flg | |
| 51 return m | |
| 52 } | |
| 53 func (m *mcItem) SetExpiration(exp time.Duration) gae.MCItem { | |
| 54 m.expiration = exp | |
| 55 return m | |
| 56 } | |
| 57 | |
| 58 func (m *mcItem) duplicate() *mcItem { | |
| 59 ret := mcItem{} | |
| 60 ret = *m | |
| 61 ret.value = make([]byte, len(m.value)) | |
| 62 copy(ret.value, m.value) | |
| 63 return &ret | |
| 64 } | |
| 65 | |
| 66 type memcacheData struct { | |
| 67 lock sync.Mutex | |
| 68 items map[string]*mcItem | |
| 69 casID uint64 | |
| 70 } | |
| 71 | |
| 72 // memcacheImpl binds the current connection's memcache data to an | |
| 73 // implementation of {gae.Memcache, gae.Testable}. | |
| 74 type memcacheImpl struct { | |
| 75 gae.Memcache | |
| 76 | |
| 77 data *memcacheData | |
| 78 ctx context.Context | |
| 79 } | |
| 80 | |
| 81 var _ gae.Memcache = (*memcacheImpl)(nil) | |
| 82 | |
| 83 // useMC adds a gae.Memcache implementation to context, accessible | |
| 84 // by gae.GetMC(c) | |
| 85 func useMC(c context.Context) context.Context { | |
| 86 lck := sync.Mutex{} | |
| 87 mcdMap := map[string]*memcacheData{} | |
| 88 | |
| 89 return gae.SetMCFactory(c, func(ic context.Context) gae.Memcache { | |
| 90 lck.Lock() | |
| 91 defer lck.Unlock() | |
| 92 | |
| 93 ns := curGID(ic).namespace | |
| 94 mcd, ok := mcdMap[ns] | |
| 95 if !ok { | |
| 96 mcd = &memcacheData{items: map[string]*mcItem{}} | |
| 97 mcdMap[ns] = mcd | |
| 98 } | |
| 99 | |
| 100 return &memcacheImpl{ | |
| 101 dummy.MC(), | |
| 102 mcd, | |
| 103 ic, | |
| 104 } | |
| 105 }) | |
| 106 } | |
| 107 | |
| 108 func (m *memcacheImpl) mkItemLocked(i gae.MCItem) (ret *mcItem) { | |
| 109 m.data.casID++ | |
| 110 | |
| 111 var exp time.Duration | |
| 112 if i.Expiration() != 0 { | |
| 113 exp = time.Duration(clock.Now(m.ctx).Add(i.Expiration()).UnixNan
o()) | |
| 114 } | |
| 115 newItem := mcItem{ | |
| 116 key: i.Key(), | |
| 117 flags: i.Flags(), | |
| 118 expiration: exp, | |
| 119 value: i.Value(), | |
| 120 CasID: m.data.casID, | |
| 121 } | |
| 122 return newItem.duplicate() | |
| 123 } | |
| 124 | |
| 125 func (m *memcacheImpl) NewItem(key string) gae.MCItem { | |
| 126 return &mcItem{key: key} | |
| 127 } | |
| 128 | |
| 129 // Add implements context.MCSingleReadWriter.Add. | |
| 130 func (m *memcacheImpl) Add(i gae.MCItem) error { | |
| 131 m.data.lock.Lock() | |
| 132 defer m.data.lock.Unlock() | |
| 133 | |
| 134 if _, ok := m.retrieveLocked(i.Key()); !ok { | |
| 135 m.data.items[i.Key()] = m.mkItemLocked(i) | |
| 136 return nil | |
| 137 } | |
| 138 return gae.ErrMCNotStored | |
| 139 } | |
| 140 | |
| 141 // CompareAndSwap implements context.MCSingleReadWriter.CompareAndSwap. | |
| 142 func (m *memcacheImpl) CompareAndSwap(item gae.MCItem) error { | |
| 143 m.data.lock.Lock() | |
| 144 defer m.data.lock.Unlock() | |
| 145 | |
| 146 if cur, ok := m.retrieveLocked(item.Key()); ok { | |
| 147 casid := uint64(0) | |
| 148 if mi, ok := item.(*mcItem); ok && mi != nil { | |
| 149 casid = mi.CasID | |
| 150 } | |
| 151 | |
| 152 if cur.CasID == casid { | |
| 153 m.data.items[item.Key()] = m.mkItemLocked(item) | |
| 154 } else { | |
| 155 return gae.ErrMCCASConflict | |
| 156 } | |
| 157 } else { | |
| 158 return gae.ErrMCNotStored | |
| 159 } | |
| 160 return nil | |
| 161 } | |
| 162 | |
| 163 // Set implements context.MCSingleReadWriter.Set. | |
| 164 func (m *memcacheImpl) Set(i gae.MCItem) error { | |
| 165 m.data.lock.Lock() | |
| 166 defer m.data.lock.Unlock() | |
| 167 m.data.items[i.Key()] = m.mkItemLocked(i) | |
| 168 return nil | |
| 169 } | |
| 170 | |
| 171 // Get implements context.MCSingleReadWriter.Get. | |
| 172 func (m *memcacheImpl) Get(key string) (itm gae.MCItem, err error) { | |
| 173 m.data.lock.Lock() | |
| 174 defer m.data.lock.Unlock() | |
| 175 if val, ok := m.retrieveLocked(key); ok { | |
| 176 itm = val.duplicate().SetExpiration(0) | |
| 177 } else { | |
| 178 err = gae.ErrMCCacheMiss | |
| 179 } | |
| 180 return | |
| 181 } | |
| 182 | |
| 183 // Delete implements context.MCSingleReadWriter.Delete. | |
| 184 func (m *memcacheImpl) Delete(key string) error { | |
| 185 m.data.lock.Lock() | |
| 186 defer m.data.lock.Unlock() | |
| 187 | |
| 188 if _, ok := m.retrieveLocked(key); ok { | |
| 189 delete(m.data.items, key) | |
| 190 return nil | |
| 191 } | |
| 192 return gae.ErrMCCacheMiss | |
| 193 } | |
| 194 | |
| 195 func (m *memcacheImpl) retrieveLocked(key string) (*mcItem, bool) { | |
| 196 ret, ok := m.data.items[key] | |
| 197 if ok && ret.Expiration() != 0 && ret.Expiration() < time.Duration(clock
.Now(m.ctx).UnixNano()) { | |
| 198 ret = nil | |
| 199 ok = false | |
| 200 delete(m.data.items, key) | |
| 201 } | |
| 202 return ret, ok | |
| 203 } | |
| OLD | NEW |