changes
[model-checker-benchmarks.git] / concurrent-hashmap / hashmap_wildcard.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 "sc_annotation.h"
18 #include "wildcard.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, wildcard(1)); // relaxed
89                 this->next.store(n, wildcard(2)); // 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(wildcard(3)); // 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(wildcard(4)); // seq_cst
176                                 if (res != NULL)
177                                         return res;
178                                 else
179                                         break;
180                         }
181                         // Loading the next entry
182                         e = e->next.load(wildcard(5)); // relaxed 
183                 }
184         
185                 // Recheck under synch if key apparently not there or interference
186                 Segment *seg = segments[hash & SEGMENT_MASK];
187                 seg->lock(); // Critical region begins
188                 // Not considering resize now, so ignore the reload of table...
189
190                 // Synchronized by locking, no need to be load acquire
191                 Entry *newFirstPtr = first->load(wildcard(6)); // relaxed
192                 if (e != NULL || firstPtr != newFirstPtr) {
193                         e = newFirstPtr;
194                         while (e != NULL) {
195                                 if (e->hash == hash && eq(key, e->key)) {
196                                         res = e->value.load(wildcard(7)); // relaxed
197                                         seg->unlock(); // Critical region ends
198                                         return res;
199                                 }
200                                 // Synchronized by locking
201                                 e = e->next.load(wildcard(8)); // relaxed
202                         }
203                 }
204                 seg->unlock(); // Critical region ends
205                 return NULL;
206         }
207
208         Value* put(Key *key, Value *value) {
209                 // Don't allow NULL key or value
210                 MODEL_ASSERT (key && value);
211
212                 int hash = hashKey(key);
213                 Segment *seg = segments[hash & SEGMENT_MASK];
214                 atomic<Entry*> *tab;
215
216                 seg->lock(); // Critical region begins
217                 tab = table;
218                 int index = hash & (capacity - 1);
219
220                 atomic<Entry*> *first = &tab[index];
221                 Entry *e;
222                 Value *oldValue = NULL;
223         
224                 // The written of the entry is synchronized by locking
225                 Entry *firstPtr = first->load(wildcard(9)); // relaxed
226                 e = firstPtr;
227                 while (e != NULL) {
228                         if (e->hash == hash && eq(key, e->key)) {
229                                 // FIXME: This could be a relaxed (because locking synchronize
230                                 // with the previous put())?? 
231                                 oldValue = e->value.load(wildcard(10)); // relaxed 
232                                 e->value.store(value, wildcard(11)); // seq_cst
233                                 seg->unlock(); // Don't forget to unlock before return
234                                 return oldValue;
235                         }
236                         // Synchronized by locking
237                         e = e->next.load(wildcard(12)); // relaxed
238                 }
239
240                 // Add to front of list
241                 Entry *newEntry = new Entry(hash, key, value, firstPtr);
242                 // Publish the newEntry to others
243                 first->store(newEntry, wildcard(13)); // release
244                 seg->unlock(); // Critical region ends
245                 return NULL;
246         }
247
248         Value* remove(Key *key, Value *value) {
249                 MODEL_ASSERT (key);
250                 int hash = hashKey(key);
251                 Segment *seg = segments[hash & SEGMENT_MASK];
252                 atomic<Entry*> *tab;
253
254                 seg->lock(); // Critical region begins
255                 tab = table;
256                 int index = hash & (capacity - 1);
257
258                 atomic<Entry*> *first = &tab[index];
259                 Entry *e;
260                 Value *oldValue = NULL;
261         
262                 // The written of the entry is synchronized by locking
263                 Entry *firstPtr = first->load(relaxed);
264                 e = firstPtr;
265
266                 while (true) {
267                         if (e != NULL) {
268                                 seg->unlock(); // Don't forget to unlock
269                                 return NULL;
270                         }
271                         if (e->hash == hash && eq(key, e->key))
272                                 break;
273                         // Synchronized by locking
274                         e = e->next.load(relaxed);
275                 }
276
277                 // FIXME: This could be a relaxed (because locking synchronize
278                 // with the previous put())?? 
279                 oldValue = e->value.load(relaxed);
280                 // If the value parameter is NULL, we will remove the entry anyway
281                 if (value != NULL && value->equals(oldValue)) {
282                         seg->unlock();
283                         return NULL;
284                 }
285
286                 // Force the get() to grab the lock and retry
287                 e->value.store(NULL, relaxed);
288
289                 // The strategy to remove the entry is to keep the entries after the
290                 // removed one and copy the ones before it
291                 Entry *head = e->next.load(relaxed);
292                 Entry *p;
293                 p = first->load(relaxed);
294                 while (p != e) {
295                         head = new Entry(p->hash, p->key, p->value.load(relaxed), head);
296                         p = p->next.load(relaxed);
297                 }
298
299                 // Publish the new head to readers 
300                 first->store(head, release);
301                 seg->unlock(); // Critical region ends
302                 return oldValue;
303         }
304 };
305
306 #endif