xref: /freebsd-src/sys/contrib/ck/src/ck_hs.c (revision 74e9b5f29ad0056bbe11a30c91dfa0705fa19cd5)
11fb62fb0SOlivier Houchard /*
21fb62fb0SOlivier Houchard  * Copyright 2012-2015 Samy Al Bahra.
31fb62fb0SOlivier Houchard  * All rights reserved.
41fb62fb0SOlivier Houchard  *
51fb62fb0SOlivier Houchard  * Redistribution and use in source and binary forms, with or without
61fb62fb0SOlivier Houchard  * modification, are permitted provided that the following conditions
71fb62fb0SOlivier Houchard  * are met:
81fb62fb0SOlivier Houchard  * 1. Redistributions of source code must retain the above copyright
91fb62fb0SOlivier Houchard  *    notice, this list of conditions and the following disclaimer.
101fb62fb0SOlivier Houchard  * 2. Redistributions in binary form must reproduce the above copyright
111fb62fb0SOlivier Houchard  *    notice, this list of conditions and the following disclaimer in the
121fb62fb0SOlivier Houchard  *    documentation and/or other materials provided with the distribution.
131fb62fb0SOlivier Houchard  *
141fb62fb0SOlivier Houchard  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151fb62fb0SOlivier Houchard  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161fb62fb0SOlivier Houchard  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171fb62fb0SOlivier Houchard  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181fb62fb0SOlivier Houchard  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191fb62fb0SOlivier Houchard  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201fb62fb0SOlivier Houchard  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211fb62fb0SOlivier Houchard  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221fb62fb0SOlivier Houchard  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231fb62fb0SOlivier Houchard  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241fb62fb0SOlivier Houchard  * SUCH DAMAGE.
251fb62fb0SOlivier Houchard  */
261fb62fb0SOlivier Houchard 
271fb62fb0SOlivier Houchard #include <ck_cc.h>
281fb62fb0SOlivier Houchard #include <ck_hs.h>
291fb62fb0SOlivier Houchard #include <ck_limits.h>
301fb62fb0SOlivier Houchard #include <ck_md.h>
311fb62fb0SOlivier Houchard #include <ck_pr.h>
321fb62fb0SOlivier Houchard #include <ck_stdint.h>
331fb62fb0SOlivier Houchard #include <ck_stdbool.h>
341fb62fb0SOlivier Houchard #include <ck_string.h>
351fb62fb0SOlivier Houchard 
361fb62fb0SOlivier Houchard #include "ck_internal.h"
371fb62fb0SOlivier Houchard 
381fb62fb0SOlivier Houchard #ifndef CK_HS_PROBE_L1_SHIFT
391fb62fb0SOlivier Houchard #define CK_HS_PROBE_L1_SHIFT 3ULL
401fb62fb0SOlivier Houchard #endif /* CK_HS_PROBE_L1_SHIFT */
411fb62fb0SOlivier Houchard 
421fb62fb0SOlivier Houchard #define CK_HS_PROBE_L1 (1 << CK_HS_PROBE_L1_SHIFT)
431fb62fb0SOlivier Houchard #define CK_HS_PROBE_L1_MASK (CK_HS_PROBE_L1 - 1)
441fb62fb0SOlivier Houchard 
451fb62fb0SOlivier Houchard #ifndef CK_HS_PROBE_L1_DEFAULT
461fb62fb0SOlivier Houchard #define CK_HS_PROBE_L1_DEFAULT CK_MD_CACHELINE
471fb62fb0SOlivier Houchard #endif
481fb62fb0SOlivier Houchard 
491fb62fb0SOlivier Houchard #define CK_HS_VMA_MASK ((uintptr_t)((1ULL << CK_MD_VMA_BITS) - 1))
501fb62fb0SOlivier Houchard #define CK_HS_VMA(x)	\
511fb62fb0SOlivier Houchard 	((void *)((uintptr_t)(x) & CK_HS_VMA_MASK))
521fb62fb0SOlivier Houchard 
531fb62fb0SOlivier Houchard #define CK_HS_EMPTY     NULL
541fb62fb0SOlivier Houchard #define CK_HS_TOMBSTONE ((void *)~(uintptr_t)0)
551fb62fb0SOlivier Houchard #define CK_HS_G		(2)
561fb62fb0SOlivier Houchard #define CK_HS_G_MASK	(CK_HS_G - 1)
571fb62fb0SOlivier Houchard 
581fb62fb0SOlivier Houchard #if defined(CK_F_PR_LOAD_8) && defined(CK_F_PR_STORE_8)
591fb62fb0SOlivier Houchard #define CK_HS_WORD          uint8_t
601fb62fb0SOlivier Houchard #define CK_HS_WORD_MAX	    UINT8_MAX
611fb62fb0SOlivier Houchard #define CK_HS_STORE(x, y)   ck_pr_store_8(x, y)
621fb62fb0SOlivier Houchard #define CK_HS_LOAD(x)       ck_pr_load_8(x)
631fb62fb0SOlivier Houchard #elif defined(CK_F_PR_LOAD_16) && defined(CK_F_PR_STORE_16)
641fb62fb0SOlivier Houchard #define CK_HS_WORD          uint16_t
651fb62fb0SOlivier Houchard #define CK_HS_WORD_MAX	    UINT16_MAX
661fb62fb0SOlivier Houchard #define CK_HS_STORE(x, y)   ck_pr_store_16(x, y)
671fb62fb0SOlivier Houchard #define CK_HS_LOAD(x)       ck_pr_load_16(x)
681fb62fb0SOlivier Houchard #elif defined(CK_F_PR_LOAD_32) && defined(CK_F_PR_STORE_32)
691fb62fb0SOlivier Houchard #define CK_HS_WORD          uint32_t
701fb62fb0SOlivier Houchard #define CK_HS_WORD_MAX	    UINT32_MAX
711fb62fb0SOlivier Houchard #define CK_HS_STORE(x, y)   ck_pr_store_32(x, y)
721fb62fb0SOlivier Houchard #define CK_HS_LOAD(x)       ck_pr_load_32(x)
731fb62fb0SOlivier Houchard #else
741fb62fb0SOlivier Houchard #error "ck_hs is not supported on your platform."
751fb62fb0SOlivier Houchard #endif
761fb62fb0SOlivier Houchard 
771fb62fb0SOlivier Houchard enum ck_hs_probe_behavior {
781fb62fb0SOlivier Houchard 	CK_HS_PROBE = 0,	/* Default behavior. */
791fb62fb0SOlivier Houchard 	CK_HS_PROBE_TOMBSTONE,	/* Short-circuit on tombstone. */
801fb62fb0SOlivier Houchard 	CK_HS_PROBE_INSERT	/* Short-circuit on probe bound if tombstone found. */
811fb62fb0SOlivier Houchard };
821fb62fb0SOlivier Houchard 
831fb62fb0SOlivier Houchard struct ck_hs_map {
841fb62fb0SOlivier Houchard 	unsigned int generation[CK_HS_G];
851fb62fb0SOlivier Houchard 	unsigned int probe_maximum;
861fb62fb0SOlivier Houchard 	unsigned long mask;
871fb62fb0SOlivier Houchard 	unsigned long step;
881fb62fb0SOlivier Houchard 	unsigned int probe_limit;
891fb62fb0SOlivier Houchard 	unsigned int tombstones;
901fb62fb0SOlivier Houchard 	unsigned long n_entries;
911fb62fb0SOlivier Houchard 	unsigned long capacity;
921fb62fb0SOlivier Houchard 	unsigned long size;
931fb62fb0SOlivier Houchard 	CK_HS_WORD *probe_bound;
941fb62fb0SOlivier Houchard 	const void **entries;
951fb62fb0SOlivier Houchard };
961fb62fb0SOlivier Houchard 
971fb62fb0SOlivier Houchard static inline void
ck_hs_map_signal(struct ck_hs_map * map,unsigned long h)981fb62fb0SOlivier Houchard ck_hs_map_signal(struct ck_hs_map *map, unsigned long h)
991fb62fb0SOlivier Houchard {
1001fb62fb0SOlivier Houchard 
1011fb62fb0SOlivier Houchard 	h &= CK_HS_G_MASK;
1021fb62fb0SOlivier Houchard 	ck_pr_store_uint(&map->generation[h],
1031fb62fb0SOlivier Houchard 	    map->generation[h] + 1);
1041fb62fb0SOlivier Houchard 	ck_pr_fence_store();
1051fb62fb0SOlivier Houchard 	return;
1061fb62fb0SOlivier Houchard }
1071fb62fb0SOlivier Houchard 
108271ce402SOlivier Houchard static bool
_ck_hs_next(struct ck_hs * hs,struct ck_hs_map * map,struct ck_hs_iterator * i,void ** key)109*74e9b5f2SOlivier Houchard _ck_hs_next(struct ck_hs *hs, struct ck_hs_map *map,
110*74e9b5f2SOlivier Houchard     struct ck_hs_iterator *i, void **key)
1111fb62fb0SOlivier Houchard {
1121fb62fb0SOlivier Houchard 	void *value;
113*74e9b5f2SOlivier Houchard 
1141fb62fb0SOlivier Houchard 	if (i->offset >= map->capacity)
1151fb62fb0SOlivier Houchard 		return false;
1161fb62fb0SOlivier Houchard 
1171fb62fb0SOlivier Houchard 	do {
1181fb62fb0SOlivier Houchard 		value = CK_CC_DECONST_PTR(map->entries[i->offset]);
1191fb62fb0SOlivier Houchard 		if (value != CK_HS_EMPTY && value != CK_HS_TOMBSTONE) {
1201fb62fb0SOlivier Houchard #ifdef CK_HS_PP
1211fb62fb0SOlivier Houchard 			if (hs->mode & CK_HS_MODE_OBJECT)
1221fb62fb0SOlivier Houchard 				value = CK_HS_VMA(value);
123271ce402SOlivier Houchard #else
124271ce402SOlivier Houchard 			(void)hs; /* Avoid unused parameter warning. */
1251fb62fb0SOlivier Houchard #endif
1261fb62fb0SOlivier Houchard 			i->offset++;
1271fb62fb0SOlivier Houchard 			*key = value;
1281fb62fb0SOlivier Houchard 			return true;
1291fb62fb0SOlivier Houchard 		}
1301fb62fb0SOlivier Houchard 	} while (++i->offset < map->capacity);
1311fb62fb0SOlivier Houchard 
1321fb62fb0SOlivier Houchard 	return false;
1331fb62fb0SOlivier Houchard }
1341fb62fb0SOlivier Houchard 
1351fb62fb0SOlivier Houchard void
ck_hs_iterator_init(struct ck_hs_iterator * iterator)136271ce402SOlivier Houchard ck_hs_iterator_init(struct ck_hs_iterator *iterator)
137271ce402SOlivier Houchard {
138271ce402SOlivier Houchard 
139271ce402SOlivier Houchard 	iterator->cursor = NULL;
140271ce402SOlivier Houchard 	iterator->offset = 0;
141271ce402SOlivier Houchard 	iterator->map = NULL;
142271ce402SOlivier Houchard 	return;
143271ce402SOlivier Houchard }
144271ce402SOlivier Houchard 
145271ce402SOlivier Houchard bool
ck_hs_next(struct ck_hs * hs,struct ck_hs_iterator * i,void ** key)146271ce402SOlivier Houchard ck_hs_next(struct ck_hs *hs, struct ck_hs_iterator *i, void **key)
147271ce402SOlivier Houchard {
148*74e9b5f2SOlivier Houchard 
149271ce402SOlivier Houchard 	return _ck_hs_next(hs, hs->map, i, key);
150271ce402SOlivier Houchard }
151271ce402SOlivier Houchard 
152271ce402SOlivier Houchard bool
ck_hs_next_spmc(struct ck_hs * hs,struct ck_hs_iterator * i,void ** key)153271ce402SOlivier Houchard ck_hs_next_spmc(struct ck_hs *hs, struct ck_hs_iterator *i, void **key)
154271ce402SOlivier Houchard {
155271ce402SOlivier Houchard 	struct ck_hs_map *m = i->map;
156*74e9b5f2SOlivier Houchard 
157271ce402SOlivier Houchard 	if (m == NULL) {
158271ce402SOlivier Houchard 		m = i->map = ck_pr_load_ptr(&hs->map);
159271ce402SOlivier Houchard 	}
160*74e9b5f2SOlivier Houchard 
161271ce402SOlivier Houchard 	return _ck_hs_next(hs, m, i, key);
162271ce402SOlivier Houchard }
163271ce402SOlivier Houchard 
164271ce402SOlivier Houchard void
ck_hs_stat(struct ck_hs * hs,struct ck_hs_stat * st)1651fb62fb0SOlivier Houchard ck_hs_stat(struct ck_hs *hs, struct ck_hs_stat *st)
1661fb62fb0SOlivier Houchard {
1671fb62fb0SOlivier Houchard 	struct ck_hs_map *map = hs->map;
1681fb62fb0SOlivier Houchard 
1691fb62fb0SOlivier Houchard 	st->n_entries = map->n_entries;
1701fb62fb0SOlivier Houchard 	st->tombstones = map->tombstones;
1711fb62fb0SOlivier Houchard 	st->probe_maximum = map->probe_maximum;
1721fb62fb0SOlivier Houchard 	return;
1731fb62fb0SOlivier Houchard }
1741fb62fb0SOlivier Houchard 
1751fb62fb0SOlivier Houchard unsigned long
ck_hs_count(struct ck_hs * hs)1761fb62fb0SOlivier Houchard ck_hs_count(struct ck_hs *hs)
1771fb62fb0SOlivier Houchard {
1781fb62fb0SOlivier Houchard 
1791fb62fb0SOlivier Houchard 	return hs->map->n_entries;
1801fb62fb0SOlivier Houchard }
1811fb62fb0SOlivier Houchard 
1821fb62fb0SOlivier Houchard static void
ck_hs_map_destroy(struct ck_malloc * m,struct ck_hs_map * map,bool defer)1831fb62fb0SOlivier Houchard ck_hs_map_destroy(struct ck_malloc *m, struct ck_hs_map *map, bool defer)
1841fb62fb0SOlivier Houchard {
1851fb62fb0SOlivier Houchard 
1861fb62fb0SOlivier Houchard 	m->free(map, map->size, defer);
1871fb62fb0SOlivier Houchard 	return;
1881fb62fb0SOlivier Houchard }
1891fb62fb0SOlivier Houchard 
1901fb62fb0SOlivier Houchard void
ck_hs_destroy(struct ck_hs * hs)1911fb62fb0SOlivier Houchard ck_hs_destroy(struct ck_hs *hs)
1921fb62fb0SOlivier Houchard {
1931fb62fb0SOlivier Houchard 
1941fb62fb0SOlivier Houchard 	ck_hs_map_destroy(hs->m, hs->map, false);
1951fb62fb0SOlivier Houchard 	return;
1961fb62fb0SOlivier Houchard }
1971fb62fb0SOlivier Houchard 
1981fb62fb0SOlivier Houchard static struct ck_hs_map *
ck_hs_map_create(struct ck_hs * hs,unsigned long entries)1991fb62fb0SOlivier Houchard ck_hs_map_create(struct ck_hs *hs, unsigned long entries)
2001fb62fb0SOlivier Houchard {
2011fb62fb0SOlivier Houchard 	struct ck_hs_map *map;
2021fb62fb0SOlivier Houchard 	unsigned long size, n_entries, prefix, limit;
2031fb62fb0SOlivier Houchard 
2041fb62fb0SOlivier Houchard 	n_entries = ck_internal_power_2(entries);
2051fb62fb0SOlivier Houchard 	if (n_entries < CK_HS_PROBE_L1)
2061fb62fb0SOlivier Houchard 		n_entries = CK_HS_PROBE_L1;
2071fb62fb0SOlivier Houchard 
2081fb62fb0SOlivier Houchard 	size = sizeof(struct ck_hs_map) + (sizeof(void *) * n_entries + CK_MD_CACHELINE - 1);
2091fb62fb0SOlivier Houchard 
2101fb62fb0SOlivier Houchard 	if (hs->mode & CK_HS_MODE_DELETE) {
2111fb62fb0SOlivier Houchard 		prefix = sizeof(CK_HS_WORD) * n_entries;
2121fb62fb0SOlivier Houchard 		size += prefix;
2131fb62fb0SOlivier Houchard 	} else {
2141fb62fb0SOlivier Houchard 		prefix = 0;
2151fb62fb0SOlivier Houchard 	}
2161fb62fb0SOlivier Houchard 
2171fb62fb0SOlivier Houchard 	map = hs->m->malloc(size);
2181fb62fb0SOlivier Houchard 	if (map == NULL)
2191fb62fb0SOlivier Houchard 		return NULL;
2201fb62fb0SOlivier Houchard 
2211fb62fb0SOlivier Houchard 	map->size = size;
2221fb62fb0SOlivier Houchard 
2231fb62fb0SOlivier Houchard 	/* We should probably use a more intelligent heuristic for default probe length. */
2241fb62fb0SOlivier Houchard 	limit = ck_internal_max(n_entries >> (CK_HS_PROBE_L1_SHIFT + 2), CK_HS_PROBE_L1_DEFAULT);
2251fb62fb0SOlivier Houchard 	if (limit > UINT_MAX)
2261fb62fb0SOlivier Houchard 		limit = UINT_MAX;
2271fb62fb0SOlivier Houchard 
2281fb62fb0SOlivier Houchard 	map->probe_limit = (unsigned int)limit;
2291fb62fb0SOlivier Houchard 	map->probe_maximum = 0;
2301fb62fb0SOlivier Houchard 	map->capacity = n_entries;
231271ce402SOlivier Houchard 	map->step = ck_cc_ffsl(n_entries);
2321fb62fb0SOlivier Houchard 	map->mask = n_entries - 1;
2331fb62fb0SOlivier Houchard 	map->n_entries = 0;
2341fb62fb0SOlivier Houchard 
2351fb62fb0SOlivier Houchard 	/* Align map allocation to cache line. */
2361fb62fb0SOlivier Houchard 	map->entries = (void *)(((uintptr_t)&map[1] + prefix +
2371fb62fb0SOlivier Houchard 	    CK_MD_CACHELINE - 1) & ~(CK_MD_CACHELINE - 1));
2381fb62fb0SOlivier Houchard 
2391fb62fb0SOlivier Houchard 	memset(map->entries, 0, sizeof(void *) * n_entries);
2401fb62fb0SOlivier Houchard 	memset(map->generation, 0, sizeof map->generation);
2411fb62fb0SOlivier Houchard 
2421fb62fb0SOlivier Houchard 	if (hs->mode & CK_HS_MODE_DELETE) {
2431fb62fb0SOlivier Houchard 		map->probe_bound = (CK_HS_WORD *)&map[1];
2441fb62fb0SOlivier Houchard 		memset(map->probe_bound, 0, prefix);
2451fb62fb0SOlivier Houchard 	} else {
2461fb62fb0SOlivier Houchard 		map->probe_bound = NULL;
2471fb62fb0SOlivier Houchard 	}
2481fb62fb0SOlivier Houchard 
2491fb62fb0SOlivier Houchard 	/* Commit entries purge with respect to map publication. */
2501fb62fb0SOlivier Houchard 	ck_pr_fence_store();
2511fb62fb0SOlivier Houchard 	return map;
2521fb62fb0SOlivier Houchard }
2531fb62fb0SOlivier Houchard 
2541fb62fb0SOlivier Houchard bool
ck_hs_reset_size(struct ck_hs * hs,unsigned long capacity)2551fb62fb0SOlivier Houchard ck_hs_reset_size(struct ck_hs *hs, unsigned long capacity)
2561fb62fb0SOlivier Houchard {
2571fb62fb0SOlivier Houchard 	struct ck_hs_map *map, *previous;
2581fb62fb0SOlivier Houchard 
2591fb62fb0SOlivier Houchard 	previous = hs->map;
2601fb62fb0SOlivier Houchard 	map = ck_hs_map_create(hs, capacity);
2611fb62fb0SOlivier Houchard 	if (map == NULL)
2621fb62fb0SOlivier Houchard 		return false;
2631fb62fb0SOlivier Houchard 
2641fb62fb0SOlivier Houchard 	ck_pr_store_ptr(&hs->map, map);
2651fb62fb0SOlivier Houchard 	ck_hs_map_destroy(hs->m, previous, true);
2661fb62fb0SOlivier Houchard 	return true;
2671fb62fb0SOlivier Houchard }
2681fb62fb0SOlivier Houchard 
2691fb62fb0SOlivier Houchard bool
ck_hs_reset(struct ck_hs * hs)2701fb62fb0SOlivier Houchard ck_hs_reset(struct ck_hs *hs)
2711fb62fb0SOlivier Houchard {
2721fb62fb0SOlivier Houchard 	struct ck_hs_map *previous;
2731fb62fb0SOlivier Houchard 
2741fb62fb0SOlivier Houchard 	previous = hs->map;
2751fb62fb0SOlivier Houchard 	return ck_hs_reset_size(hs, previous->capacity);
2761fb62fb0SOlivier Houchard }
2771fb62fb0SOlivier Houchard 
2781fb62fb0SOlivier Houchard 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)2791fb62fb0SOlivier Houchard ck_hs_map_probe_next(struct ck_hs_map *map,
2801fb62fb0SOlivier Houchard     unsigned long offset,
2811fb62fb0SOlivier Houchard     unsigned long h,
2821fb62fb0SOlivier Houchard     unsigned long level,
2831fb62fb0SOlivier Houchard     unsigned long probes)
2841fb62fb0SOlivier Houchard {
2851fb62fb0SOlivier Houchard 	unsigned long r, stride;
2861fb62fb0SOlivier Houchard 
2871fb62fb0SOlivier Houchard 	r = (h >> map->step) >> level;
2881fb62fb0SOlivier Houchard 	stride = (r & ~CK_HS_PROBE_L1_MASK) << 1 | (r & CK_HS_PROBE_L1_MASK);
2891fb62fb0SOlivier Houchard 
2901fb62fb0SOlivier Houchard 	return (offset + (probes >> CK_HS_PROBE_L1_SHIFT) +
2911fb62fb0SOlivier Houchard 	    (stride | CK_HS_PROBE_L1)) & map->mask;
2921fb62fb0SOlivier Houchard }
2931fb62fb0SOlivier Houchard 
2941fb62fb0SOlivier Houchard static inline void
ck_hs_map_bound_set(struct ck_hs_map * m,unsigned long h,unsigned long n_probes)2951fb62fb0SOlivier Houchard ck_hs_map_bound_set(struct ck_hs_map *m,
2961fb62fb0SOlivier Houchard     unsigned long h,
2971fb62fb0SOlivier Houchard     unsigned long n_probes)
2981fb62fb0SOlivier Houchard {
2991fb62fb0SOlivier Houchard 	unsigned long offset = h & m->mask;
3001fb62fb0SOlivier Houchard 
3011fb62fb0SOlivier Houchard 	if (n_probes > m->probe_maximum)
3021fb62fb0SOlivier Houchard 		ck_pr_store_uint(&m->probe_maximum, n_probes);
3031fb62fb0SOlivier Houchard 
3041fb62fb0SOlivier Houchard 	if (m->probe_bound != NULL && m->probe_bound[offset] < n_probes) {
3051fb62fb0SOlivier Houchard 		if (n_probes > CK_HS_WORD_MAX)
3061fb62fb0SOlivier Houchard 			n_probes = CK_HS_WORD_MAX;
3071fb62fb0SOlivier Houchard 
3081fb62fb0SOlivier Houchard 		CK_HS_STORE(&m->probe_bound[offset], n_probes);
3091fb62fb0SOlivier Houchard 		ck_pr_fence_store();
3101fb62fb0SOlivier Houchard 	}
3111fb62fb0SOlivier Houchard 
3121fb62fb0SOlivier Houchard 	return;
3131fb62fb0SOlivier Houchard }
3141fb62fb0SOlivier Houchard 
3151fb62fb0SOlivier Houchard static inline unsigned int
ck_hs_map_bound_get(struct ck_hs_map * m,unsigned long h)3161fb62fb0SOlivier Houchard ck_hs_map_bound_get(struct ck_hs_map *m, unsigned long h)
3171fb62fb0SOlivier Houchard {
3181fb62fb0SOlivier Houchard 	unsigned long offset = h & m->mask;
3191fb62fb0SOlivier Houchard 	unsigned int r = CK_HS_WORD_MAX;
3201fb62fb0SOlivier Houchard 
3211fb62fb0SOlivier Houchard 	if (m->probe_bound != NULL) {
3221fb62fb0SOlivier Houchard 		r = CK_HS_LOAD(&m->probe_bound[offset]);
3231fb62fb0SOlivier Houchard 		if (r == CK_HS_WORD_MAX)
3241fb62fb0SOlivier Houchard 			r = ck_pr_load_uint(&m->probe_maximum);
3251fb62fb0SOlivier Houchard 	} else {
3261fb62fb0SOlivier Houchard 		r = ck_pr_load_uint(&m->probe_maximum);
3271fb62fb0SOlivier Houchard 	}
3281fb62fb0SOlivier Houchard 
3291fb62fb0SOlivier Houchard 	return r;
3301fb62fb0SOlivier Houchard }
3311fb62fb0SOlivier Houchard 
3321fb62fb0SOlivier Houchard bool
ck_hs_grow(struct ck_hs * hs,unsigned long capacity)3331fb62fb0SOlivier Houchard ck_hs_grow(struct ck_hs *hs,
3341fb62fb0SOlivier Houchard     unsigned long capacity)
3351fb62fb0SOlivier Houchard {
3361fb62fb0SOlivier Houchard 	struct ck_hs_map *map, *update;
3371fb62fb0SOlivier Houchard 	unsigned long k, i, j, offset, probes;
3381fb62fb0SOlivier Houchard 	const void *previous, **bucket;
3391fb62fb0SOlivier Houchard 
3401fb62fb0SOlivier Houchard restart:
3411fb62fb0SOlivier Houchard 	map = hs->map;
3421fb62fb0SOlivier Houchard 	if (map->capacity > capacity)
3431fb62fb0SOlivier Houchard 		return false;
3441fb62fb0SOlivier Houchard 
3451fb62fb0SOlivier Houchard 	update = ck_hs_map_create(hs, capacity);
3461fb62fb0SOlivier Houchard 	if (update == NULL)
3471fb62fb0SOlivier Houchard 		return false;
3481fb62fb0SOlivier Houchard 
3491fb62fb0SOlivier Houchard 	for (k = 0; k < map->capacity; k++) {
3501fb62fb0SOlivier Houchard 		unsigned long h;
3511fb62fb0SOlivier Houchard 
3521fb62fb0SOlivier Houchard 		previous = map->entries[k];
3531fb62fb0SOlivier Houchard 		if (previous == CK_HS_EMPTY || previous == CK_HS_TOMBSTONE)
3541fb62fb0SOlivier Houchard 			continue;
3551fb62fb0SOlivier Houchard 
3561fb62fb0SOlivier Houchard #ifdef CK_HS_PP
3571fb62fb0SOlivier Houchard 		if (hs->mode & CK_HS_MODE_OBJECT)
3581fb62fb0SOlivier Houchard 			previous = CK_HS_VMA(previous);
3591fb62fb0SOlivier Houchard #endif
3601fb62fb0SOlivier Houchard 
3611fb62fb0SOlivier Houchard 		h = hs->hf(previous, hs->seed);
3621fb62fb0SOlivier Houchard 		offset = h & update->mask;
3631fb62fb0SOlivier Houchard 		i = probes = 0;
3641fb62fb0SOlivier Houchard 
3651fb62fb0SOlivier Houchard 		for (;;) {
3661fb62fb0SOlivier Houchard 			bucket = (const void **)((uintptr_t)&update->entries[offset] & ~(CK_MD_CACHELINE - 1));
3671fb62fb0SOlivier Houchard 
3681fb62fb0SOlivier Houchard 			for (j = 0; j < CK_HS_PROBE_L1; j++) {
3691fb62fb0SOlivier Houchard 				const void **cursor = bucket + ((j + offset) & (CK_HS_PROBE_L1 - 1));
3701fb62fb0SOlivier Houchard 
3711fb62fb0SOlivier Houchard 				if (probes++ == update->probe_limit)
3721fb62fb0SOlivier Houchard 					break;
3731fb62fb0SOlivier Houchard 
3741fb62fb0SOlivier Houchard 				if (CK_CC_LIKELY(*cursor == CK_HS_EMPTY)) {
3751fb62fb0SOlivier Houchard 					*cursor = map->entries[k];
3761fb62fb0SOlivier Houchard 					update->n_entries++;
3771fb62fb0SOlivier Houchard 
3781fb62fb0SOlivier Houchard 					ck_hs_map_bound_set(update, h, probes);
3791fb62fb0SOlivier Houchard 					break;
3801fb62fb0SOlivier Houchard 				}
3811fb62fb0SOlivier Houchard 			}
3821fb62fb0SOlivier Houchard 
3831fb62fb0SOlivier Houchard 			if (j < CK_HS_PROBE_L1)
3841fb62fb0SOlivier Houchard 				break;
3851fb62fb0SOlivier Houchard 
3861fb62fb0SOlivier Houchard 			offset = ck_hs_map_probe_next(update, offset, h, i++, probes);
3871fb62fb0SOlivier Houchard 		}
3881fb62fb0SOlivier Houchard 
3891fb62fb0SOlivier Houchard 		if (probes > update->probe_limit) {
3901fb62fb0SOlivier Houchard 			/*
3911fb62fb0SOlivier Houchard 			 * We have hit the probe limit, map needs to be even larger.
3921fb62fb0SOlivier Houchard 			 */
3931fb62fb0SOlivier Houchard 			ck_hs_map_destroy(hs->m, update, false);
3941fb62fb0SOlivier Houchard 			capacity <<= 1;
3951fb62fb0SOlivier Houchard 			goto restart;
3961fb62fb0SOlivier Houchard 		}
3971fb62fb0SOlivier Houchard 	}
3981fb62fb0SOlivier Houchard 
3991fb62fb0SOlivier Houchard 	ck_pr_fence_store();
4001fb62fb0SOlivier Houchard 	ck_pr_store_ptr(&hs->map, update);
4011fb62fb0SOlivier Houchard 	ck_hs_map_destroy(hs->m, map, true);
4021fb62fb0SOlivier Houchard 	return true;
4031fb62fb0SOlivier Houchard }
4041fb62fb0SOlivier Houchard 
4051fb62fb0SOlivier Houchard static void
ck_hs_map_postinsert(struct ck_hs * hs,struct ck_hs_map * map)4061fb62fb0SOlivier Houchard ck_hs_map_postinsert(struct ck_hs *hs, struct ck_hs_map *map)
4071fb62fb0SOlivier Houchard {
4081fb62fb0SOlivier Houchard 
4091fb62fb0SOlivier Houchard 	map->n_entries++;
4101fb62fb0SOlivier Houchard 	if ((map->n_entries << 1) > map->capacity)
4111fb62fb0SOlivier Houchard 		ck_hs_grow(hs, map->capacity << 1);
4121fb62fb0SOlivier Houchard 
4131fb62fb0SOlivier Houchard 	return;
4141fb62fb0SOlivier Houchard }
4151fb62fb0SOlivier Houchard 
4161fb62fb0SOlivier Houchard bool
ck_hs_rebuild(struct ck_hs * hs)4171fb62fb0SOlivier Houchard ck_hs_rebuild(struct ck_hs *hs)
4181fb62fb0SOlivier Houchard {
4191fb62fb0SOlivier Houchard 
4201fb62fb0SOlivier Houchard 	return ck_hs_grow(hs, hs->map->capacity);
4211fb62fb0SOlivier Houchard }
4221fb62fb0SOlivier Houchard 
4231fb62fb0SOlivier Houchard 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)4241fb62fb0SOlivier Houchard ck_hs_map_probe(struct ck_hs *hs,
4251fb62fb0SOlivier Houchard     struct ck_hs_map *map,
4261fb62fb0SOlivier Houchard     unsigned long *n_probes,
4271fb62fb0SOlivier Houchard     const void ***priority,
4281fb62fb0SOlivier Houchard     unsigned long h,
4291fb62fb0SOlivier Houchard     const void *key,
4301fb62fb0SOlivier Houchard     const void **object,
4311fb62fb0SOlivier Houchard     unsigned long probe_limit,
4321fb62fb0SOlivier Houchard     enum ck_hs_probe_behavior behavior)
4331fb62fb0SOlivier Houchard {
4341fb62fb0SOlivier Houchard 	const void **bucket, **cursor, *k, *compare;
4351fb62fb0SOlivier Houchard 	const void **pr = NULL;
4361fb62fb0SOlivier Houchard 	unsigned long offset, j, i, probes, opl;
4371fb62fb0SOlivier Houchard 
4381fb62fb0SOlivier Houchard #ifdef CK_HS_PP
4391fb62fb0SOlivier Houchard 	/* If we are storing object pointers, then we may leverage pointer packing. */
4401fb62fb0SOlivier Houchard 	unsigned long hv = 0;
4411fb62fb0SOlivier Houchard 
4421fb62fb0SOlivier Houchard 	if (hs->mode & CK_HS_MODE_OBJECT) {
4431fb62fb0SOlivier Houchard 		hv = (h >> 25) & CK_HS_KEY_MASK;
4441fb62fb0SOlivier Houchard 		compare = CK_HS_VMA(key);
4451fb62fb0SOlivier Houchard 	} else {
4461fb62fb0SOlivier Houchard 		compare = key;
4471fb62fb0SOlivier Houchard 	}
4481fb62fb0SOlivier Houchard #else
4491fb62fb0SOlivier Houchard 	compare = key;
4501fb62fb0SOlivier Houchard #endif
4511fb62fb0SOlivier Houchard 
4521fb62fb0SOlivier Houchard 	offset = h & map->mask;
4531fb62fb0SOlivier Houchard 	*object = NULL;
4541fb62fb0SOlivier Houchard 	i = probes = 0;
4551fb62fb0SOlivier Houchard 
4561fb62fb0SOlivier Houchard 	opl = probe_limit;
4571fb62fb0SOlivier Houchard 	if (behavior == CK_HS_PROBE_INSERT)
4581fb62fb0SOlivier Houchard 		probe_limit = ck_hs_map_bound_get(map, h);
4591fb62fb0SOlivier Houchard 
4601fb62fb0SOlivier Houchard 	for (;;) {
4611fb62fb0SOlivier Houchard 		bucket = (const void **)((uintptr_t)&map->entries[offset] & ~(CK_MD_CACHELINE - 1));
4621fb62fb0SOlivier Houchard 
4631fb62fb0SOlivier Houchard 		for (j = 0; j < CK_HS_PROBE_L1; j++) {
4641fb62fb0SOlivier Houchard 			cursor = bucket + ((j + offset) & (CK_HS_PROBE_L1 - 1));
4651fb62fb0SOlivier Houchard 
4661fb62fb0SOlivier Houchard 			if (probes++ == probe_limit) {
4671fb62fb0SOlivier Houchard 				if (probe_limit == opl || pr != NULL) {
4681fb62fb0SOlivier Houchard 					k = CK_HS_EMPTY;
4691fb62fb0SOlivier Houchard 					goto leave;
4701fb62fb0SOlivier Houchard 				}
4711fb62fb0SOlivier Houchard 
4721fb62fb0SOlivier Houchard 				/*
4731fb62fb0SOlivier Houchard 				 * If no eligible slot has been found yet, continue probe
4741fb62fb0SOlivier Houchard 				 * sequence with original probe limit.
4751fb62fb0SOlivier Houchard 				 */
4761fb62fb0SOlivier Houchard 				probe_limit = opl;
4771fb62fb0SOlivier Houchard 			}
4781fb62fb0SOlivier Houchard 
4791fb62fb0SOlivier Houchard 			k = ck_pr_load_ptr(cursor);
4801fb62fb0SOlivier Houchard 			if (k == CK_HS_EMPTY)
4811fb62fb0SOlivier Houchard 				goto leave;
4821fb62fb0SOlivier Houchard 
4831fb62fb0SOlivier Houchard 			if (k == CK_HS_TOMBSTONE) {
4841fb62fb0SOlivier Houchard 				if (pr == NULL) {
4851fb62fb0SOlivier Houchard 					pr = cursor;
4861fb62fb0SOlivier Houchard 					*n_probes = probes;
4871fb62fb0SOlivier Houchard 
4881fb62fb0SOlivier Houchard 					if (behavior == CK_HS_PROBE_TOMBSTONE) {
4891fb62fb0SOlivier Houchard 						k = CK_HS_EMPTY;
4901fb62fb0SOlivier Houchard 						goto leave;
4911fb62fb0SOlivier Houchard 					}
4921fb62fb0SOlivier Houchard 				}
4931fb62fb0SOlivier Houchard 
4941fb62fb0SOlivier Houchard 				continue;
4951fb62fb0SOlivier Houchard 			}
4961fb62fb0SOlivier Houchard 
4971fb62fb0SOlivier Houchard #ifdef CK_HS_PP
4981fb62fb0SOlivier Houchard 			if (hs->mode & CK_HS_MODE_OBJECT) {
4991fb62fb0SOlivier Houchard 				if (((uintptr_t)k >> CK_MD_VMA_BITS) != hv)
5001fb62fb0SOlivier Houchard 					continue;
5011fb62fb0SOlivier Houchard 
5021fb62fb0SOlivier Houchard 				k = CK_HS_VMA(k);
5031fb62fb0SOlivier Houchard 			}
5041fb62fb0SOlivier Houchard #endif
5051fb62fb0SOlivier Houchard 
5061fb62fb0SOlivier Houchard 			if (k == compare)
5071fb62fb0SOlivier Houchard 				goto leave;
5081fb62fb0SOlivier Houchard 
5091fb62fb0SOlivier Houchard 			if (hs->compare == NULL)
5101fb62fb0SOlivier Houchard 				continue;
5111fb62fb0SOlivier Houchard 
5121fb62fb0SOlivier Houchard 			if (hs->compare(k, key) == true)
5131fb62fb0SOlivier Houchard 				goto leave;
5141fb62fb0SOlivier Houchard 		}
5151fb62fb0SOlivier Houchard 
5161fb62fb0SOlivier Houchard 		offset = ck_hs_map_probe_next(map, offset, h, i++, probes);
5171fb62fb0SOlivier Houchard 	}
5181fb62fb0SOlivier Houchard 
5191fb62fb0SOlivier Houchard leave:
5201fb62fb0SOlivier Houchard 	if (probes > probe_limit) {
5211fb62fb0SOlivier Houchard 		cursor = NULL;
5221fb62fb0SOlivier Houchard 	} else {
5231fb62fb0SOlivier Houchard 		*object = k;
5241fb62fb0SOlivier Houchard 	}
5251fb62fb0SOlivier Houchard 
5261fb62fb0SOlivier Houchard 	if (pr == NULL)
5271fb62fb0SOlivier Houchard 		*n_probes = probes;
5281fb62fb0SOlivier Houchard 
5291fb62fb0SOlivier Houchard 	*priority = pr;
5301fb62fb0SOlivier Houchard 	return cursor;
5311fb62fb0SOlivier Houchard }
5321fb62fb0SOlivier Houchard 
5331fb62fb0SOlivier Houchard static inline const void *
ck_hs_marshal(unsigned int mode,const void * key,unsigned long h)5341fb62fb0SOlivier Houchard ck_hs_marshal(unsigned int mode, const void *key, unsigned long h)
5351fb62fb0SOlivier Houchard {
5361fb62fb0SOlivier Houchard #ifdef CK_HS_PP
5371fb62fb0SOlivier Houchard 	const void *insert;
5381fb62fb0SOlivier Houchard 
5391fb62fb0SOlivier Houchard 	if (mode & CK_HS_MODE_OBJECT) {
5401fb62fb0SOlivier Houchard 		insert = (void *)((uintptr_t)CK_HS_VMA(key) |
5411fb62fb0SOlivier Houchard 		    ((h >> 25) << CK_MD_VMA_BITS));
5421fb62fb0SOlivier Houchard 	} else {
5431fb62fb0SOlivier Houchard 		insert = key;
5441fb62fb0SOlivier Houchard 	}
5451fb62fb0SOlivier Houchard 
5461fb62fb0SOlivier Houchard 	return insert;
5471fb62fb0SOlivier Houchard #else
5481fb62fb0SOlivier Houchard 	(void)mode;
5491fb62fb0SOlivier Houchard 	(void)h;
5501fb62fb0SOlivier Houchard 
5511fb62fb0SOlivier Houchard 	return key;
5521fb62fb0SOlivier Houchard #endif
5531fb62fb0SOlivier Houchard }
5541fb62fb0SOlivier Houchard 
5551fb62fb0SOlivier Houchard bool
ck_hs_gc(struct ck_hs * hs,unsigned long cycles,unsigned long seed)5561fb62fb0SOlivier Houchard ck_hs_gc(struct ck_hs *hs, unsigned long cycles, unsigned long seed)
5571fb62fb0SOlivier Houchard {
5581fb62fb0SOlivier Houchard 	unsigned long size = 0;
5591fb62fb0SOlivier Houchard 	unsigned long i;
5601fb62fb0SOlivier Houchard 	struct ck_hs_map *map = hs->map;
5611fb62fb0SOlivier Houchard 	unsigned int maximum;
5621fb62fb0SOlivier Houchard 	CK_HS_WORD *bounds = NULL;
5631fb62fb0SOlivier Houchard 
5641fb62fb0SOlivier Houchard 	if (map->n_entries == 0) {
5651fb62fb0SOlivier Houchard 		ck_pr_store_uint(&map->probe_maximum, 0);
5661fb62fb0SOlivier Houchard 		if (map->probe_bound != NULL)
5671fb62fb0SOlivier Houchard 			memset(map->probe_bound, 0, sizeof(CK_HS_WORD) * map->capacity);
5681fb62fb0SOlivier Houchard 
5691fb62fb0SOlivier Houchard 		return true;
5701fb62fb0SOlivier Houchard 	}
5711fb62fb0SOlivier Houchard 
5721fb62fb0SOlivier Houchard 	if (cycles == 0) {
5731fb62fb0SOlivier Houchard 		maximum = 0;
5741fb62fb0SOlivier Houchard 
5751fb62fb0SOlivier Houchard 		if (map->probe_bound != NULL) {
5761fb62fb0SOlivier Houchard 			size = sizeof(CK_HS_WORD) * map->capacity;
5771fb62fb0SOlivier Houchard 			bounds = hs->m->malloc(size);
5781fb62fb0SOlivier Houchard 			if (bounds == NULL)
5791fb62fb0SOlivier Houchard 				return false;
5801fb62fb0SOlivier Houchard 
5811fb62fb0SOlivier Houchard 			memset(bounds, 0, size);
5821fb62fb0SOlivier Houchard 		}
5831fb62fb0SOlivier Houchard 	} else {
5841fb62fb0SOlivier Houchard 		maximum = map->probe_maximum;
5851fb62fb0SOlivier Houchard 	}
5861fb62fb0SOlivier Houchard 
5871fb62fb0SOlivier Houchard 	for (i = 0; i < map->capacity; i++) {
5881fb62fb0SOlivier Houchard 		const void **first, *object, **slot, *entry;
5891fb62fb0SOlivier Houchard 		unsigned long n_probes, offset, h;
5901fb62fb0SOlivier Houchard 
5911fb62fb0SOlivier Houchard 		entry = map->entries[(i + seed) & map->mask];
5921fb62fb0SOlivier Houchard 		if (entry == CK_HS_EMPTY || entry == CK_HS_TOMBSTONE)
5931fb62fb0SOlivier Houchard 			continue;
5941fb62fb0SOlivier Houchard 
5951fb62fb0SOlivier Houchard #ifdef CK_HS_PP
5961fb62fb0SOlivier Houchard 		if (hs->mode & CK_HS_MODE_OBJECT)
5971fb62fb0SOlivier Houchard 			entry = CK_HS_VMA(entry);
5981fb62fb0SOlivier Houchard #endif
5991fb62fb0SOlivier Houchard 
6001fb62fb0SOlivier Houchard 		h = hs->hf(entry, hs->seed);
6011fb62fb0SOlivier Houchard 		offset = h & map->mask;
6021fb62fb0SOlivier Houchard 
6031fb62fb0SOlivier Houchard 		slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, entry, &object,
6041fb62fb0SOlivier Houchard 		    ck_hs_map_bound_get(map, h), CK_HS_PROBE);
6051fb62fb0SOlivier Houchard 
6061fb62fb0SOlivier Houchard 		if (first != NULL) {
6071fb62fb0SOlivier Houchard 			const void *insert = ck_hs_marshal(hs->mode, entry, h);
6081fb62fb0SOlivier Houchard 
6091fb62fb0SOlivier Houchard 			ck_pr_store_ptr(first, insert);
6101fb62fb0SOlivier Houchard 			ck_hs_map_signal(map, h);
6111fb62fb0SOlivier Houchard 			ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
6121fb62fb0SOlivier Houchard 		}
6131fb62fb0SOlivier Houchard 
6141fb62fb0SOlivier Houchard 		if (cycles == 0) {
6151fb62fb0SOlivier Houchard 			if (n_probes > maximum)
6161fb62fb0SOlivier Houchard 				maximum = n_probes;
6171fb62fb0SOlivier Houchard 
6181fb62fb0SOlivier Houchard 			if (n_probes > CK_HS_WORD_MAX)
6191fb62fb0SOlivier Houchard 				n_probes = CK_HS_WORD_MAX;
6201fb62fb0SOlivier Houchard 
6211fb62fb0SOlivier Houchard 			if (bounds != NULL && n_probes > bounds[offset])
6221fb62fb0SOlivier Houchard 				bounds[offset] = n_probes;
6231fb62fb0SOlivier Houchard 		} else if (--cycles == 0)
6241fb62fb0SOlivier Houchard 			break;
6251fb62fb0SOlivier Houchard 	}
6261fb62fb0SOlivier Houchard 
6271fb62fb0SOlivier Houchard 	/*
6281fb62fb0SOlivier Houchard 	 * The following only apply to garbage collection involving
6291fb62fb0SOlivier Houchard 	 * a full scan of all entries.
6301fb62fb0SOlivier Houchard 	 */
6311fb62fb0SOlivier Houchard 	if (maximum != map->probe_maximum)
6321fb62fb0SOlivier Houchard 		ck_pr_store_uint(&map->probe_maximum, maximum);
6331fb62fb0SOlivier Houchard 
6341fb62fb0SOlivier Houchard 	if (bounds != NULL) {
6351fb62fb0SOlivier Houchard 		for (i = 0; i < map->capacity; i++)
6361fb62fb0SOlivier Houchard 			CK_HS_STORE(&map->probe_bound[i], bounds[i]);
6371fb62fb0SOlivier Houchard 
6381fb62fb0SOlivier Houchard 		hs->m->free(bounds, size, false);
6391fb62fb0SOlivier Houchard 	}
6401fb62fb0SOlivier Houchard 
6411fb62fb0SOlivier Houchard 	return true;
6421fb62fb0SOlivier Houchard }
6431fb62fb0SOlivier Houchard 
6441fb62fb0SOlivier Houchard bool
ck_hs_fas(struct ck_hs * hs,unsigned long h,const void * key,void ** previous)6451fb62fb0SOlivier Houchard ck_hs_fas(struct ck_hs *hs,
6461fb62fb0SOlivier Houchard     unsigned long h,
6471fb62fb0SOlivier Houchard     const void *key,
6481fb62fb0SOlivier Houchard     void **previous)
6491fb62fb0SOlivier Houchard {
6501fb62fb0SOlivier Houchard 	const void **slot, **first, *object, *insert;
6511fb62fb0SOlivier Houchard 	struct ck_hs_map *map = hs->map;
6521fb62fb0SOlivier Houchard 	unsigned long n_probes;
6531fb62fb0SOlivier Houchard 
6541fb62fb0SOlivier Houchard 	*previous = NULL;
6551fb62fb0SOlivier Houchard 	slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object,
6561fb62fb0SOlivier Houchard 	    ck_hs_map_bound_get(map, h), CK_HS_PROBE);
6571fb62fb0SOlivier Houchard 
6581fb62fb0SOlivier Houchard 	/* Replacement semantics presume existence. */
6591fb62fb0SOlivier Houchard 	if (object == NULL)
6601fb62fb0SOlivier Houchard 		return false;
6611fb62fb0SOlivier Houchard 
6621fb62fb0SOlivier Houchard 	insert = ck_hs_marshal(hs->mode, key, h);
6631fb62fb0SOlivier Houchard 
6641fb62fb0SOlivier Houchard 	if (first != NULL) {
6651fb62fb0SOlivier Houchard 		ck_pr_store_ptr(first, insert);
6661fb62fb0SOlivier Houchard 		ck_hs_map_signal(map, h);
6671fb62fb0SOlivier Houchard 		ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
6681fb62fb0SOlivier Houchard 	} else {
6691fb62fb0SOlivier Houchard 		ck_pr_store_ptr(slot, insert);
6701fb62fb0SOlivier Houchard 	}
6711fb62fb0SOlivier Houchard 
6721fb62fb0SOlivier Houchard 	*previous = CK_CC_DECONST_PTR(object);
6731fb62fb0SOlivier Houchard 	return true;
6741fb62fb0SOlivier Houchard }
6751fb62fb0SOlivier Houchard 
6761fb62fb0SOlivier Houchard /*
6771fb62fb0SOlivier Houchard  * An apply function takes two arguments. The first argument is a pointer to a
6781fb62fb0SOlivier Houchard  * pre-existing object. The second argument is a pointer to the fifth argument
6791fb62fb0SOlivier Houchard  * passed to ck_hs_apply. If a non-NULL pointer is passed to the first argument
6801fb62fb0SOlivier Houchard  * and the return value of the apply function is NULL, then the pre-existing
6811fb62fb0SOlivier Houchard  * value is deleted. If the return pointer is the same as the one passed to the
6821fb62fb0SOlivier Houchard  * apply function then no changes are made to the hash table.  If the first
6831fb62fb0SOlivier Houchard  * argument is non-NULL and the return pointer is different than that passed to
6841fb62fb0SOlivier Houchard  * the apply function, then the pre-existing value is replaced. For
6851fb62fb0SOlivier Houchard  * replacement, it is required that the value itself is identical to the
6861fb62fb0SOlivier Houchard  * previous value.
6871fb62fb0SOlivier Houchard  */
6881fb62fb0SOlivier Houchard bool
ck_hs_apply(struct ck_hs * hs,unsigned long h,const void * key,ck_hs_apply_fn_t * fn,void * cl)6891fb62fb0SOlivier Houchard ck_hs_apply(struct ck_hs *hs,
6901fb62fb0SOlivier Houchard     unsigned long h,
6911fb62fb0SOlivier Houchard     const void *key,
6921fb62fb0SOlivier Houchard     ck_hs_apply_fn_t *fn,
6931fb62fb0SOlivier Houchard     void *cl)
6941fb62fb0SOlivier Houchard {
6951fb62fb0SOlivier Houchard 	const void **slot, **first, *object, *delta, *insert;
6961fb62fb0SOlivier Houchard 	unsigned long n_probes;
6971fb62fb0SOlivier Houchard 	struct ck_hs_map *map;
6981fb62fb0SOlivier Houchard 
6991fb62fb0SOlivier Houchard restart:
7001fb62fb0SOlivier Houchard 	map = hs->map;
7011fb62fb0SOlivier Houchard 
7021fb62fb0SOlivier Houchard 	slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object, map->probe_limit, CK_HS_PROBE_INSERT);
7031fb62fb0SOlivier Houchard 	if (slot == NULL && first == NULL) {
7041fb62fb0SOlivier Houchard 		if (ck_hs_grow(hs, map->capacity << 1) == false)
7051fb62fb0SOlivier Houchard 			return false;
7061fb62fb0SOlivier Houchard 
7071fb62fb0SOlivier Houchard 		goto restart;
7081fb62fb0SOlivier Houchard 	}
7091fb62fb0SOlivier Houchard 
7101fb62fb0SOlivier Houchard 	delta = fn(CK_CC_DECONST_PTR(object), cl);
7111fb62fb0SOlivier Houchard 	if (delta == NULL) {
7121fb62fb0SOlivier Houchard 		/*
7131fb62fb0SOlivier Houchard 		 * The apply function has requested deletion. If the object doesn't exist,
7141fb62fb0SOlivier Houchard 		 * then exit early.
7151fb62fb0SOlivier Houchard 		 */
7161fb62fb0SOlivier Houchard 		if (CK_CC_UNLIKELY(object == NULL))
7171fb62fb0SOlivier Houchard 			return true;
7181fb62fb0SOlivier Houchard 
7191fb62fb0SOlivier Houchard 		/* Otherwise, mark slot as deleted. */
7201fb62fb0SOlivier Houchard 		ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
7211fb62fb0SOlivier Houchard 		map->n_entries--;
7221fb62fb0SOlivier Houchard 		map->tombstones++;
7231fb62fb0SOlivier Houchard 		return true;
7241fb62fb0SOlivier Houchard 	}
7251fb62fb0SOlivier Houchard 
7261fb62fb0SOlivier Houchard 	/* The apply function has not requested hash set modification so exit early. */
7271fb62fb0SOlivier Houchard 	if (delta == object)
7281fb62fb0SOlivier Houchard 		return true;
7291fb62fb0SOlivier Houchard 
7301fb62fb0SOlivier Houchard 	/* A modification or insertion has been requested. */
7311fb62fb0SOlivier Houchard 	ck_hs_map_bound_set(map, h, n_probes);
7321fb62fb0SOlivier Houchard 
7331fb62fb0SOlivier Houchard 	insert = ck_hs_marshal(hs->mode, delta, h);
7341fb62fb0SOlivier Houchard 	if (first != NULL) {
7351fb62fb0SOlivier Houchard 		/*
7361fb62fb0SOlivier Houchard 		 * This follows the same semantics as ck_hs_set, please refer to that
7371fb62fb0SOlivier Houchard 		 * function for documentation.
7381fb62fb0SOlivier Houchard 		 */
7391fb62fb0SOlivier Houchard 		ck_pr_store_ptr(first, insert);
7401fb62fb0SOlivier Houchard 
7411fb62fb0SOlivier Houchard 		if (object != NULL) {
7421fb62fb0SOlivier Houchard 			ck_hs_map_signal(map, h);
7431fb62fb0SOlivier Houchard 			ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
7441fb62fb0SOlivier Houchard 		}
7451fb62fb0SOlivier Houchard 	} else {
7461fb62fb0SOlivier Houchard 		/*
7471fb62fb0SOlivier Houchard 		 * If we are storing into same slot, then atomic store is sufficient
7481fb62fb0SOlivier Houchard 		 * for replacement.
7491fb62fb0SOlivier Houchard 		 */
7501fb62fb0SOlivier Houchard 		ck_pr_store_ptr(slot, insert);
7511fb62fb0SOlivier Houchard 	}
7521fb62fb0SOlivier Houchard 
7531fb62fb0SOlivier Houchard 	if (object == NULL)
7541fb62fb0SOlivier Houchard 		ck_hs_map_postinsert(hs, map);
7551fb62fb0SOlivier Houchard 
7561fb62fb0SOlivier Houchard 	return true;
7571fb62fb0SOlivier Houchard }
7581fb62fb0SOlivier Houchard 
7591fb62fb0SOlivier Houchard bool
ck_hs_set(struct ck_hs * hs,unsigned long h,const void * key,void ** previous)7601fb62fb0SOlivier Houchard ck_hs_set(struct ck_hs *hs,
7611fb62fb0SOlivier Houchard     unsigned long h,
7621fb62fb0SOlivier Houchard     const void *key,
7631fb62fb0SOlivier Houchard     void **previous)
7641fb62fb0SOlivier Houchard {
7651fb62fb0SOlivier Houchard 	const void **slot, **first, *object, *insert;
7661fb62fb0SOlivier Houchard 	unsigned long n_probes;
7671fb62fb0SOlivier Houchard 	struct ck_hs_map *map;
7681fb62fb0SOlivier Houchard 
7691fb62fb0SOlivier Houchard 	*previous = NULL;
7701fb62fb0SOlivier Houchard 
7711fb62fb0SOlivier Houchard restart:
7721fb62fb0SOlivier Houchard 	map = hs->map;
7731fb62fb0SOlivier Houchard 
7741fb62fb0SOlivier Houchard 	slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object, map->probe_limit, CK_HS_PROBE_INSERT);
7751fb62fb0SOlivier Houchard 	if (slot == NULL && first == NULL) {
7761fb62fb0SOlivier Houchard 		if (ck_hs_grow(hs, map->capacity << 1) == false)
7771fb62fb0SOlivier Houchard 			return false;
7781fb62fb0SOlivier Houchard 
7791fb62fb0SOlivier Houchard 		goto restart;
7801fb62fb0SOlivier Houchard 	}
7811fb62fb0SOlivier Houchard 
7821fb62fb0SOlivier Houchard 	ck_hs_map_bound_set(map, h, n_probes);
7831fb62fb0SOlivier Houchard 	insert = ck_hs_marshal(hs->mode, key, h);
7841fb62fb0SOlivier Houchard 
7851fb62fb0SOlivier Houchard 	if (first != NULL) {
7861fb62fb0SOlivier Houchard 		/* If an earlier bucket was found, then store entry there. */
7871fb62fb0SOlivier Houchard 		ck_pr_store_ptr(first, insert);
7881fb62fb0SOlivier Houchard 
7891fb62fb0SOlivier Houchard 		/*
7901fb62fb0SOlivier Houchard 		 * If a duplicate key was found, then delete it after
7911fb62fb0SOlivier Houchard 		 * signaling concurrent probes to restart. Optionally,
7921fb62fb0SOlivier Houchard 		 * it is possible to install tombstone after grace
7931fb62fb0SOlivier Houchard 		 * period if we can guarantee earlier position of
7941fb62fb0SOlivier Houchard 		 * duplicate key.
7951fb62fb0SOlivier Houchard 		 */
7961fb62fb0SOlivier Houchard 		if (object != NULL) {
7971fb62fb0SOlivier Houchard 			ck_hs_map_signal(map, h);
7981fb62fb0SOlivier Houchard 			ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
7991fb62fb0SOlivier Houchard 		}
8001fb62fb0SOlivier Houchard 	} else {
8011fb62fb0SOlivier Houchard 		/*
8021fb62fb0SOlivier Houchard 		 * If we are storing into same slot, then atomic store is sufficient
8031fb62fb0SOlivier Houchard 		 * for replacement.
8041fb62fb0SOlivier Houchard 		 */
8051fb62fb0SOlivier Houchard 		ck_pr_store_ptr(slot, insert);
8061fb62fb0SOlivier Houchard 	}
8071fb62fb0SOlivier Houchard 
8081fb62fb0SOlivier Houchard 	if (object == NULL)
8091fb62fb0SOlivier Houchard 		ck_hs_map_postinsert(hs, map);
8101fb62fb0SOlivier Houchard 
8111fb62fb0SOlivier Houchard 	*previous = CK_CC_DECONST_PTR(object);
8121fb62fb0SOlivier Houchard 	return true;
8131fb62fb0SOlivier Houchard }
8141fb62fb0SOlivier Houchard 
8151fb62fb0SOlivier Houchard 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)8161fb62fb0SOlivier Houchard ck_hs_put_internal(struct ck_hs *hs,
8171fb62fb0SOlivier Houchard     unsigned long h,
8181fb62fb0SOlivier Houchard     const void *key,
8191fb62fb0SOlivier Houchard     enum ck_hs_probe_behavior behavior)
8201fb62fb0SOlivier Houchard {
8211fb62fb0SOlivier Houchard 	const void **slot, **first, *object, *insert;
8221fb62fb0SOlivier Houchard 	unsigned long n_probes;
8231fb62fb0SOlivier Houchard 	struct ck_hs_map *map;
8241fb62fb0SOlivier Houchard 
8251fb62fb0SOlivier Houchard restart:
8261fb62fb0SOlivier Houchard 	map = hs->map;
8271fb62fb0SOlivier Houchard 
8281fb62fb0SOlivier Houchard 	slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object,
8291fb62fb0SOlivier Houchard 	    map->probe_limit, behavior);
8301fb62fb0SOlivier Houchard 
8311fb62fb0SOlivier Houchard 	if (slot == NULL && first == NULL) {
8321fb62fb0SOlivier Houchard 		if (ck_hs_grow(hs, map->capacity << 1) == false)
8331fb62fb0SOlivier Houchard 			return false;
8341fb62fb0SOlivier Houchard 
8351fb62fb0SOlivier Houchard 		goto restart;
8361fb62fb0SOlivier Houchard 	}
8371fb62fb0SOlivier Houchard 
8381fb62fb0SOlivier Houchard 	/* Fail operation if a match was found. */
8391fb62fb0SOlivier Houchard 	if (object != NULL)
8401fb62fb0SOlivier Houchard 		return false;
8411fb62fb0SOlivier Houchard 
8421fb62fb0SOlivier Houchard 	ck_hs_map_bound_set(map, h, n_probes);
8431fb62fb0SOlivier Houchard 	insert = ck_hs_marshal(hs->mode, key, h);
8441fb62fb0SOlivier Houchard 
8451fb62fb0SOlivier Houchard 	if (first != NULL) {
8461fb62fb0SOlivier Houchard 		/* Insert key into first bucket in probe sequence. */
8471fb62fb0SOlivier Houchard 		ck_pr_store_ptr(first, insert);
8481fb62fb0SOlivier Houchard 	} else {
8491fb62fb0SOlivier Houchard 		/* An empty slot was found. */
8501fb62fb0SOlivier Houchard 		ck_pr_store_ptr(slot, insert);
8511fb62fb0SOlivier Houchard 	}
8521fb62fb0SOlivier Houchard 
8531fb62fb0SOlivier Houchard 	ck_hs_map_postinsert(hs, map);
8541fb62fb0SOlivier Houchard 	return true;
8551fb62fb0SOlivier Houchard }
8561fb62fb0SOlivier Houchard 
8571fb62fb0SOlivier Houchard bool
ck_hs_put(struct ck_hs * hs,unsigned long h,const void * key)8581fb62fb0SOlivier Houchard ck_hs_put(struct ck_hs *hs,
8591fb62fb0SOlivier Houchard     unsigned long h,
8601fb62fb0SOlivier Houchard     const void *key)
8611fb62fb0SOlivier Houchard {
8621fb62fb0SOlivier Houchard 
8631fb62fb0SOlivier Houchard 	return ck_hs_put_internal(hs, h, key, CK_HS_PROBE_INSERT);
8641fb62fb0SOlivier Houchard }
8651fb62fb0SOlivier Houchard 
8661fb62fb0SOlivier Houchard bool
ck_hs_put_unique(struct ck_hs * hs,unsigned long h,const void * key)8671fb62fb0SOlivier Houchard ck_hs_put_unique(struct ck_hs *hs,
8681fb62fb0SOlivier Houchard     unsigned long h,
8691fb62fb0SOlivier Houchard     const void *key)
8701fb62fb0SOlivier Houchard {
8711fb62fb0SOlivier Houchard 
8721fb62fb0SOlivier Houchard 	return ck_hs_put_internal(hs, h, key, CK_HS_PROBE_TOMBSTONE);
8731fb62fb0SOlivier Houchard }
8741fb62fb0SOlivier Houchard 
8751fb62fb0SOlivier Houchard void *
ck_hs_get(struct ck_hs * hs,unsigned long h,const void * key)8761fb62fb0SOlivier Houchard ck_hs_get(struct ck_hs *hs,
8771fb62fb0SOlivier Houchard     unsigned long h,
8781fb62fb0SOlivier Houchard     const void *key)
8791fb62fb0SOlivier Houchard {
8801fb62fb0SOlivier Houchard 	const void **first, *object;
8811fb62fb0SOlivier Houchard 	struct ck_hs_map *map;
8821fb62fb0SOlivier Houchard 	unsigned long n_probes;
8831fb62fb0SOlivier Houchard 	unsigned int g, g_p, probe;
8841fb62fb0SOlivier Houchard 	unsigned int *generation;
8851fb62fb0SOlivier Houchard 
8861fb62fb0SOlivier Houchard 	do {
8871fb62fb0SOlivier Houchard 		map = ck_pr_load_ptr(&hs->map);
8881fb62fb0SOlivier Houchard 		generation = &map->generation[h & CK_HS_G_MASK];
8891fb62fb0SOlivier Houchard 		g = ck_pr_load_uint(generation);
8901fb62fb0SOlivier Houchard 		probe  = ck_hs_map_bound_get(map, h);
8911fb62fb0SOlivier Houchard 		ck_pr_fence_load();
8921fb62fb0SOlivier Houchard 
8931fb62fb0SOlivier Houchard 		ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object, probe, CK_HS_PROBE);
8941fb62fb0SOlivier Houchard 
8951fb62fb0SOlivier Houchard 		ck_pr_fence_load();
8961fb62fb0SOlivier Houchard 		g_p = ck_pr_load_uint(generation);
8971fb62fb0SOlivier Houchard 	} while (g != g_p);
8981fb62fb0SOlivier Houchard 
8991fb62fb0SOlivier Houchard 	return CK_CC_DECONST_PTR(object);
9001fb62fb0SOlivier Houchard }
9011fb62fb0SOlivier Houchard 
9021fb62fb0SOlivier Houchard void *
ck_hs_remove(struct ck_hs * hs,unsigned long h,const void * key)9031fb62fb0SOlivier Houchard ck_hs_remove(struct ck_hs *hs,
9041fb62fb0SOlivier Houchard     unsigned long h,
9051fb62fb0SOlivier Houchard     const void *key)
9061fb62fb0SOlivier Houchard {
9071fb62fb0SOlivier Houchard 	const void **slot, **first, *object;
9081fb62fb0SOlivier Houchard 	struct ck_hs_map *map = hs->map;
9091fb62fb0SOlivier Houchard 	unsigned long n_probes;
9101fb62fb0SOlivier Houchard 
9111fb62fb0SOlivier Houchard 	slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object,
9121fb62fb0SOlivier Houchard 	    ck_hs_map_bound_get(map, h), CK_HS_PROBE);
9131fb62fb0SOlivier Houchard 	if (object == NULL)
9141fb62fb0SOlivier Houchard 		return NULL;
9151fb62fb0SOlivier Houchard 
9161fb62fb0SOlivier Houchard 	ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
9171fb62fb0SOlivier Houchard 	map->n_entries--;
9181fb62fb0SOlivier Houchard 	map->tombstones++;
9191fb62fb0SOlivier Houchard 	return CK_CC_DECONST_PTR(object);
9201fb62fb0SOlivier Houchard }
9211fb62fb0SOlivier Houchard 
9221fb62fb0SOlivier Houchard 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)9231fb62fb0SOlivier Houchard ck_hs_move(struct ck_hs *hs,
9241fb62fb0SOlivier Houchard     struct ck_hs *source,
9251fb62fb0SOlivier Houchard     ck_hs_hash_cb_t *hf,
9261fb62fb0SOlivier Houchard     ck_hs_compare_cb_t *compare,
9271fb62fb0SOlivier Houchard     struct ck_malloc *m)
9281fb62fb0SOlivier Houchard {
9291fb62fb0SOlivier Houchard 
9301fb62fb0SOlivier Houchard 	if (m == NULL || m->malloc == NULL || m->free == NULL || hf == NULL)
9311fb62fb0SOlivier Houchard 		return false;
9321fb62fb0SOlivier Houchard 
9331fb62fb0SOlivier Houchard 	hs->mode = source->mode;
9341fb62fb0SOlivier Houchard 	hs->seed = source->seed;
9351fb62fb0SOlivier Houchard 	hs->map = source->map;
9361fb62fb0SOlivier Houchard 	hs->m = m;
9371fb62fb0SOlivier Houchard 	hs->hf = hf;
9381fb62fb0SOlivier Houchard 	hs->compare = compare;
9391fb62fb0SOlivier Houchard 	return true;
9401fb62fb0SOlivier Houchard }
9411fb62fb0SOlivier Houchard 
9421fb62fb0SOlivier Houchard 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)9431fb62fb0SOlivier Houchard ck_hs_init(struct ck_hs *hs,
9441fb62fb0SOlivier Houchard     unsigned int mode,
9451fb62fb0SOlivier Houchard     ck_hs_hash_cb_t *hf,
9461fb62fb0SOlivier Houchard     ck_hs_compare_cb_t *compare,
9471fb62fb0SOlivier Houchard     struct ck_malloc *m,
9481fb62fb0SOlivier Houchard     unsigned long n_entries,
9491fb62fb0SOlivier Houchard     unsigned long seed)
9501fb62fb0SOlivier Houchard {
9511fb62fb0SOlivier Houchard 
9521fb62fb0SOlivier Houchard 	if (m == NULL || m->malloc == NULL || m->free == NULL || hf == NULL)
9531fb62fb0SOlivier Houchard 		return false;
9541fb62fb0SOlivier Houchard 
9551fb62fb0SOlivier Houchard 	hs->m = m;
9561fb62fb0SOlivier Houchard 	hs->mode = mode;
9571fb62fb0SOlivier Houchard 	hs->seed = seed;
9581fb62fb0SOlivier Houchard 	hs->hf = hf;
9591fb62fb0SOlivier Houchard 	hs->compare = compare;
9601fb62fb0SOlivier Houchard 
9611fb62fb0SOlivier Houchard 	hs->map = ck_hs_map_create(hs, n_entries);
9621fb62fb0SOlivier Houchard 	return hs->map != NULL;
9631fb62fb0SOlivier Houchard }
964