1 /*
2 * Copyright 2012-2015 Samy Al Bahra.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <ck_cc.h>
28 #include <ck_hs.h>
29 #include <ck_limits.h>
30 #include <ck_md.h>
31 #include <ck_pr.h>
32 #include <ck_stdint.h>
33 #include <ck_stdbool.h>
34 #include <ck_string.h>
35
36 #include "ck_internal.h"
37
38 #ifndef CK_HS_PROBE_L1_SHIFT
39 #define CK_HS_PROBE_L1_SHIFT 3ULL
40 #endif /* CK_HS_PROBE_L1_SHIFT */
41
42 #define CK_HS_PROBE_L1 (1 << CK_HS_PROBE_L1_SHIFT)
43 #define CK_HS_PROBE_L1_MASK (CK_HS_PROBE_L1 - 1)
44
45 #ifndef CK_HS_PROBE_L1_DEFAULT
46 #define CK_HS_PROBE_L1_DEFAULT CK_MD_CACHELINE
47 #endif
48
49 #define CK_HS_VMA_MASK ((uintptr_t)((1ULL << CK_MD_VMA_BITS) - 1))
50 #define CK_HS_VMA(x) \
51 ((void *)((uintptr_t)(x) & CK_HS_VMA_MASK))
52
53 #define CK_HS_EMPTY NULL
54 #define CK_HS_TOMBSTONE ((void *)~(uintptr_t)0)
55 #define CK_HS_G (2)
56 #define CK_HS_G_MASK (CK_HS_G - 1)
57
58 #if defined(CK_F_PR_LOAD_8) && defined(CK_F_PR_STORE_8)
59 #define CK_HS_WORD uint8_t
60 #define CK_HS_WORD_MAX UINT8_MAX
61 #define CK_HS_STORE(x, y) ck_pr_store_8(x, y)
62 #define CK_HS_LOAD(x) ck_pr_load_8(x)
63 #elif defined(CK_F_PR_LOAD_16) && defined(CK_F_PR_STORE_16)
64 #define CK_HS_WORD uint16_t
65 #define CK_HS_WORD_MAX UINT16_MAX
66 #define CK_HS_STORE(x, y) ck_pr_store_16(x, y)
67 #define CK_HS_LOAD(x) ck_pr_load_16(x)
68 #elif defined(CK_F_PR_LOAD_32) && defined(CK_F_PR_STORE_32)
69 #define CK_HS_WORD uint32_t
70 #define CK_HS_WORD_MAX UINT32_MAX
71 #define CK_HS_STORE(x, y) ck_pr_store_32(x, y)
72 #define CK_HS_LOAD(x) ck_pr_load_32(x)
73 #else
74 #error "ck_hs is not supported on your platform."
75 #endif
76
77 enum ck_hs_probe_behavior {
78 CK_HS_PROBE = 0, /* Default behavior. */
79 CK_HS_PROBE_TOMBSTONE, /* Short-circuit on tombstone. */
80 CK_HS_PROBE_INSERT /* Short-circuit on probe bound if tombstone found. */
81 };
82
83 struct ck_hs_map {
84 unsigned int generation[CK_HS_G];
85 unsigned int probe_maximum;
86 unsigned long mask;
87 unsigned long step;
88 unsigned int probe_limit;
89 unsigned int tombstones;
90 unsigned long n_entries;
91 unsigned long capacity;
92 unsigned long size;
93 CK_HS_WORD *probe_bound;
94 const void **entries;
95 };
96
97 static inline void
ck_hs_map_signal(struct ck_hs_map * map,unsigned long h)98 ck_hs_map_signal(struct ck_hs_map *map, unsigned long h)
99 {
100
101 h &= CK_HS_G_MASK;
102 ck_pr_store_uint(&map->generation[h],
103 map->generation[h] + 1);
104 ck_pr_fence_store();
105 return;
106 }
107
108 static bool
_ck_hs_next(struct ck_hs * hs,struct ck_hs_map * map,struct ck_hs_iterator * i,void ** key)109 _ck_hs_next(struct ck_hs *hs, struct ck_hs_map *map, struct ck_hs_iterator *i, void **key)
110 {
111 void *value;
112 if (i->offset >= map->capacity)
113 return false;
114
115 do {
116 value = CK_CC_DECONST_PTR(map->entries[i->offset]);
117 if (value != CK_HS_EMPTY && value != CK_HS_TOMBSTONE) {
118 #ifdef CK_HS_PP
119 if (hs->mode & CK_HS_MODE_OBJECT)
120 value = CK_HS_VMA(value);
121 #else
122 (void)hs; /* Avoid unused parameter warning. */
123 #endif
124 i->offset++;
125 *key = value;
126 return true;
127 }
128 } while (++i->offset < map->capacity);
129
130 return false;
131 }
132
133 void
ck_hs_iterator_init(struct ck_hs_iterator * iterator)134 ck_hs_iterator_init(struct ck_hs_iterator *iterator)
135 {
136
137 iterator->cursor = NULL;
138 iterator->offset = 0;
139 iterator->map = NULL;
140 return;
141 }
142
143 bool
ck_hs_next(struct ck_hs * hs,struct ck_hs_iterator * i,void ** key)144 ck_hs_next(struct ck_hs *hs, struct ck_hs_iterator *i, void **key)
145 {
146 return _ck_hs_next(hs, hs->map, i, key);
147 }
148
149 bool
ck_hs_next_spmc(struct ck_hs * hs,struct ck_hs_iterator * i,void ** key)150 ck_hs_next_spmc(struct ck_hs *hs, struct ck_hs_iterator *i, void **key)
151 {
152 struct ck_hs_map *m = i->map;
153 if (m == NULL) {
154 m = i->map = ck_pr_load_ptr(&hs->map);
155 }
156 return _ck_hs_next(hs, m, i, key);
157 }
158
159 void
ck_hs_stat(struct ck_hs * hs,struct ck_hs_stat * st)160 ck_hs_stat(struct ck_hs *hs, struct ck_hs_stat *st)
161 {
162 struct ck_hs_map *map = hs->map;
163
164 st->n_entries = map->n_entries;
165 st->tombstones = map->tombstones;
166 st->probe_maximum = map->probe_maximum;
167 return;
168 }
169
170 unsigned long
ck_hs_count(struct ck_hs * hs)171 ck_hs_count(struct ck_hs *hs)
172 {
173
174 return hs->map->n_entries;
175 }
176
177 static void
ck_hs_map_destroy(struct ck_malloc * m,struct ck_hs_map * map,bool defer)178 ck_hs_map_destroy(struct ck_malloc *m, struct ck_hs_map *map, bool defer)
179 {
180
181 m->free(map, map->size, defer);
182 return;
183 }
184
185 void
ck_hs_destroy(struct ck_hs * hs)186 ck_hs_destroy(struct ck_hs *hs)
187 {
188
189 ck_hs_map_destroy(hs->m, hs->map, false);
190 return;
191 }
192
193 static struct ck_hs_map *
ck_hs_map_create(struct ck_hs * hs,unsigned long entries)194 ck_hs_map_create(struct ck_hs *hs, unsigned long entries)
195 {
196 struct ck_hs_map *map;
197 unsigned long size, n_entries, prefix, limit;
198
199 n_entries = ck_internal_power_2(entries);
200 if (n_entries < CK_HS_PROBE_L1)
201 n_entries = CK_HS_PROBE_L1;
202
203 size = sizeof(struct ck_hs_map) + (sizeof(void *) * n_entries + CK_MD_CACHELINE - 1);
204
205 if (hs->mode & CK_HS_MODE_DELETE) {
206 prefix = sizeof(CK_HS_WORD) * n_entries;
207 size += prefix;
208 } else {
209 prefix = 0;
210 }
211
212 map = hs->m->malloc(size);
213 if (map == NULL)
214 return NULL;
215
216 map->size = size;
217
218 /* We should probably use a more intelligent heuristic for default probe length. */
219 limit = ck_internal_max(n_entries >> (CK_HS_PROBE_L1_SHIFT + 2), CK_HS_PROBE_L1_DEFAULT);
220 if (limit > UINT_MAX)
221 limit = UINT_MAX;
222
223 map->probe_limit = (unsigned int)limit;
224 map->probe_maximum = 0;
225 map->capacity = n_entries;
226 map->step = ck_cc_ffsl(n_entries);
227 map->mask = n_entries - 1;
228 map->n_entries = 0;
229
230 /* Align map allocation to cache line. */
231 map->entries = (void *)(((uintptr_t)&map[1] + prefix +
232 CK_MD_CACHELINE - 1) & ~(CK_MD_CACHELINE - 1));
233
234 memset(map->entries, 0, sizeof(void *) * n_entries);
235 memset(map->generation, 0, sizeof map->generation);
236
237 if (hs->mode & CK_HS_MODE_DELETE) {
238 map->probe_bound = (CK_HS_WORD *)&map[1];
239 memset(map->probe_bound, 0, prefix);
240 } else {
241 map->probe_bound = NULL;
242 }
243
244 /* Commit entries purge with respect to map publication. */
245 ck_pr_fence_store();
246 return map;
247 }
248
249 bool
ck_hs_reset_size(struct ck_hs * hs,unsigned long capacity)250 ck_hs_reset_size(struct ck_hs *hs, unsigned long capacity)
251 {
252 struct ck_hs_map *map, *previous;
253
254 previous = hs->map;
255 map = ck_hs_map_create(hs, capacity);
256 if (map == NULL)
257 return false;
258
259 ck_pr_store_ptr(&hs->map, map);
260 ck_hs_map_destroy(hs->m, previous, true);
261 return true;
262 }
263
264 bool
ck_hs_reset(struct ck_hs * hs)265 ck_hs_reset(struct ck_hs *hs)
266 {
267 struct ck_hs_map *previous;
268
269 previous = hs->map;
270 return ck_hs_reset_size(hs, previous->capacity);
271 }
272
273 static inline unsigned long
ck_hs_map_probe_next(struct ck_hs_map * map,unsigned long offset,unsigned long h,unsigned long level,unsigned long probes)274 ck_hs_map_probe_next(struct ck_hs_map *map,
275 unsigned long offset,
276 unsigned long h,
277 unsigned long level,
278 unsigned long probes)
279 {
280 unsigned long r, stride;
281
282 r = (h >> map->step) >> level;
283 stride = (r & ~CK_HS_PROBE_L1_MASK) << 1 | (r & CK_HS_PROBE_L1_MASK);
284
285 return (offset + (probes >> CK_HS_PROBE_L1_SHIFT) +
286 (stride | CK_HS_PROBE_L1)) & map->mask;
287 }
288
289 static inline void
ck_hs_map_bound_set(struct ck_hs_map * m,unsigned long h,unsigned long n_probes)290 ck_hs_map_bound_set(struct ck_hs_map *m,
291 unsigned long h,
292 unsigned long n_probes)
293 {
294 unsigned long offset = h & m->mask;
295
296 if (n_probes > m->probe_maximum)
297 ck_pr_store_uint(&m->probe_maximum, n_probes);
298
299 if (m->probe_bound != NULL && m->probe_bound[offset] < n_probes) {
300 if (n_probes > CK_HS_WORD_MAX)
301 n_probes = CK_HS_WORD_MAX;
302
303 CK_HS_STORE(&m->probe_bound[offset], n_probes);
304 ck_pr_fence_store();
305 }
306
307 return;
308 }
309
310 static inline unsigned int
ck_hs_map_bound_get(struct ck_hs_map * m,unsigned long h)311 ck_hs_map_bound_get(struct ck_hs_map *m, unsigned long h)
312 {
313 unsigned long offset = h & m->mask;
314 unsigned int r = CK_HS_WORD_MAX;
315
316 if (m->probe_bound != NULL) {
317 r = CK_HS_LOAD(&m->probe_bound[offset]);
318 if (r == CK_HS_WORD_MAX)
319 r = ck_pr_load_uint(&m->probe_maximum);
320 } else {
321 r = ck_pr_load_uint(&m->probe_maximum);
322 }
323
324 return r;
325 }
326
327 bool
ck_hs_grow(struct ck_hs * hs,unsigned long capacity)328 ck_hs_grow(struct ck_hs *hs,
329 unsigned long capacity)
330 {
331 struct ck_hs_map *map, *update;
332 unsigned long k, i, j, offset, probes;
333 const void *previous, **bucket;
334
335 restart:
336 map = hs->map;
337 if (map->capacity > capacity)
338 return false;
339
340 update = ck_hs_map_create(hs, capacity);
341 if (update == NULL)
342 return false;
343
344 for (k = 0; k < map->capacity; k++) {
345 unsigned long h;
346
347 previous = map->entries[k];
348 if (previous == CK_HS_EMPTY || previous == CK_HS_TOMBSTONE)
349 continue;
350
351 #ifdef CK_HS_PP
352 if (hs->mode & CK_HS_MODE_OBJECT)
353 previous = CK_HS_VMA(previous);
354 #endif
355
356 h = hs->hf(previous, hs->seed);
357 offset = h & update->mask;
358 i = probes = 0;
359
360 for (;;) {
361 bucket = (const void **)((uintptr_t)&update->entries[offset] & ~(CK_MD_CACHELINE - 1));
362
363 for (j = 0; j < CK_HS_PROBE_L1; j++) {
364 const void **cursor = bucket + ((j + offset) & (CK_HS_PROBE_L1 - 1));
365
366 if (probes++ == update->probe_limit)
367 break;
368
369 if (CK_CC_LIKELY(*cursor == CK_HS_EMPTY)) {
370 *cursor = map->entries[k];
371 update->n_entries++;
372
373 ck_hs_map_bound_set(update, h, probes);
374 break;
375 }
376 }
377
378 if (j < CK_HS_PROBE_L1)
379 break;
380
381 offset = ck_hs_map_probe_next(update, offset, h, i++, probes);
382 }
383
384 if (probes > update->probe_limit) {
385 /*
386 * We have hit the probe limit, map needs to be even larger.
387 */
388 ck_hs_map_destroy(hs->m, update, false);
389 capacity <<= 1;
390 goto restart;
391 }
392 }
393
394 ck_pr_fence_store();
395 ck_pr_store_ptr(&hs->map, update);
396 ck_hs_map_destroy(hs->m, map, true);
397 return true;
398 }
399
400 static void
ck_hs_map_postinsert(struct ck_hs * hs,struct ck_hs_map * map)401 ck_hs_map_postinsert(struct ck_hs *hs, struct ck_hs_map *map)
402 {
403
404 map->n_entries++;
405 if ((map->n_entries << 1) > map->capacity)
406 ck_hs_grow(hs, map->capacity << 1);
407
408 return;
409 }
410
411 bool
ck_hs_rebuild(struct ck_hs * hs)412 ck_hs_rebuild(struct ck_hs *hs)
413 {
414
415 return ck_hs_grow(hs, hs->map->capacity);
416 }
417
418 static const void **
ck_hs_map_probe(struct ck_hs * hs,struct ck_hs_map * map,unsigned long * n_probes,const void *** priority,unsigned long h,const void * key,const void ** object,unsigned long probe_limit,enum ck_hs_probe_behavior behavior)419 ck_hs_map_probe(struct ck_hs *hs,
420 struct ck_hs_map *map,
421 unsigned long *n_probes,
422 const void ***priority,
423 unsigned long h,
424 const void *key,
425 const void **object,
426 unsigned long probe_limit,
427 enum ck_hs_probe_behavior behavior)
428 {
429 const void **bucket, **cursor, *k, *compare;
430 const void **pr = NULL;
431 unsigned long offset, j, i, probes, opl;
432
433 #ifdef CK_HS_PP
434 /* If we are storing object pointers, then we may leverage pointer packing. */
435 unsigned long hv = 0;
436
437 if (hs->mode & CK_HS_MODE_OBJECT) {
438 hv = (h >> 25) & CK_HS_KEY_MASK;
439 compare = CK_HS_VMA(key);
440 } else {
441 compare = key;
442 }
443 #else
444 compare = key;
445 #endif
446
447 offset = h & map->mask;
448 *object = NULL;
449 i = probes = 0;
450
451 opl = probe_limit;
452 if (behavior == CK_HS_PROBE_INSERT)
453 probe_limit = ck_hs_map_bound_get(map, h);
454
455 for (;;) {
456 bucket = (const void **)((uintptr_t)&map->entries[offset] & ~(CK_MD_CACHELINE - 1));
457
458 for (j = 0; j < CK_HS_PROBE_L1; j++) {
459 cursor = bucket + ((j + offset) & (CK_HS_PROBE_L1 - 1));
460
461 if (probes++ == probe_limit) {
462 if (probe_limit == opl || pr != NULL) {
463 k = CK_HS_EMPTY;
464 goto leave;
465 }
466
467 /*
468 * If no eligible slot has been found yet, continue probe
469 * sequence with original probe limit.
470 */
471 probe_limit = opl;
472 }
473
474 k = ck_pr_load_ptr(cursor);
475 if (k == CK_HS_EMPTY)
476 goto leave;
477
478 if (k == CK_HS_TOMBSTONE) {
479 if (pr == NULL) {
480 pr = cursor;
481 *n_probes = probes;
482
483 if (behavior == CK_HS_PROBE_TOMBSTONE) {
484 k = CK_HS_EMPTY;
485 goto leave;
486 }
487 }
488
489 continue;
490 }
491
492 #ifdef CK_HS_PP
493 if (hs->mode & CK_HS_MODE_OBJECT) {
494 if (((uintptr_t)k >> CK_MD_VMA_BITS) != hv)
495 continue;
496
497 k = CK_HS_VMA(k);
498 }
499 #endif
500
501 if (k == compare)
502 goto leave;
503
504 if (hs->compare == NULL)
505 continue;
506
507 if (hs->compare(k, key) == true)
508 goto leave;
509 }
510
511 offset = ck_hs_map_probe_next(map, offset, h, i++, probes);
512 }
513
514 leave:
515 if (probes > probe_limit) {
516 cursor = NULL;
517 } else {
518 *object = k;
519 }
520
521 if (pr == NULL)
522 *n_probes = probes;
523
524 *priority = pr;
525 return cursor;
526 }
527
528 static inline const void *
ck_hs_marshal(unsigned int mode,const void * key,unsigned long h)529 ck_hs_marshal(unsigned int mode, const void *key, unsigned long h)
530 {
531 #ifdef CK_HS_PP
532 const void *insert;
533
534 if (mode & CK_HS_MODE_OBJECT) {
535 insert = (void *)((uintptr_t)CK_HS_VMA(key) |
536 ((h >> 25) << CK_MD_VMA_BITS));
537 } else {
538 insert = key;
539 }
540
541 return insert;
542 #else
543 (void)mode;
544 (void)h;
545
546 return key;
547 #endif
548 }
549
550 bool
ck_hs_gc(struct ck_hs * hs,unsigned long cycles,unsigned long seed)551 ck_hs_gc(struct ck_hs *hs, unsigned long cycles, unsigned long seed)
552 {
553 unsigned long size = 0;
554 unsigned long i;
555 struct ck_hs_map *map = hs->map;
556 unsigned int maximum;
557 CK_HS_WORD *bounds = NULL;
558
559 if (map->n_entries == 0) {
560 ck_pr_store_uint(&map->probe_maximum, 0);
561 if (map->probe_bound != NULL)
562 memset(map->probe_bound, 0, sizeof(CK_HS_WORD) * map->capacity);
563
564 return true;
565 }
566
567 if (cycles == 0) {
568 maximum = 0;
569
570 if (map->probe_bound != NULL) {
571 size = sizeof(CK_HS_WORD) * map->capacity;
572 bounds = hs->m->malloc(size);
573 if (bounds == NULL)
574 return false;
575
576 memset(bounds, 0, size);
577 }
578 } else {
579 maximum = map->probe_maximum;
580 }
581
582 for (i = 0; i < map->capacity; i++) {
583 const void **first, *object, **slot, *entry;
584 unsigned long n_probes, offset, h;
585
586 entry = map->entries[(i + seed) & map->mask];
587 if (entry == CK_HS_EMPTY || entry == CK_HS_TOMBSTONE)
588 continue;
589
590 #ifdef CK_HS_PP
591 if (hs->mode & CK_HS_MODE_OBJECT)
592 entry = CK_HS_VMA(entry);
593 #endif
594
595 h = hs->hf(entry, hs->seed);
596 offset = h & map->mask;
597
598 slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, entry, &object,
599 ck_hs_map_bound_get(map, h), CK_HS_PROBE);
600
601 if (first != NULL) {
602 const void *insert = ck_hs_marshal(hs->mode, entry, h);
603
604 ck_pr_store_ptr(first, insert);
605 ck_hs_map_signal(map, h);
606 ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
607 }
608
609 if (cycles == 0) {
610 if (n_probes > maximum)
611 maximum = n_probes;
612
613 if (n_probes > CK_HS_WORD_MAX)
614 n_probes = CK_HS_WORD_MAX;
615
616 if (bounds != NULL && n_probes > bounds[offset])
617 bounds[offset] = n_probes;
618 } else if (--cycles == 0)
619 break;
620 }
621
622 /*
623 * The following only apply to garbage collection involving
624 * a full scan of all entries.
625 */
626 if (maximum != map->probe_maximum)
627 ck_pr_store_uint(&map->probe_maximum, maximum);
628
629 if (bounds != NULL) {
630 for (i = 0; i < map->capacity; i++)
631 CK_HS_STORE(&map->probe_bound[i], bounds[i]);
632
633 hs->m->free(bounds, size, false);
634 }
635
636 return true;
637 }
638
639 bool
ck_hs_fas(struct ck_hs * hs,unsigned long h,const void * key,void ** previous)640 ck_hs_fas(struct ck_hs *hs,
641 unsigned long h,
642 const void *key,
643 void **previous)
644 {
645 const void **slot, **first, *object, *insert;
646 struct ck_hs_map *map = hs->map;
647 unsigned long n_probes;
648
649 *previous = NULL;
650 slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object,
651 ck_hs_map_bound_get(map, h), CK_HS_PROBE);
652
653 /* Replacement semantics presume existence. */
654 if (object == NULL)
655 return false;
656
657 insert = ck_hs_marshal(hs->mode, key, h);
658
659 if (first != NULL) {
660 ck_pr_store_ptr(first, insert);
661 ck_hs_map_signal(map, h);
662 ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
663 } else {
664 ck_pr_store_ptr(slot, insert);
665 }
666
667 *previous = CK_CC_DECONST_PTR(object);
668 return true;
669 }
670
671 /*
672 * An apply function takes two arguments. The first argument is a pointer to a
673 * pre-existing object. The second argument is a pointer to the fifth argument
674 * passed to ck_hs_apply. If a non-NULL pointer is passed to the first argument
675 * and the return value of the apply function is NULL, then the pre-existing
676 * value is deleted. If the return pointer is the same as the one passed to the
677 * apply function then no changes are made to the hash table. If the first
678 * argument is non-NULL and the return pointer is different than that passed to
679 * the apply function, then the pre-existing value is replaced. For
680 * replacement, it is required that the value itself is identical to the
681 * previous value.
682 */
683 bool
ck_hs_apply(struct ck_hs * hs,unsigned long h,const void * key,ck_hs_apply_fn_t * fn,void * cl)684 ck_hs_apply(struct ck_hs *hs,
685 unsigned long h,
686 const void *key,
687 ck_hs_apply_fn_t *fn,
688 void *cl)
689 {
690 const void **slot, **first, *object, *delta, *insert;
691 unsigned long n_probes;
692 struct ck_hs_map *map;
693
694 restart:
695 map = hs->map;
696
697 slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object, map->probe_limit, CK_HS_PROBE_INSERT);
698 if (slot == NULL && first == NULL) {
699 if (ck_hs_grow(hs, map->capacity << 1) == false)
700 return false;
701
702 goto restart;
703 }
704
705 delta = fn(CK_CC_DECONST_PTR(object), cl);
706 if (delta == NULL) {
707 /*
708 * The apply function has requested deletion. If the object doesn't exist,
709 * then exit early.
710 */
711 if (CK_CC_UNLIKELY(object == NULL))
712 return true;
713
714 /* Otherwise, mark slot as deleted. */
715 ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
716 map->n_entries--;
717 map->tombstones++;
718 return true;
719 }
720
721 /* The apply function has not requested hash set modification so exit early. */
722 if (delta == object)
723 return true;
724
725 /* A modification or insertion has been requested. */
726 ck_hs_map_bound_set(map, h, n_probes);
727
728 insert = ck_hs_marshal(hs->mode, delta, h);
729 if (first != NULL) {
730 /*
731 * This follows the same semantics as ck_hs_set, please refer to that
732 * function for documentation.
733 */
734 ck_pr_store_ptr(first, insert);
735
736 if (object != NULL) {
737 ck_hs_map_signal(map, h);
738 ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
739 }
740 } else {
741 /*
742 * If we are storing into same slot, then atomic store is sufficient
743 * for replacement.
744 */
745 ck_pr_store_ptr(slot, insert);
746 }
747
748 if (object == NULL)
749 ck_hs_map_postinsert(hs, map);
750
751 return true;
752 }
753
754 bool
ck_hs_set(struct ck_hs * hs,unsigned long h,const void * key,void ** previous)755 ck_hs_set(struct ck_hs *hs,
756 unsigned long h,
757 const void *key,
758 void **previous)
759 {
760 const void **slot, **first, *object, *insert;
761 unsigned long n_probes;
762 struct ck_hs_map *map;
763
764 *previous = NULL;
765
766 restart:
767 map = hs->map;
768
769 slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object, map->probe_limit, CK_HS_PROBE_INSERT);
770 if (slot == NULL && first == NULL) {
771 if (ck_hs_grow(hs, map->capacity << 1) == false)
772 return false;
773
774 goto restart;
775 }
776
777 ck_hs_map_bound_set(map, h, n_probes);
778 insert = ck_hs_marshal(hs->mode, key, h);
779
780 if (first != NULL) {
781 /* If an earlier bucket was found, then store entry there. */
782 ck_pr_store_ptr(first, insert);
783
784 /*
785 * If a duplicate key was found, then delete it after
786 * signaling concurrent probes to restart. Optionally,
787 * it is possible to install tombstone after grace
788 * period if we can guarantee earlier position of
789 * duplicate key.
790 */
791 if (object != NULL) {
792 ck_hs_map_signal(map, h);
793 ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
794 }
795 } else {
796 /*
797 * If we are storing into same slot, then atomic store is sufficient
798 * for replacement.
799 */
800 ck_pr_store_ptr(slot, insert);
801 }
802
803 if (object == NULL)
804 ck_hs_map_postinsert(hs, map);
805
806 *previous = CK_CC_DECONST_PTR(object);
807 return true;
808 }
809
810 CK_CC_INLINE static bool
ck_hs_put_internal(struct ck_hs * hs,unsigned long h,const void * key,enum ck_hs_probe_behavior behavior)811 ck_hs_put_internal(struct ck_hs *hs,
812 unsigned long h,
813 const void *key,
814 enum ck_hs_probe_behavior behavior)
815 {
816 const void **slot, **first, *object, *insert;
817 unsigned long n_probes;
818 struct ck_hs_map *map;
819
820 restart:
821 map = hs->map;
822
823 slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object,
824 map->probe_limit, behavior);
825
826 if (slot == NULL && first == NULL) {
827 if (ck_hs_grow(hs, map->capacity << 1) == false)
828 return false;
829
830 goto restart;
831 }
832
833 /* Fail operation if a match was found. */
834 if (object != NULL)
835 return false;
836
837 ck_hs_map_bound_set(map, h, n_probes);
838 insert = ck_hs_marshal(hs->mode, key, h);
839
840 if (first != NULL) {
841 /* Insert key into first bucket in probe sequence. */
842 ck_pr_store_ptr(first, insert);
843 } else {
844 /* An empty slot was found. */
845 ck_pr_store_ptr(slot, insert);
846 }
847
848 ck_hs_map_postinsert(hs, map);
849 return true;
850 }
851
852 bool
ck_hs_put(struct ck_hs * hs,unsigned long h,const void * key)853 ck_hs_put(struct ck_hs *hs,
854 unsigned long h,
855 const void *key)
856 {
857
858 return ck_hs_put_internal(hs, h, key, CK_HS_PROBE_INSERT);
859 }
860
861 bool
ck_hs_put_unique(struct ck_hs * hs,unsigned long h,const void * key)862 ck_hs_put_unique(struct ck_hs *hs,
863 unsigned long h,
864 const void *key)
865 {
866
867 return ck_hs_put_internal(hs, h, key, CK_HS_PROBE_TOMBSTONE);
868 }
869
870 void *
ck_hs_get(struct ck_hs * hs,unsigned long h,const void * key)871 ck_hs_get(struct ck_hs *hs,
872 unsigned long h,
873 const void *key)
874 {
875 const void **first, *object;
876 struct ck_hs_map *map;
877 unsigned long n_probes;
878 unsigned int g, g_p, probe;
879 unsigned int *generation;
880
881 do {
882 map = ck_pr_load_ptr(&hs->map);
883 generation = &map->generation[h & CK_HS_G_MASK];
884 g = ck_pr_load_uint(generation);
885 probe = ck_hs_map_bound_get(map, h);
886 ck_pr_fence_load();
887
888 ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object, probe, CK_HS_PROBE);
889
890 ck_pr_fence_load();
891 g_p = ck_pr_load_uint(generation);
892 } while (g != g_p);
893
894 return CK_CC_DECONST_PTR(object);
895 }
896
897 void *
ck_hs_remove(struct ck_hs * hs,unsigned long h,const void * key)898 ck_hs_remove(struct ck_hs *hs,
899 unsigned long h,
900 const void *key)
901 {
902 const void **slot, **first, *object;
903 struct ck_hs_map *map = hs->map;
904 unsigned long n_probes;
905
906 slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object,
907 ck_hs_map_bound_get(map, h), CK_HS_PROBE);
908 if (object == NULL)
909 return NULL;
910
911 ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
912 map->n_entries--;
913 map->tombstones++;
914 return CK_CC_DECONST_PTR(object);
915 }
916
917 bool
ck_hs_move(struct ck_hs * hs,struct ck_hs * source,ck_hs_hash_cb_t * hf,ck_hs_compare_cb_t * compare,struct ck_malloc * m)918 ck_hs_move(struct ck_hs *hs,
919 struct ck_hs *source,
920 ck_hs_hash_cb_t *hf,
921 ck_hs_compare_cb_t *compare,
922 struct ck_malloc *m)
923 {
924
925 if (m == NULL || m->malloc == NULL || m->free == NULL || hf == NULL)
926 return false;
927
928 hs->mode = source->mode;
929 hs->seed = source->seed;
930 hs->map = source->map;
931 hs->m = m;
932 hs->hf = hf;
933 hs->compare = compare;
934 return true;
935 }
936
937 bool
ck_hs_init(struct ck_hs * hs,unsigned int mode,ck_hs_hash_cb_t * hf,ck_hs_compare_cb_t * compare,struct ck_malloc * m,unsigned long n_entries,unsigned long seed)938 ck_hs_init(struct ck_hs *hs,
939 unsigned int mode,
940 ck_hs_hash_cb_t *hf,
941 ck_hs_compare_cb_t *compare,
942 struct ck_malloc *m,
943 unsigned long n_entries,
944 unsigned long seed)
945 {
946
947 if (m == NULL || m->malloc == NULL || m->free == NULL || hf == NULL)
948 return false;
949
950 hs->m = m;
951 hs->mode = mode;
952 hs->seed = seed;
953 hs->hf = hf;
954 hs->compare = compare;
955
956 hs->map = ck_hs_map_create(hs, n_entries);
957 return hs->map != NULL;
958 }
959