ck_hs: add ck_hs_next_spmc

Memoize the map into ck_hs_iterator_t to make iteration more safe in the face of growth or shrinkage of the map.  Tests for same.

Work from Riley Berton.
ck_pring
Riley Berton 7 years ago committed by Samy Al Bahra
parent 4a8957f727
commit 0d1e86d18e

@ -100,10 +100,11 @@ struct ck_hs_stat {
struct ck_hs_iterator { struct ck_hs_iterator {
void **cursor; void **cursor;
unsigned long offset; unsigned long offset;
struct ck_hs_map *map;
}; };
typedef struct ck_hs_iterator ck_hs_iterator_t; typedef struct ck_hs_iterator ck_hs_iterator_t;
#define CK_HS_ITERATOR_INITIALIZER { NULL, 0 } #define CK_HS_ITERATOR_INITIALIZER { NULL, 0, NULL }
/* Convenience wrapper to table hash function. */ /* Convenience wrapper to table hash function. */
#define CK_HS_HASH(T, F, K) F((K), (T)->seed) #define CK_HS_HASH(T, F, K) F((K), (T)->seed)
@ -112,6 +113,7 @@ typedef void *ck_hs_apply_fn_t(void *, void *);
bool ck_hs_apply(ck_hs_t *, unsigned long, const void *, ck_hs_apply_fn_t *, void *); bool ck_hs_apply(ck_hs_t *, unsigned long, const void *, ck_hs_apply_fn_t *, void *);
void ck_hs_iterator_init(ck_hs_iterator_t *); void ck_hs_iterator_init(ck_hs_iterator_t *);
bool ck_hs_next(ck_hs_t *, ck_hs_iterator_t *, void **); bool ck_hs_next(ck_hs_t *, ck_hs_iterator_t *, void **);
bool ck_hs_next_spmc(ck_hs_t *, ck_hs_iterator_t *, void **);
bool ck_hs_move(ck_hs_t *, ck_hs_t *, ck_hs_hash_cb_t *, bool ck_hs_move(ck_hs_t *, ck_hs_t *, ck_hs_hash_cb_t *,
ck_hs_compare_cb_t *, struct ck_malloc *); ck_hs_compare_cb_t *, struct ck_malloc *);
bool ck_hs_init(ck_hs_t *, unsigned int, ck_hs_hash_cb_t *, bool ck_hs_init(ck_hs_t *, unsigned int, ck_hs_hash_cb_t *,

@ -5,9 +5,9 @@
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions * modification, are permitted provided that the following conditions
* are met: * are met:
* 1. Redistributions of source code must retain the above copyrighs * 1. Redistributions of source code must retain the above copyrights
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyrighs * 2. Redistributions in binary form must reproduce the above copyrights
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* *
@ -136,6 +136,7 @@ run_test(unsigned int is, unsigned int ad)
size_t i, j; size_t i, j;
const char *blob = "#blobs"; const char *blob = "#blobs";
unsigned long h; unsigned long h;
ck_hs_iterator_t it;
if (ck_hs_init(&hs[0], CK_HS_MODE_SPMC | CK_HS_MODE_OBJECT | ad, hs_hash, hs_compare, &my_allocator, is, 6602834) == false) if (ck_hs_init(&hs[0], CK_HS_MODE_SPMC | CK_HS_MODE_OBJECT | ad, hs_hash, hs_compare, &my_allocator, is, 6602834) == false)
ck_error("ck_hs_init\n"); ck_error("ck_hs_init\n");
@ -181,6 +182,51 @@ run_test(unsigned int is, unsigned int ad)
} }
} }
/* Test iteration */
if (j == 0) { // avoid the blob stuff as it's not in the test array
ck_hs_iterator_init(&it);
void *k = NULL;
int matches = 0;
int entries = 0;
while(ck_hs_next(&hs[j], &it, &k)) {
entries++;
for (i = 0; i < sizeof(test) / sizeof(*test); i++) {
int x = strcmp(test[i], (char *)k);
if (x == 0) {
matches++;
break;
}
}
}
if (entries != matches) {
ck_error("Iteration must match all elements, has: %d, matched: %d [%d]", entries, matches, is);
}
/* Now test iteration in the face of grows (spmc)*/
ck_hs_iterator_init(&it);
k = NULL;
matches = 0;
entries = 0;
while(ck_hs_next_spmc(&hs[j], &it, &k)) {
entries++;
for (i = 0; i < sizeof(test) / sizeof(*test); i++) {
int x = strcmp(test[i], (char *)k);
if (x == 0) {
matches++;
break;
}
}
if (entries == 20) {
ck_hs_grow(&hs[j], 128);
}
}
if (entries != matches) {
ck_error("After growth, iteration must match all elements, has: %d, matched: %d [%d]", entries, matches, is);
}
}
/* Test grow semantics. */ /* Test grow semantics. */
ck_hs_grow(&hs[j], 128); ck_hs_grow(&hs[j], 128);
for (i = 0; i < sizeof(test) / sizeof(*test); i++) { for (i = 0; i < sizeof(test) / sizeof(*test); i++) {

@ -105,21 +105,10 @@ ck_hs_map_signal(struct ck_hs_map *map, unsigned long h)
return; return;
} }
void static bool
ck_hs_iterator_init(struct ck_hs_iterator *iterator) _ck_hs_next(struct ck_hs *hs, struct ck_hs_map *map, struct ck_hs_iterator *i, void **key)
{ {
iterator->cursor = NULL;
iterator->offset = 0;
return;
}
bool
ck_hs_next(struct ck_hs *hs, struct ck_hs_iterator *i, void **key)
{
struct ck_hs_map *map = hs->map;
void *value; void *value;
if (i->offset >= map->capacity) if (i->offset >= map->capacity)
return false; return false;
@ -129,6 +118,8 @@ ck_hs_next(struct ck_hs *hs, struct ck_hs_iterator *i, void **key)
#ifdef CK_HS_PP #ifdef CK_HS_PP
if (hs->mode & CK_HS_MODE_OBJECT) if (hs->mode & CK_HS_MODE_OBJECT)
value = CK_HS_VMA(value); value = CK_HS_VMA(value);
#else
(void)hs; // avoid unused param warning
#endif #endif
i->offset++; i->offset++;
*key = value; *key = value;
@ -139,6 +130,32 @@ ck_hs_next(struct ck_hs *hs, struct ck_hs_iterator *i, void **key)
return false; return false;
} }
void
ck_hs_iterator_init(struct ck_hs_iterator *iterator)
{
iterator->cursor = NULL;
iterator->offset = 0;
iterator->map = NULL;
return;
}
bool
ck_hs_next(struct ck_hs *hs, struct ck_hs_iterator *i, void **key)
{
return _ck_hs_next(hs, hs->map, i, key);
}
bool
ck_hs_next_spmc(struct ck_hs *hs, struct ck_hs_iterator *i, void **key)
{
struct ck_hs_map *m = i->map;
if (m == NULL) {
m = i->map = ck_pr_load_ptr(&hs->map);
}
return _ck_hs_next(hs, m, i, key);
}
void void
ck_hs_stat(struct ck_hs *hs, struct ck_hs_stat *st) ck_hs_stat(struct ck_hs *hs, struct ck_hs_stat *st)
{ {

Loading…
Cancel
Save