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 |