edits
[cdsspec-compiler.git] / benchmark / concurrent-hashmap / hashmap.h
1 #ifndef _HASHMAP_H
2 #define _HASHMAP_H
3
4 #include <iostream>
5 #include <atomic>
6 #include "stdio.h" 
7 //#include <common.h>
8 #ifdef STANDALONE
9 #include <assert.h>
10 #define MODEL_ASSERT assert 
11 #else
12 #include <model-assert.h>
13 #endif
14 #include <stdlib.h>
15 #include <mutex>
16
17 #include "common.h"
18 #include "sc_annotation.h"
19
20 #define relaxed memory_order_relaxed
21 #define release memory_order_release
22 #define acquire memory_order_acquire
23 #define acq_rel memory_order_acq_rel
24 #define seq_cst memory_order_seq_cst
25
26 using namespace std;
27
28 /**
29         For the sake of simplicity, we do not use template but some toy structs to
30         represent the Key and Value.
31 */
32 struct Key {
33         // Probably represent the coordinate (x, y, z)
34         int x;
35         int y;
36         int z;
37
38         int hashCode() {
39                 return x + 31 * y + 31 * 31 * z;
40         }
41
42         bool equals(Key *other) {
43                 if (!other)
44                         return false;
45                 return x == other->x && y == other->y && z == other->z;
46         }
47
48         Key(int x, int y, int z) :
49                 x(x),
50                 y(y),
51                 z(z)
52         {
53
54         }
55 };
56
57 struct Value {
58         // Probably represent the speed vector (vX, vY, vZ)
59         int vX;
60         int vY;
61         int vZ;
62
63         Value(int vX, int vY, int vZ) :
64                 vX(vX),
65                 vY(vY),
66                 vZ(vZ)
67         {
68
69         }
70
71         bool equals(Value *other) {
72                 if (!other)
73                         return false;
74                 return vX == other->vX && vY == other->vY && vZ == other->vZ;
75         }
76 };
77
78 class Entry {
79         public:
80         Key *key;
81         atomic<Value*> value;
82         int hash;
83         atomic<Entry*> next;
84
85         Entry(int h, Key *k, Value *v, Entry *n) {
86                 this->hash = h;
87                 this->key = k;
88                 this->value.store(v, relaxed);
89                 this->next.store(n, relaxed);
90         }
91 };
92
93 class Segment {
94         public:
95         int count;
96         mutex segMutex;
97
98         void lock() {
99                 segMutex.lock();
100         }
101
102         void unlock() {
103                 segMutex.unlock();
104         }
105
106         Segment() {
107                 this->count = 0;
108         }
109 };
110
111 class HashMap {
112         public:
113         atomic<Entry*> *table;
114
115         int capacity;
116         int size;
117
118         static const int CONCURRENCY_LEVEL = 16;
119
120         static const int SEGMENT_MASK = CONCURRENCY_LEVEL - 1;
121
122         Segment *segments[CONCURRENCY_LEVEL];
123
124         static const int DEFAULT_INITIAL_CAPACITY = 16;
125
126         // Not gonna consider resize now...
127         
128         HashMap() {
129                 this->size = 0;
130                 this->capacity = DEFAULT_INITIAL_CAPACITY;
131                 this->table = new atomic<Entry*>[capacity];
132                 for (int i = 0; i < capacity; i++) {
133                         atomic_init(&table[i], NULL);
134                 }
135                 for (int i = 0; i < CONCURRENCY_LEVEL; i++) {
136                         segments[i] = new Segment;
137                 }
138         }
139
140         int hashKey(Key *x) {
141                 int h = x->hashCode();
142                 // Use logical right shift
143                 unsigned int tmp = (unsigned int) h;
144                 return ((h << 7) - h + (tmp >> 9) + (tmp >> 17));
145         }
146
147         bool eq(Key *x, Key *y) {
148                 return x == y || x->equals(y);
149         }
150
151         Value* get(Key *key) {
152                 MODEL_ASSERT (key);
153                 int hash = hashKey(key);
154
155                 // Try first without locking...
156                 atomic<Entry*> *tab = table;
157                 int index = hash & (capacity - 1);
158                 atomic<Entry*> *first = &tab[index];
159                 Entry *e;
160                 Value *res = NULL;
161
162                 // Should be a load acquire
163                 // This load action here makes it problematic for the SC analysis, what
164                 // we need to do is as follows: if the get() method ever acquires the
165                 // lock, we ignore this operation for the SC analysis, and otherwise we
166                 // take it into consideration
167                 
168                 SC_BEGIN();
169                 Entry *firstPtr = first->load(acquire);
170                 SC_END();
171
172                 e = firstPtr;
173                 while (e != NULL) {
174                         if (e->hash == hash && eq(key, e->key)) {
175                                 res = e->value.load(seq_cst);
176                                 if (res != NULL)
177                                         return res;
178                                 else
179                                         break;
180                         }
181                         // Loading the next entry, this can be relaxed because the
182                         // synchronization has been established by the load of head
183                         e = e->next.load(relaxed);
184                 }
185         
186                 // Recheck under synch if key apparently not there or interference
187                 Segment *seg = segments[hash & SEGMENT_MASK];
188                 seg->lock(); // Critical region begins
189                 // Not considering resize now, so ignore the reload of table...
190
191                 // Synchronized by locking, no need to be load acquire
192                 Entry *newFirstPtr = first->load(relaxed);
193                 if (e != NULL || firstPtr != newFirstPtr) {
194                         e = newFirstPtr;
195                         while (e != NULL) {
196                                 if (e->hash == hash && eq(key, e->key)) {
197                                         // Protected by lock, no need to be SC
198                                         res = e->value.load(relaxed);
199                                         seg->unlock(); // Critical region ends
200                                         return res;
201                                 }
202                                 // Synchronized by locking
203                                 e = e->next.load(relaxed);
204                         }
205                 }
206                 seg->unlock(); // Critical region ends
207                 return NULL;
208         }
209
210         Value* put(Key *key, Value *value) {
211                 // Don't allow NULL key or value
212                 MODEL_ASSERT (key && value);
213
214                 int hash = hashKey(key);
215                 Segment *seg = segments[hash & SEGMENT_MASK];
216                 atomic<Entry*> *tab;
217
218                 seg->lock(); // Critical region begins
219                 tab = table;
220                 int index = hash & (capacity - 1);
221
222                 atomic<Entry*> *first = &tab[index];
223                 Entry *e;
224                 Value *oldValue = NULL;
225         
226                 // The written of the entry is synchronized by locking
227                 Entry *firstPtr = first->load(relaxed);
228                 e = firstPtr;
229                 while (e != NULL) {
230                         if (e->hash == hash && eq(key, e->key)) {
231                                 // FIXME: This could be a relaxed (because locking synchronize
232                                 // with the previous put())??  no need to be acquire
233                                 oldValue = e->value.load(relaxed);
234                                 e->value.store(value, seq_cst);
235                                 seg->unlock(); // Don't forget to unlock before return
236                                 return oldValue;
237                         }
238                         // Synchronized by locking
239                         e = e->next.load(relaxed);
240                 }
241
242                 // Add to front of list
243                 Entry *newEntry = new Entry(hash, key, value, firstPtr);
244                 // Publish the newEntry to others
245                 first->store(newEntry, release);
246                 seg->unlock(); // Critical region ends
247                 return NULL;
248         }
249
250         Value* remove(Key *key, Value *value) {
251                 MODEL_ASSERT (key);
252                 int hash = hashKey(key);
253                 Segment *seg = segments[hash & SEGMENT_MASK];
254                 atomic<Entry*> *tab;
255
256                 seg->lock(); // Critical region begins
257                 tab = table;
258                 int index = hash & (capacity - 1);
259
260                 atomic<Entry*> *first = &tab[index];
261                 Entry *e;
262                 Value *oldValue = NULL;
263         
264                 // The written of the entry is synchronized by locking
265                 Entry *firstPtr = first->load(relaxed);
266                 e = firstPtr;
267
268                 while (true) {
269                         if (e != NULL) {
270                                 seg->unlock(); // Don't forget to unlock
271                                 return NULL;
272                         }
273                         if (e->hash == hash && eq(key, e->key))
274                                 break;
275                         // Synchronized by locking
276                         e = e->next.load(relaxed);
277                 }
278
279                 // FIXME: This could be a relaxed (because locking synchronize
280                 // with the previous put())?? No need to be acquire
281                 oldValue = e->value.load(relaxed);
282                 // If the value parameter is NULL, we will remove the entry anyway
283                 if (value != NULL && value->equals(oldValue)) {
284                         seg->unlock();
285                         return NULL;
286                 }
287
288                 // Force the get() to grab the lock and retry
289                 e->value.store(NULL, relaxed);
290
291                 // The strategy to remove the entry is to keep the entries after the
292                 // removed one and copy the ones before it
293                 Entry *head = e->next.load(relaxed);
294                 Entry *p;
295                 p = first->load(relaxed);
296                 while (p != e) {
297                         head = new Entry(p->hash, p->key, p->value.load(relaxed), head);
298                         p = p->next.load(relaxed);
299                 }
300
301                 // Publish the new head to readers 
302                 first->store(head, release);
303                 seg->unlock(); // Critical region ends
304                 return oldValue;
305         }
306 };
307
308 #endif