diff --git a/.gitignore b/.gitignore index b0e966e..652de5e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,9 @@ build_unix/** *~ -TAGS -tags +*.o +libskiplist.so +libskiplist.a +tests/test +examples/example .direnv/ .idea/ -dist/autom4te.cache/ -dist/config.hin -dist/configure -dist/config.log diff --git a/Makefile b/Makefile index 957153f..8bb5d27 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ + CFLAGS = -Wall -Wextra -Wpedantic -Og -g -std=c99 -Iinclude/ -fPIC -TEST_FLAGS = -fsanitize=address,undefined +TEST_FLAGS = -std=c99 -Itests/ -fsanitize=address,undefined OBJS = skiplist.o STATIC_LIB = libskiplist.a @@ -25,14 +26,14 @@ $(STATIC_LIB): $(OBJS) $(SHARED_LIB): $(OBJS) $(CC) $(CFLAGS) -o $@ $? -shared +tests/%.o: tests/%.c + $(CC) $(CFLAGS) -c -o $@ $^ + tests: $(TESTS) ./tests/test -tests/test.c: tests/test.c tests/munit.c $(STATIC_LIB) - $(CC) $^ -o $@ $(CFLAGS) $(TEST_FLAGS) - -tests/munit.c: tests/munit.c - $(CC) $^ -o $@ $(CFLAGS) $(TEST_FLAGS) +tests/test: tests/test.o tests/munit.o $(STATIC_LIB) + $(CC) $^ -o $@ $(CFLAGS) $(TEST_FLAGS) -pthread examples: $(EXAMPLES) @@ -44,5 +45,6 @@ clean: rm -f $(STATIC_LIB) rm -f $(TESTS) rm -f $(EXAMPLES) + format: clang-format -i include/*.h src/*.c tests/*.c diff --git a/examples/example.c b/examples/example.c index 10d4b77..acb18c2 100644 --- a/examples/example.c +++ b/examples/example.c @@ -4,7 +4,7 @@ #include #include -#include "skiplist.h" +#include "../include/skiplist.h" // Define a node that contains key and value pair. struct my_node { @@ -67,7 +67,7 @@ main() // Define a query. struct my_node query; int min = 1, max = NUM_NODES - 1; - int k = min + random() / (RAND_MAX / (max - min + 1) + 1); + int k = min + (int)random() / (RAND_MAX / (max - min + 1) + 1); query.key = k; // Find a skiplist node `cursor`. sl_node *cursor = sl_find(&slist, &query.snode); diff --git a/src/skiplist.c b/src/skiplist.c index 128c01f..00fa116 100644 --- a/src/skiplist.c +++ b/src/skiplist.c @@ -24,84 +24,14 @@ * IN THE SOFTWARE. */ +#include #include #include #include "skiplist.h" #define __SL_DEBUG 0 -#if __SL_DEBUG > 0 -#include -#include -#include -#include -#include -#endif -#if __SL_DEBUG >= 1 -#define __SLD_ASSERT(cond) assert(cond) -#define __SLD_(b) b -#elif __SL_DEBUG >= 2 -#define __SLD_P(...) printf(__VA_ARGS__) -#elif __SL_DEBUG >= 3 -typedef struct dbg_node { - sl_node snode; - int value; -} dbg_node_t; - -inline void -__sld_rt_ins(int error_code, sl_node *node, int top_layer, int cur_layer) -{ - dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); - printf("[INS] retry (code %d) " - "%p (top %d, cur %d) %d\n", - error_code, node, top_layer, cur_layer, ddd->value); -} - -inline void -__sld_nc_ins(sl_node *node, sl_node *next_node, int top_layer, int cur_layer) -{ - dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); - dbg_node_t *ddd_next = sl_get_entry(next_node, dbg_node_t, snode); - - printf("[INS] next node changed, " - "%p %p (top %d, cur %d) %d %d\n", - node, next_node, top_layer, cur_layer, ddd->value, ddd_next->value); -} - -inline void -__sld_rt_rmv(int error_code, sl_node *node, int top_layer, int cur_layer) -{ - dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); - printf("[RMV] retry (code %d) " - "%p (top %d, cur %d) %d\n", - error_code, node, top_layer, cur_layer, ddd->value); -} - -inline void -__sld_nc_rmv(sl_node *node, sl_node *next_node, int top_layer, int cur_layer) -{ - dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); - dbg_node_t *ddd_next = sl_get_entry(next_node, dbg_node_t, snode); - - printf("[RMV] next node changed, " - "%p %p (top %d, cur %d) %d %d\n", - node, next_node, top_layer, cur_layer, ddd->value, ddd_next->value); -} - -inline void -__sld_bm(sl_node *node) -{ - dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); - printf("[RMV] node is being modified %d\n", ddd->value); -} - -#define __SLD_RT_INS(e, n, t, c) __sld_rt_ins(e, n, t, c) -#define __SLD_NC_INS(n, nn, t, c) __sld_nc_ins(n, nn, t, c) -#define __SLD_RT_RMV(e, n, t, c) __sld_rt_rmv(e, n, t, c) -#define __SLD_NC_RMV(n, nn, t, c) __sld_nc_rmv(n, nn, t, c) -#define __SLD_BM(n) __sld_bm(n) -#else #define __SLD_RT_INS(e, n, t, c) ((void)0) #define __SLD_NC_INS(n, nn, t, c) ((void)0) #define __SLD_RT_RMV(e, n, t, c) ((void)0) @@ -110,7 +40,6 @@ __sld_bm(sl_node *node) #define __SLD_ASSERT(cond) ((void)0) #define __SLD_P(...) ((void)0) #define __SLD_(b) ((void)0) -#endif #define YIELD() sched_yield() @@ -152,8 +81,8 @@ __sl_node_init(sl_node *node, size_t top_layer) if (top_layer > UINT8_MAX) top_layer = UINT8_MAX; - __SLD_ASSERT(node->is_fully_linked == false); - __SLD_ASSERT(node->being_modified == false); + assert(node->is_fully_linked == false); + assert(node->being_modified == false); ATM_STORE(node->is_fully_linked, bool_val); ATM_STORE(node->being_modified, bool_val); @@ -482,9 +411,9 @@ __sl_fnd_next(sl_node *cur_node, size_t layer, sl_node *to_find, bool *found) * * ... maybe resolved using RW spinlock (Aug 21, 2017). */ - __SLD_ASSERT(next_node); + assert(next_node); ATM_FETCH_ADD(next_node->ref_count, 1); - __SLD_ASSERT(next_node->top_layer >= layer); + assert(next_node->top_layer >= layer); } __sl_read_unlock_an(cur_node); @@ -495,7 +424,7 @@ __sl_fnd_next(sl_node *cur_node, size_t layer, sl_node *to_find, bool *found) temp = next_node; __sl_read_lock_an(temp); { - __SLD_ASSERT(next_node); + assert(next_node); if (!__sl_valid_node(temp)) { __sl_read_unlock_an(temp); ATM_FETCH_SUB(temp->ref_count, 1); @@ -505,7 +434,7 @@ __sl_fnd_next(sl_node *cur_node, size_t layer, sl_node *to_find, bool *found) ATM_LOAD(temp->next[layer], next_node); ATM_FETCH_ADD(next_node->ref_count, 1); nodes[num_nodes++] = temp; - __SLD_ASSERT(next_node->top_layer >= layer); + assert(next_node->top_layer >= layer); } __sl_read_unlock_an(temp); } @@ -565,7 +494,7 @@ __sl_clr_flags(sl_node **node_arr, unsigned long start_layer, bool exp = true; bool bool_false = false; if (!ATM_CAS(node_arr[layer]->being_modified, exp, bool_false)) { - __SLD_ASSERT(0); + assert(0); } } } @@ -586,8 +515,8 @@ __sl_valid_prev_next(sl_node *prev, sl_node *next) static int __sl_insert(sl_raw *slist, sl_node *node, bool no_dup) { - int i, cmp, error_code; - size_t layer, cur_layer, top_layer, locked_layer, sl_top_layer; + int i, cmp, error_code, cur_layer; + size_t layer, top_layer, locked_layer, sl_top_layer; bool bool_true = true; sl_node *exp, *cur_node, *next_node, *next_node_again, *prev[SKIPLIST_MAX_LAYER], *next[SKIPLIST_MAX_LAYER]; @@ -719,7 +648,7 @@ insert_retry:; __SLD_P("%02x ASSERT ins %p[%d] -> %p (expected %p)\n", (int)tid_hash, prev[layer], cur_layer, ATM_GET(prev[layer]->next[layer]), next[layer]); - __SLD_ASSERT(0); + assert(0); } __SLD_P("%02x ins %p[%d] -> %p -> %p\n", (int)tid_hash, prev[layer], layer, node, ATM_GET(node->next[layer])); @@ -980,7 +909,7 @@ erase_node_retry: __sl_clr_flags(prev, cur_layer + 1, top_layer); ATM_FETCH_SUB(temp->ref_count, 1); ATM_FETCH_SUB(next_node->ref_count, 1); - __SLD_ASSERT(0); + assert(0); } }); ATM_FETCH_SUB(temp->ref_count, 1); @@ -994,7 +923,7 @@ erase_node_retry: prev[cur_layer] = cur_node; /* 'next_node' and 'node' should not be the same, as 'removed' flag is already set. */ - __SLD_ASSERT(next_node != node); + assert(next_node != node); next[cur_layer] = next_node; /* check if prev node duplicates with upper layer */ @@ -1049,7 +978,7 @@ erase_node_retry: } while (cur_node != &slist->tail); } /* not exist in the skiplist, should not happen */ - __SLD_ASSERT(found_node_to_erase); + assert(found_node_to_erase); /* bottom layer => removal succeeded, mark this node unlinked */ __sl_write_lock_an(node); { @@ -1061,15 +990,15 @@ erase_node_retry: for (cur_layer = 0; cur_layer <= top_layer; ++cur_layer) { __sl_write_lock_an(prev[cur_layer]); exp = node; - __SLD_ASSERT(exp != next[cur_layer]); - __SLD_ASSERT(next[cur_layer]->is_fully_linked); + assert(exp != next[cur_layer]); + assert(next[cur_layer]->is_fully_linked); if (!ATM_CAS(prev[cur_layer]->next[cur_layer], exp, next[cur_layer])) { __SLD_P("%02x ASSERT rmv %p[%d] -> %p (node %p)\n", (int)tid_hash, prev[cur_layer], cur_layer, ATM_GET(prev[cur_layer]->next[cur_layer]), node); - __SLD_ASSERT(0); + assert(0); } - __SLD_ASSERT(next[cur_layer]->top_layer >= cur_layer); + assert(next[cur_layer]->top_layer >= cur_layer); __SLD_P("%02x rmv %p[%d] -> %p (node %p)\n", (int)tid_hash, prev[cur_layer], cur_layer, next[cur_layer], node); __sl_write_unlock_an(prev[cur_layer]); @@ -1135,7 +1064,7 @@ sl_is_valid_node(sl_node *node) int sl_is_safe_to_free(sl_node *node) { - uint16_t ref_count; + uint16_t ref_count; if (node->accessing_next) return 0; @@ -1168,7 +1097,7 @@ sl_grab_node(sl_node *node) void sl_release_node(sl_node *node) { - __SLD_ASSERT(node->ref_count); + assert(node->ref_count); ATM_FETCH_SUB(node->ref_count, 1); } diff --git a/tests/mt.c b/tests/mt.c new file mode 100644 index 0000000..273bae4 --- /dev/null +++ b/tests/mt.c @@ -0,0 +1,5 @@ + +static MunitTest mt_tests[] = { + { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } +}; + diff --git a/tests/scale.c b/tests/scale.c new file mode 100644 index 0000000..7efd936 --- /dev/null +++ b/tests/scale.c @@ -0,0 +1,5 @@ + +static MunitTest scale_tests[] = { + { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } +}; + diff --git a/tests/test.c b/tests/test.c index 14a8aee..18f7328 100644 --- a/tests/test.c +++ b/tests/test.c @@ -1,7 +1,340 @@ -#include "stdio.h" +/* + * skiplist is MIT-licensed, but for this file: + * + * To the extent possible under law, the author(s) of this file have + * waived all copyright and related or neighboring rights to this + * work. See for + * details. + */ + +#define MUNIT_ENABLE_ASSERT_ALIASES (1) + +#include + +#define __SL_DEBUG 0 +#if __SL_DEBUG > 0 +#include + +#include +#include +#include +#include +#endif +#if __SL_DEBUG >= 1 +#define __SLD_ASSERT(cond) assert(cond) +#define __SLD_(b) b +#elif __SL_DEBUG >= 2 +#define __SLD_P(...) printf(__VA_ARGS__) +#elif __SL_DEBUG >= 3 +typedef struct dbg_node { + sl_node snode; + int value; +} dbg_node_t; + +inline void +__sld_rt_ins(int error_code, sl_node *node, int top_layer, int cur_layer) +{ + dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); + printf("[INS] retry (code %d) " + "%p (top %d, cur %d) %d\n", + error_code, node, top_layer, cur_layer, ddd->value); +} + +inline void +__sld_nc_ins(sl_node *node, sl_node *next_node, int top_layer, int cur_layer) +{ + dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); + dbg_node_t *ddd_next = sl_get_entry(next_node, dbg_node_t, snode); + + printf("[INS] next node changed, " + "%p %p (top %d, cur %d) %d %d\n", + node, next_node, top_layer, cur_layer, ddd->value, ddd_next->value); +} + +inline void +__sld_rt_rmv(int error_code, sl_node *node, int top_layer, int cur_layer) +{ + dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); + printf("[RMV] retry (code %d) " + "%p (top %d, cur %d) %d\n", + error_code, node, top_layer, cur_layer, ddd->value); +} + +inline void +__sld_nc_rmv(sl_node *node, sl_node *next_node, int top_layer, int cur_layer) +{ + dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); + dbg_node_t *ddd_next = sl_get_entry(next_node, dbg_node_t, snode); + + printf("[RMV] next node changed, " + "%p %p (top %d, cur %d) %d %d\n", + node, next_node, top_layer, cur_layer, ddd->value, ddd_next->value); +} + +inline void +__sld_bm(sl_node *node) +{ + dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); + printf("[RMV] node is being modified %d\n", ddd->value); +} + +#define __SLD_RT_INS(e, n, t, c) __sld_rt_ins(e, n, t, c) +#define __SLD_NC_INS(n, nn, t, c) __sld_nc_ins(n, nn, t, c) +#define __SLD_RT_RMV(e, n, t, c) __sld_rt_rmv(e, n, t, c) +#define __SLD_NC_RMV(n, nn, t, c) __sld_nc_rmv(n, nn, t, c) +#define __SLD_BM(n) __sld_bm(n) +#endif + +#include "../include/skiplist.h" +#include "munit.h" + +#if defined(_MSC_VER) +#pragma warning(disable : 4127) +#endif + +struct user_data { + size_t n_ele; +}; + +typedef struct ex_node { + sl_node snode; + uint32_t key; + uint32_t value; +} ex_node_t; + +typedef sl_raw ex_sl_t; + +static int +uint32_key_cmp(sl_node *a, sl_node *b, void *aux) +{ + ex_node_t *aa, *bb; + (void)aux; + aa = sl_get_entry(a, ex_node_t, snode); + bb = sl_get_entry(b, ex_node_t, snode); + + if (aa->key < bb->key) + return -1; + if (aa->key > bb->key) + return 1; + return 0; +} + +static void * +test_api_setup(const MunitParameter params[], void *user_data) +{ + struct test_info *info = (struct test_info *)user_data; + (void)info; + (void)params; + + ex_sl_t *slist = calloc(sizeof(ex_sl_t), 1); + if (slist == NULL) + return NULL; + sl_init(slist, uint32_key_cmp); + return (void *)(uintptr_t)slist; +} + +static void +test_api_tear_down(void *fixture) +{ + ex_sl_t *slist = (ex_sl_t *)fixture; + assert_ptr_not_null(slist); + sl_node *cursor = sl_begin(slist); + while (cursor) { + assert_ptr_not_null(cursor); + ex_node_t *entry = sl_get_entry(cursor, ex_node_t, snode); + assert_ptr_not_null(entry); + assert_uint32(entry->key, ==, entry->value); + cursor = sl_next(slist, cursor); + sl_erase_node(slist, &entry->snode); + sl_release_node(&entry->snode); + sl_wait_for_free(&entry->snode); + sl_free_node(&entry->snode); + free(entry); + } + sl_free(slist); + free(fixture); +} + +static void * +test_api_insert_setup(const MunitParameter params[], void *user_data) { + return test_api_setup(params, user_data); +} +static void +test_api_insert_tear_down(void *fixture) { + test_api_tear_down(fixture); +} +static MunitResult +test_api_insert(const MunitParameter params[], void *data) +{ + sl_raw *slist = (sl_raw *)data; + (void)params; + assert_ptr_not_null(data); + int n = munit_rand_int_range(128, 4096); + int key = munit_rand_int_range(0, (((uint32_t)0) - 1) / 10); + while (n--) { + ex_node_t *node = (ex_node_t *)calloc(sizeof(ex_node_t), 1); + sl_init_node(&node->snode); + node->key = key; + node->value = key; + sl_insert(slist, &node->snode); + } + return MUNIT_OK; +} + +static void * +test_api_remove_setup(const MunitParameter params[], void *user_data) { + return test_api_setup(params, user_data); +} +static void +test_api_remove_tear_down(void *fixture) { + test_api_tear_down(fixture); +} +static MunitResult +test_api_remove(const MunitParameter params[], void *data) +{ + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; +} + +static void * +test_api_find_setup(const MunitParameter params[], void *user_data) { + return test_api_setup(params, user_data); +} +static void +test_api_find_tear_down(void *fixture) { + test_api_tear_down(fixture); +} +static MunitResult +test_api_find(const MunitParameter params[], void *data) +{ + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; +} + +static void * +test_api_update_setup(const MunitParameter params[], void *user_data) { + return test_api_setup(params, user_data); +} +static void +test_api_update_tear_down(void *fixture) { + test_api_tear_down(fixture); +} +static MunitResult +test_api_update(const MunitParameter params[], void *data) +{ + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; +} + +static void * +test_api_delete_setup(const MunitParameter params[], void *user_data) { + return test_api_setup(params, user_data); +} +static void +test_api_delete_tear_down(void *fixture) { + test_api_tear_down(fixture); +} +static MunitResult +test_api_delete(const MunitParameter params[], void *data) +{ + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; +} + +static void * +test_api_duplicates_setup(const MunitParameter params[], void *user_data) { + return test_api_setup(params, user_data); +} +static void +test_api_duplicates_tear_down(void *fixture) { + test_api_tear_down(fixture); +} +static MunitResult +test_api_duplicates(const MunitParameter params[], void *data) +{ + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; +} + +static void * +test_api_size_setup(const MunitParameter params[], void *user_data) { + return test_api_setup(params, user_data); +} +static void +test_api_size_tear_down(void *fixture) { + test_api_tear_down(fixture); +} +static MunitResult +test_api_size(const MunitParameter params[], void *data) +{ + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; +} + +static void * +test_api_iterators_setup(const MunitParameter params[], void *user_data) { + return test_api_setup(params, user_data); +} +static void +test_api_iterators_tear_down(void *fixture) { + test_api_tear_down(fixture); +} +static MunitResult +test_api_iterators(const MunitParameter params[], void *data) +{ + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; +} + +static MunitTest api_test_suite[] = { + { (char *)"/api/insert", test_api_insert, test_api_insert_setup, + test_api_insert_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/api/remove", test_api_remove, test_api_remove_setup, + test_api_remove_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/api/find", test_api_find, test_api_find_setup, + test_api_find_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/api/update", test_api_update, test_api_update_setup, + test_api_update_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/api/delete", test_api_delete, test_api_delete_setup, + test_api_delete_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/api/duplicates", test_api_duplicates, test_api_duplicates_setup, + test_api_duplicates_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/api/size", test_api_size, test_api_size_setup, + test_api_size_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/api/iterators", test_api_iterators, test_api_iterators_setup, + test_api_iterators_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } +}; + +#include "mt.c" +#include "scale.c" +static MunitSuite other_test_suite[] = { + { "/mt", mt_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE }, + { "/scale", scale_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE }, + { NULL, NULL, NULL, 0, MUNIT_SUITE_OPTION_NONE} +}; + +static const MunitSuite main_test_suite = { + (char *)"/api", api_test_suite,other_test_suite, 1, MUNIT_SUITE_OPTION_NONE +}; int -main(int argc, char **argv) +main(int argc, char *argv[MUNIT_ARRAY_PARAM(argc + 1)]) { - printf("Hello Nixers!\n"); + struct user_data info; + return munit_suite_main(&main_test_suite, (void *)&info, argc, argv); }