annotate libsanitizer/sanitizer_common/sanitizer_addrhashmap.h @ 144:8f4e72ab4e11

fix segmentation fault caused by nothing next cur_op to end
author Takahiro SHIMIZU <anatofuz@cr.ie.u-ryukyu.ac.jp>
date Sun, 23 Dec 2018 21:23:56 +0900
parents 04ced10e8804
children 1830386684a0
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
111
kono
parents:
diff changeset
1 //===-- sanitizer_addrhashmap.h ---------------------------------*- C++ -*-===//
kono
parents:
diff changeset
2 //
kono
parents:
diff changeset
3 // This file is distributed under the University of Illinois Open Source
kono
parents:
diff changeset
4 // License. See LICENSE.TXT for details.
kono
parents:
diff changeset
5 //
kono
parents:
diff changeset
6 //===----------------------------------------------------------------------===//
kono
parents:
diff changeset
7 //
kono
parents:
diff changeset
8 // Concurrent uptr->T hashmap.
kono
parents:
diff changeset
9 //
kono
parents:
diff changeset
10 //===----------------------------------------------------------------------===//
kono
parents:
diff changeset
11
kono
parents:
diff changeset
12 #ifndef SANITIZER_ADDRHASHMAP_H
kono
parents:
diff changeset
13 #define SANITIZER_ADDRHASHMAP_H
kono
parents:
diff changeset
14
kono
parents:
diff changeset
15 #include "sanitizer_common.h"
kono
parents:
diff changeset
16 #include "sanitizer_mutex.h"
kono
parents:
diff changeset
17 #include "sanitizer_atomic.h"
kono
parents:
diff changeset
18 #include "sanitizer_allocator_internal.h"
kono
parents:
diff changeset
19
kono
parents:
diff changeset
20 namespace __sanitizer {
kono
parents:
diff changeset
21
kono
parents:
diff changeset
22 // Concurrent uptr->T hashmap.
kono
parents:
diff changeset
23 // T must be a POD type, kSize is preferably a prime but can be any number.
kono
parents:
diff changeset
24 // Usage example:
kono
parents:
diff changeset
25 //
kono
parents:
diff changeset
26 // typedef AddrHashMap<uptr, 11> Map;
kono
parents:
diff changeset
27 // Map m;
kono
parents:
diff changeset
28 // {
kono
parents:
diff changeset
29 // Map::Handle h(&m, addr);
kono
parents:
diff changeset
30 // use h.operator->() to access the data
kono
parents:
diff changeset
31 // if h.created() then the element was just created, and the current thread
kono
parents:
diff changeset
32 // has exclusive access to it
kono
parents:
diff changeset
33 // otherwise the current thread has only read access to the data
kono
parents:
diff changeset
34 // }
kono
parents:
diff changeset
35 // {
kono
parents:
diff changeset
36 // Map::Handle h(&m, addr, true);
kono
parents:
diff changeset
37 // this will remove the data from the map in Handle dtor
kono
parents:
diff changeset
38 // the current thread has exclusive access to the data
kono
parents:
diff changeset
39 // if !h.exists() then the element never existed
kono
parents:
diff changeset
40 // }
kono
parents:
diff changeset
41 template<typename T, uptr kSize>
kono
parents:
diff changeset
42 class AddrHashMap {
kono
parents:
diff changeset
43 private:
kono
parents:
diff changeset
44 struct Cell {
kono
parents:
diff changeset
45 atomic_uintptr_t addr;
kono
parents:
diff changeset
46 T val;
kono
parents:
diff changeset
47 };
kono
parents:
diff changeset
48
kono
parents:
diff changeset
49 struct AddBucket {
kono
parents:
diff changeset
50 uptr cap;
kono
parents:
diff changeset
51 uptr size;
kono
parents:
diff changeset
52 Cell cells[1]; // variable len
kono
parents:
diff changeset
53 };
kono
parents:
diff changeset
54
kono
parents:
diff changeset
55 static const uptr kBucketSize = 3;
kono
parents:
diff changeset
56
kono
parents:
diff changeset
57 struct Bucket {
kono
parents:
diff changeset
58 RWMutex mtx;
kono
parents:
diff changeset
59 atomic_uintptr_t add;
kono
parents:
diff changeset
60 Cell cells[kBucketSize];
kono
parents:
diff changeset
61 };
kono
parents:
diff changeset
62
kono
parents:
diff changeset
63 public:
kono
parents:
diff changeset
64 AddrHashMap();
kono
parents:
diff changeset
65
kono
parents:
diff changeset
66 class Handle {
kono
parents:
diff changeset
67 public:
kono
parents:
diff changeset
68 Handle(AddrHashMap<T, kSize> *map, uptr addr);
kono
parents:
diff changeset
69 Handle(AddrHashMap<T, kSize> *map, uptr addr, bool remove);
kono
parents:
diff changeset
70 Handle(AddrHashMap<T, kSize> *map, uptr addr, bool remove, bool create);
kono
parents:
diff changeset
71
kono
parents:
diff changeset
72 ~Handle();
kono
parents:
diff changeset
73 T *operator->();
kono
parents:
diff changeset
74 T &operator*();
kono
parents:
diff changeset
75 const T &operator*() const;
kono
parents:
diff changeset
76 bool created() const;
kono
parents:
diff changeset
77 bool exists() const;
kono
parents:
diff changeset
78
kono
parents:
diff changeset
79 private:
kono
parents:
diff changeset
80 friend AddrHashMap<T, kSize>;
kono
parents:
diff changeset
81 AddrHashMap<T, kSize> *map_;
kono
parents:
diff changeset
82 Bucket *bucket_;
kono
parents:
diff changeset
83 Cell *cell_;
kono
parents:
diff changeset
84 uptr addr_;
kono
parents:
diff changeset
85 uptr addidx_;
kono
parents:
diff changeset
86 bool created_;
kono
parents:
diff changeset
87 bool remove_;
kono
parents:
diff changeset
88 bool create_;
kono
parents:
diff changeset
89 };
kono
parents:
diff changeset
90
kono
parents:
diff changeset
91 private:
kono
parents:
diff changeset
92 friend class Handle;
kono
parents:
diff changeset
93 Bucket *table_;
kono
parents:
diff changeset
94
kono
parents:
diff changeset
95 void acquire(Handle *h);
kono
parents:
diff changeset
96 void release(Handle *h);
kono
parents:
diff changeset
97 uptr calcHash(uptr addr);
kono
parents:
diff changeset
98 };
kono
parents:
diff changeset
99
kono
parents:
diff changeset
100 template<typename T, uptr kSize>
kono
parents:
diff changeset
101 AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr) {
kono
parents:
diff changeset
102 map_ = map;
kono
parents:
diff changeset
103 addr_ = addr;
kono
parents:
diff changeset
104 remove_ = false;
kono
parents:
diff changeset
105 create_ = true;
kono
parents:
diff changeset
106 map_->acquire(this);
kono
parents:
diff changeset
107 }
kono
parents:
diff changeset
108
kono
parents:
diff changeset
109 template<typename T, uptr kSize>
kono
parents:
diff changeset
110 AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr,
kono
parents:
diff changeset
111 bool remove) {
kono
parents:
diff changeset
112 map_ = map;
kono
parents:
diff changeset
113 addr_ = addr;
kono
parents:
diff changeset
114 remove_ = remove;
kono
parents:
diff changeset
115 create_ = true;
kono
parents:
diff changeset
116 map_->acquire(this);
kono
parents:
diff changeset
117 }
kono
parents:
diff changeset
118
kono
parents:
diff changeset
119 template<typename T, uptr kSize>
kono
parents:
diff changeset
120 AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr,
kono
parents:
diff changeset
121 bool remove, bool create) {
kono
parents:
diff changeset
122 map_ = map;
kono
parents:
diff changeset
123 addr_ = addr;
kono
parents:
diff changeset
124 remove_ = remove;
kono
parents:
diff changeset
125 create_ = create;
kono
parents:
diff changeset
126 map_->acquire(this);
kono
parents:
diff changeset
127 }
kono
parents:
diff changeset
128
kono
parents:
diff changeset
129 template<typename T, uptr kSize>
kono
parents:
diff changeset
130 AddrHashMap<T, kSize>::Handle::~Handle() {
kono
parents:
diff changeset
131 map_->release(this);
kono
parents:
diff changeset
132 }
kono
parents:
diff changeset
133
kono
parents:
diff changeset
134 template <typename T, uptr kSize>
kono
parents:
diff changeset
135 T *AddrHashMap<T, kSize>::Handle::operator->() {
kono
parents:
diff changeset
136 return &cell_->val;
kono
parents:
diff changeset
137 }
kono
parents:
diff changeset
138
kono
parents:
diff changeset
139 template <typename T, uptr kSize>
kono
parents:
diff changeset
140 const T &AddrHashMap<T, kSize>::Handle::operator*() const {
kono
parents:
diff changeset
141 return cell_->val;
kono
parents:
diff changeset
142 }
kono
parents:
diff changeset
143
kono
parents:
diff changeset
144 template <typename T, uptr kSize>
kono
parents:
diff changeset
145 T &AddrHashMap<T, kSize>::Handle::operator*() {
kono
parents:
diff changeset
146 return cell_->val;
kono
parents:
diff changeset
147 }
kono
parents:
diff changeset
148
kono
parents:
diff changeset
149 template<typename T, uptr kSize>
kono
parents:
diff changeset
150 bool AddrHashMap<T, kSize>::Handle::created() const {
kono
parents:
diff changeset
151 return created_;
kono
parents:
diff changeset
152 }
kono
parents:
diff changeset
153
kono
parents:
diff changeset
154 template<typename T, uptr kSize>
kono
parents:
diff changeset
155 bool AddrHashMap<T, kSize>::Handle::exists() const {
kono
parents:
diff changeset
156 return cell_ != nullptr;
kono
parents:
diff changeset
157 }
kono
parents:
diff changeset
158
kono
parents:
diff changeset
159 template<typename T, uptr kSize>
kono
parents:
diff changeset
160 AddrHashMap<T, kSize>::AddrHashMap() {
kono
parents:
diff changeset
161 table_ = (Bucket*)MmapOrDie(kSize * sizeof(table_[0]), "AddrHashMap");
kono
parents:
diff changeset
162 }
kono
parents:
diff changeset
163
kono
parents:
diff changeset
164 template<typename T, uptr kSize>
kono
parents:
diff changeset
165 void AddrHashMap<T, kSize>::acquire(Handle *h) {
kono
parents:
diff changeset
166 uptr addr = h->addr_;
kono
parents:
diff changeset
167 uptr hash = calcHash(addr);
kono
parents:
diff changeset
168 Bucket *b = &table_[hash];
kono
parents:
diff changeset
169
kono
parents:
diff changeset
170 h->created_ = false;
kono
parents:
diff changeset
171 h->addidx_ = -1U;
kono
parents:
diff changeset
172 h->bucket_ = b;
kono
parents:
diff changeset
173 h->cell_ = nullptr;
kono
parents:
diff changeset
174
kono
parents:
diff changeset
175 // If we want to remove the element, we need exclusive access to the bucket,
kono
parents:
diff changeset
176 // so skip the lock-free phase.
kono
parents:
diff changeset
177 if (h->remove_)
kono
parents:
diff changeset
178 goto locked;
kono
parents:
diff changeset
179
kono
parents:
diff changeset
180 retry:
kono
parents:
diff changeset
181 // First try to find an existing element w/o read mutex.
kono
parents:
diff changeset
182 CHECK(!h->remove_);
kono
parents:
diff changeset
183 // Check the embed cells.
kono
parents:
diff changeset
184 for (uptr i = 0; i < kBucketSize; i++) {
kono
parents:
diff changeset
185 Cell *c = &b->cells[i];
kono
parents:
diff changeset
186 uptr addr1 = atomic_load(&c->addr, memory_order_acquire);
kono
parents:
diff changeset
187 if (addr1 == addr) {
kono
parents:
diff changeset
188 h->cell_ = c;
kono
parents:
diff changeset
189 return;
kono
parents:
diff changeset
190 }
kono
parents:
diff changeset
191 }
kono
parents:
diff changeset
192
kono
parents:
diff changeset
193 // Check the add cells with read lock.
kono
parents:
diff changeset
194 if (atomic_load(&b->add, memory_order_relaxed)) {
kono
parents:
diff changeset
195 b->mtx.ReadLock();
kono
parents:
diff changeset
196 AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed);
kono
parents:
diff changeset
197 for (uptr i = 0; i < add->size; i++) {
kono
parents:
diff changeset
198 Cell *c = &add->cells[i];
kono
parents:
diff changeset
199 uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
kono
parents:
diff changeset
200 if (addr1 == addr) {
kono
parents:
diff changeset
201 h->addidx_ = i;
kono
parents:
diff changeset
202 h->cell_ = c;
kono
parents:
diff changeset
203 return;
kono
parents:
diff changeset
204 }
kono
parents:
diff changeset
205 }
kono
parents:
diff changeset
206 b->mtx.ReadUnlock();
kono
parents:
diff changeset
207 }
kono
parents:
diff changeset
208
kono
parents:
diff changeset
209 locked:
kono
parents:
diff changeset
210 // Re-check existence under write lock.
kono
parents:
diff changeset
211 // Embed cells.
kono
parents:
diff changeset
212 b->mtx.Lock();
kono
parents:
diff changeset
213 for (uptr i = 0; i < kBucketSize; i++) {
kono
parents:
diff changeset
214 Cell *c = &b->cells[i];
kono
parents:
diff changeset
215 uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
kono
parents:
diff changeset
216 if (addr1 == addr) {
kono
parents:
diff changeset
217 if (h->remove_) {
kono
parents:
diff changeset
218 h->cell_ = c;
kono
parents:
diff changeset
219 return;
kono
parents:
diff changeset
220 }
kono
parents:
diff changeset
221 b->mtx.Unlock();
kono
parents:
diff changeset
222 goto retry;
kono
parents:
diff changeset
223 }
kono
parents:
diff changeset
224 }
kono
parents:
diff changeset
225
kono
parents:
diff changeset
226 // Add cells.
kono
parents:
diff changeset
227 AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed);
kono
parents:
diff changeset
228 if (add) {
kono
parents:
diff changeset
229 for (uptr i = 0; i < add->size; i++) {
kono
parents:
diff changeset
230 Cell *c = &add->cells[i];
kono
parents:
diff changeset
231 uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
kono
parents:
diff changeset
232 if (addr1 == addr) {
kono
parents:
diff changeset
233 if (h->remove_) {
kono
parents:
diff changeset
234 h->addidx_ = i;
kono
parents:
diff changeset
235 h->cell_ = c;
kono
parents:
diff changeset
236 return;
kono
parents:
diff changeset
237 }
kono
parents:
diff changeset
238 b->mtx.Unlock();
kono
parents:
diff changeset
239 goto retry;
kono
parents:
diff changeset
240 }
kono
parents:
diff changeset
241 }
kono
parents:
diff changeset
242 }
kono
parents:
diff changeset
243
kono
parents:
diff changeset
244 // The element does not exist, no need to create it if we want to remove.
kono
parents:
diff changeset
245 if (h->remove_ || !h->create_) {
kono
parents:
diff changeset
246 b->mtx.Unlock();
kono
parents:
diff changeset
247 return;
kono
parents:
diff changeset
248 }
kono
parents:
diff changeset
249
kono
parents:
diff changeset
250 // Now try to create it under the mutex.
kono
parents:
diff changeset
251 h->created_ = true;
kono
parents:
diff changeset
252 // See if we have a free embed cell.
kono
parents:
diff changeset
253 for (uptr i = 0; i < kBucketSize; i++) {
kono
parents:
diff changeset
254 Cell *c = &b->cells[i];
kono
parents:
diff changeset
255 uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
kono
parents:
diff changeset
256 if (addr1 == 0) {
kono
parents:
diff changeset
257 h->cell_ = c;
kono
parents:
diff changeset
258 return;
kono
parents:
diff changeset
259 }
kono
parents:
diff changeset
260 }
kono
parents:
diff changeset
261
kono
parents:
diff changeset
262 // Store in the add cells.
kono
parents:
diff changeset
263 if (!add) {
kono
parents:
diff changeset
264 // Allocate a new add array.
kono
parents:
diff changeset
265 const uptr kInitSize = 64;
kono
parents:
diff changeset
266 add = (AddBucket*)InternalAlloc(kInitSize);
kono
parents:
diff changeset
267 internal_memset(add, 0, kInitSize);
kono
parents:
diff changeset
268 add->cap = (kInitSize - sizeof(*add)) / sizeof(add->cells[0]) + 1;
kono
parents:
diff changeset
269 add->size = 0;
kono
parents:
diff changeset
270 atomic_store(&b->add, (uptr)add, memory_order_relaxed);
kono
parents:
diff changeset
271 }
kono
parents:
diff changeset
272 if (add->size == add->cap) {
kono
parents:
diff changeset
273 // Grow existing add array.
kono
parents:
diff changeset
274 uptr oldsize = sizeof(*add) + (add->cap - 1) * sizeof(add->cells[0]);
kono
parents:
diff changeset
275 uptr newsize = oldsize * 2;
kono
parents:
diff changeset
276 AddBucket *add1 = (AddBucket*)InternalAlloc(newsize);
kono
parents:
diff changeset
277 internal_memset(add1, 0, newsize);
kono
parents:
diff changeset
278 add1->cap = (newsize - sizeof(*add)) / sizeof(add->cells[0]) + 1;
kono
parents:
diff changeset
279 add1->size = add->size;
kono
parents:
diff changeset
280 internal_memcpy(add1->cells, add->cells, add->size * sizeof(add->cells[0]));
kono
parents:
diff changeset
281 InternalFree(add);
kono
parents:
diff changeset
282 atomic_store(&b->add, (uptr)add1, memory_order_relaxed);
kono
parents:
diff changeset
283 add = add1;
kono
parents:
diff changeset
284 }
kono
parents:
diff changeset
285 // Store.
kono
parents:
diff changeset
286 uptr i = add->size++;
kono
parents:
diff changeset
287 Cell *c = &add->cells[i];
kono
parents:
diff changeset
288 CHECK_EQ(atomic_load(&c->addr, memory_order_relaxed), 0);
kono
parents:
diff changeset
289 h->addidx_ = i;
kono
parents:
diff changeset
290 h->cell_ = c;
kono
parents:
diff changeset
291 }
kono
parents:
diff changeset
292
kono
parents:
diff changeset
293 template<typename T, uptr kSize>
kono
parents:
diff changeset
294 void AddrHashMap<T, kSize>::release(Handle *h) {
kono
parents:
diff changeset
295 if (!h->cell_)
kono
parents:
diff changeset
296 return;
kono
parents:
diff changeset
297 Bucket *b = h->bucket_;
kono
parents:
diff changeset
298 Cell *c = h->cell_;
kono
parents:
diff changeset
299 uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
kono
parents:
diff changeset
300 if (h->created_) {
kono
parents:
diff changeset
301 // Denote completion of insertion.
kono
parents:
diff changeset
302 CHECK_EQ(addr1, 0);
kono
parents:
diff changeset
303 // After the following store, the element becomes available
kono
parents:
diff changeset
304 // for lock-free reads.
kono
parents:
diff changeset
305 atomic_store(&c->addr, h->addr_, memory_order_release);
kono
parents:
diff changeset
306 b->mtx.Unlock();
kono
parents:
diff changeset
307 } else if (h->remove_) {
kono
parents:
diff changeset
308 // Denote that the cell is empty now.
kono
parents:
diff changeset
309 CHECK_EQ(addr1, h->addr_);
kono
parents:
diff changeset
310 atomic_store(&c->addr, 0, memory_order_release);
kono
parents:
diff changeset
311 // See if we need to compact the bucket.
kono
parents:
diff changeset
312 AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed);
kono
parents:
diff changeset
313 if (h->addidx_ == -1U) {
kono
parents:
diff changeset
314 // Removed from embed array, move an add element into the freed cell.
kono
parents:
diff changeset
315 if (add && add->size != 0) {
kono
parents:
diff changeset
316 uptr last = --add->size;
kono
parents:
diff changeset
317 Cell *c1 = &add->cells[last];
kono
parents:
diff changeset
318 c->val = c1->val;
kono
parents:
diff changeset
319 uptr addr1 = atomic_load(&c1->addr, memory_order_relaxed);
kono
parents:
diff changeset
320 atomic_store(&c->addr, addr1, memory_order_release);
kono
parents:
diff changeset
321 atomic_store(&c1->addr, 0, memory_order_release);
kono
parents:
diff changeset
322 }
kono
parents:
diff changeset
323 } else {
kono
parents:
diff changeset
324 // Removed from add array, compact it.
kono
parents:
diff changeset
325 uptr last = --add->size;
kono
parents:
diff changeset
326 Cell *c1 = &add->cells[last];
kono
parents:
diff changeset
327 if (c != c1) {
kono
parents:
diff changeset
328 *c = *c1;
kono
parents:
diff changeset
329 atomic_store(&c1->addr, 0, memory_order_relaxed);
kono
parents:
diff changeset
330 }
kono
parents:
diff changeset
331 }
kono
parents:
diff changeset
332 if (add && add->size == 0) {
kono
parents:
diff changeset
333 // FIXME(dvyukov): free add?
kono
parents:
diff changeset
334 }
kono
parents:
diff changeset
335 b->mtx.Unlock();
kono
parents:
diff changeset
336 } else {
kono
parents:
diff changeset
337 CHECK_EQ(addr1, h->addr_);
kono
parents:
diff changeset
338 if (h->addidx_ != -1U)
kono
parents:
diff changeset
339 b->mtx.ReadUnlock();
kono
parents:
diff changeset
340 }
kono
parents:
diff changeset
341 }
kono
parents:
diff changeset
342
kono
parents:
diff changeset
343 template<typename T, uptr kSize>
kono
parents:
diff changeset
344 uptr AddrHashMap<T, kSize>::calcHash(uptr addr) {
kono
parents:
diff changeset
345 addr += addr << 10;
kono
parents:
diff changeset
346 addr ^= addr >> 6;
kono
parents:
diff changeset
347 return addr % kSize;
kono
parents:
diff changeset
348 }
kono
parents:
diff changeset
349
kono
parents:
diff changeset
350 } // namespace __sanitizer
kono
parents:
diff changeset
351
kono
parents:
diff changeset
352 #endif // SANITIZER_ADDRHASHMAP_H