From 0d1e86d18ee58534bea976489276d75053279100 Mon Sep 17 00:00:00 2001 From: Riley Berton Date: Thu, 17 Aug 2017 10:07:45 -0400 Subject: [PATCH] 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. --- include/ck_hs.h | 4 +- regressions/ck_hs/validate/serial.c | 60 +++++++++++++++++++++++++---- src/ck_hs.c | 43 ++++++++++++++------- 3 files changed, 86 insertions(+), 21 deletions(-) diff --git a/include/ck_hs.h b/include/ck_hs.h index b3eb046..3c12b6e 100644 --- a/include/ck_hs.h +++ b/include/ck_hs.h @@ -100,10 +100,11 @@ struct ck_hs_stat { struct ck_hs_iterator { void **cursor; unsigned long offset; + struct ck_hs_map *map; }; 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. */ #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 *); 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_spmc(ck_hs_t *, ck_hs_iterator_t *, void **); bool ck_hs_move(ck_hs_t *, ck_hs_t *, ck_hs_hash_cb_t *, ck_hs_compare_cb_t *, struct ck_malloc *); bool ck_hs_init(ck_hs_t *, unsigned int, ck_hs_hash_cb_t *, diff --git a/regressions/ck_hs/validate/serial.c b/regressions/ck_hs/validate/serial.c index a16fc82..21cb13a 100644 --- a/regressions/ck_hs/validate/serial.c +++ b/regressions/ck_hs/validate/serial.c @@ -5,9 +5,9 @@ * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * 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. - * 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 * documentation and/or other materials provided with the distribution. * @@ -58,11 +58,11 @@ static struct ck_malloc my_allocator = { }; const char *test[] = { "Samy", "Al", "Bahra", "dances", "in", "the", "wind.", "Once", - "upon", "a", "time", "his", "gypsy", "ate", "one", "itsy", - "bitsy", "spider.", "What", "goes", "up", "must", - "come", "down.", "What", "is", "down", "stays", - "down.", "A", "B", "C", "D", "E", "F", "G", "H", - "I", "J", "K", "L", "M", "N", "O", "P", "Q" }; + "upon", "a", "time", "his", "gypsy", "ate", "one", "itsy", + "bitsy", "spider.", "What", "goes", "up", "must", + "come", "down.", "What", "is", "down", "stays", + "down.", "A", "B", "C", "D", "E", "F", "G", "H", + "I", "J", "K", "L", "M", "N", "O", "P", "Q" }; const char *negative = "negative"; @@ -136,6 +136,7 @@ run_test(unsigned int is, unsigned int ad) size_t i, j; const char *blob = "#blobs"; 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) 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. */ ck_hs_grow(&hs[j], 128); for (i = 0; i < sizeof(test) / sizeof(*test); i++) { diff --git a/src/ck_hs.c b/src/ck_hs.c index 31510ec..86d04ed 100644 --- a/src/ck_hs.c +++ b/src/ck_hs.c @@ -105,21 +105,10 @@ ck_hs_map_signal(struct ck_hs_map *map, unsigned long h) return; } -void -ck_hs_iterator_init(struct ck_hs_iterator *iterator) +static bool +_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; - if (i->offset >= map->capacity) return false; @@ -129,6 +118,8 @@ ck_hs_next(struct ck_hs *hs, struct ck_hs_iterator *i, void **key) #ifdef CK_HS_PP if (hs->mode & CK_HS_MODE_OBJECT) value = CK_HS_VMA(value); +#else + (void)hs; // avoid unused param warning #endif i->offset++; *key = value; @@ -139,6 +130,32 @@ ck_hs_next(struct ck_hs *hs, struct ck_hs_iterator *i, void **key) 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 ck_hs_stat(struct ck_hs *hs, struct ck_hs_stat *st) {