unsigned int nr_readers;
unsigned int nr_writers;
+static unsigned int add_ratio = 50;
+static uint64_t key_mul = 1ULL;
+
+static int add_unique, add_replace;
+
static pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int leak_detection;
+static unsigned long test_nodes_allocated, test_nodes_freed;
+
void set_affinity(void)
{
cpu_set_t mask;
}
}
+static
+struct ja_test_node *node_alloc(void)
+{
+ struct ja_test_node *node;
+
+ node = calloc(sizeof(*node), 1);
+ if (leak_detection && node)
+ uatomic_inc(&test_nodes_allocated);
+ return node;
+}
+
+static
void free_node_cb(struct rcu_head *head)
{
struct ja_test_node *node =
caa_container_of(head, struct ja_test_node, node.head);
- free(node);
+ poison_free(node);
+ if (leak_detection)
+ uatomic_inc(&test_nodes_freed);
}
#if 0
printf(" [-c duration] (reader C.S. duration (in loops))\n");
printf(" [-v] (verbose output)\n");
printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
+ printf(" [-u] Add unique keys.\n");
+ printf(" [-s] Replace existing keys.\n");
printf(" [not -u nor -s] Add entries (supports redundant keys).\n");
- printf(" [-i] Add only (no removal).\n");
- printf(" [-k nr_nodes] Number of nodes to insert initially.\n");
+ printf(" [-r ratio] Add ratio (in %% of add+removal).\n");
+ printf(" [-k] Populate init nodes.\n");
printf(" [-R offset] Lookup pool offset.\n");
printf(" [-S offset] Write pool offset.\n");
printf(" [-T offset] Init pool offset.\n");
printf(" [-N size] Write pool size.\n");
printf(" [-O size] Init pool size.\n");
printf(" [-V] Validate lookups of init values (use with filled init pool, same lookup range, with different write range).\n");
- printf(" [-s] Do sanity test.\n");
+ printf(" [-t] Do sanity test.\n");
printf(" [-B] Key bits for multithread test (default: 32).\n");
+ printf(" [-m factor] Key multiplication factor.\n");
+ printf(" [-l] Memory leak detection.\n");
printf("\n\n");
}
/* Add keys */
printf("Test #1: add keys (8-bit).\n");
for (key = 0; key < 200; key++) {
- struct ja_test_node *node =
- calloc(sizeof(*node), 1);
+ struct ja_test_node *node = node_alloc();
ja_test_node_init(node, key);
rcu_read_lock();
printf("Test #1: add keys (16-bit).\n");
for (key = 0; key < 10000; key++) {
//for (key = 0; key < 65536; key+=256) {
- struct ja_test_node *node =
- calloc(sizeof(*node), 1);
+ struct ja_test_node *node = node_alloc();
ja_test_node_init(node, key);
rcu_read_lock();
for (i = 0; i < nr_dup; i++) {
zerocount = 0;
for (key = 0; key <= max_key && (key != 0 || zerocount < 1); key += 1ULL << (bits - 8)) {
- struct ja_test_node *node =
- calloc(sizeof(*node), 1);
+ struct ja_test_node *node = node_alloc();
ja_test_node_init(node, key);
rcu_read_lock();
/* note: only looking up ulong keys */
key = ((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % lookup_pool_size) + lookup_pool_offset;
+ key *= key_mul;
head = cds_ja_lookup(test_ja, key);
if (cds_hlist_empty(&head)) {
if (validate_lookup) {
} else {
URCU_TLS(lookup_ok)++;
}
- debug_yield_read();
+ rcu_debug_yield_read();
if (caa_unlikely(rduration))
loop_sleep(rduration);
rcu_read_unlock();
return ((void*)1);
}
+static
+int is_add(void)
+{
+ return ((unsigned int) rand_r(&URCU_TLS(rand_lookup)) % 100) < add_ratio;
+}
+
static
void *test_ja_rw_thr_writer(void *_count)
{
cmm_smp_mb();
for (;;) {
- if ((addremove == AR_ADD || add_only)
- || (addremove == AR_RANDOM && rand_r(&URCU_TLS(rand_lookup)) & 1)) {
- struct ja_test_node *node = malloc(sizeof(*node));
+ if ((addremove == AR_ADD)
+ || (addremove == AR_RANDOM && is_add())) {
+ struct ja_test_node *node = node_alloc();
+ struct cds_ja_node *ret_node;
/* note: only inserting ulong keys */
key = ((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % write_pool_size) + write_pool_offset;
+ key *= key_mul;
ja_test_node_init(node, key);
rcu_read_lock();
- ret = cds_ja_add(test_ja, key, &node->node);
- URCU_TLS(nr_add)++;
- rcu_read_unlock();
- if (ret) {
- fprintf(stderr, "Error (%d) adding node %" PRIu64 "\n",
- ret, key);
- assert(0);
+ if (add_unique) {
+ ret_node = cds_ja_add_unique(test_ja, key, &node->node);
+ if (ret_node != &node->node) {
+ free(node);
+ URCU_TLS(nr_addexist)++;
+ } else {
+ URCU_TLS(nr_add)++;
+ }
+ } else if (add_replace) {
+ assert(0); /* not implemented yet. */
+ } else {
+ ret = cds_ja_add(test_ja, key, &node->node);
+ if (ret) {
+ fprintf(stderr, "Error in cds_ja_add: %d\n", ret);
+ free(node);
+ } else {
+ URCU_TLS(nr_add)++;
+ }
}
+ rcu_read_unlock();
} else {
struct ja_test_node *node;
/* May delete */
/* note: only deleting ulong keys */
key = ((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % write_pool_size) + write_pool_offset;
+ key *= key_mul;
rcu_read_lock();
node = cds_hlist_first_entry_rcu(&head, struct ja_test_node, node.list);
if (node) {
ret = cds_ja_del(test_ja, key, &node->node);
- if (ret) {
+ if (!ret) {
call_rcu(&node->node.head, free_node_cb);
- URCU_TLS(nr_delnoent)++;
- } else {
URCU_TLS(nr_del)++;
+ } else {
+ URCU_TLS(nr_delnoent)++;
}
} else {
URCU_TLS(nr_delnoent)++;
}
+ rcu_read_unlock();
}
URCU_TLS(nr_writes)++;
int do_mt_populate_ja(void)
{
struct cds_hlist_head head;
- uint64_t key;
+ uint64_t iter;
int ret;
if (!init_populate)
printf("Starting rw test\n");
- for (key = init_pool_offset; key < init_pool_offset + init_pool_size; key++) {
- struct ja_test_node *node = malloc(sizeof(*node));
+ for (iter = init_pool_offset; iter < init_pool_offset + init_pool_size; iter++) {
+ struct ja_test_node *node = node_alloc();
+ uint64_t key;
/* note: only inserting ulong keys */
- key = (unsigned long) key;
+ key = (unsigned long) iter;
+ key *= key_mul;
ja_test_node_init(node, key);
rcu_read_lock();
ret = cds_ja_add(test_ja, key, &node->node);
tot_add_exist += count_writer[i].add_exist;
tot_remove += count_writer[i].remove;
}
+ rcu_thread_online_qsbr();
ret = cds_ja_destroy(test_ja, free_node_cb);
if (ret) {
fprintf(stderr, "Error destroying judy array\n");
goto end;
}
- rcu_thread_online_qsbr();
free(tid_reader);
free(tid_writer);
return ret;
}
+static
+int check_memory_leaks(void)
+{
+ unsigned long na, nf;
+
+ na = uatomic_read(&test_nodes_allocated);
+ nf = uatomic_read(&test_nodes_freed);
+ if (na != nf) {
+ fprintf(stderr, "Memory leak of %ld test nodes detected. Allocated: %lu, freed: %lu\n",
+ na - nf, na, nf);
+ return -1;
+ }
+ return 0;
+}
+
int main(int argc, char **argv)
{
int i, j, a, ret, err;
case 'v':
verbose_mode = 1;
break;
- case 'i':
- add_only = 1;
+ case 'r':
+ add_ratio = atoi(argv[++i]);
break;
case 'k':
- init_populate = atol(argv[++i]);
+ init_populate = 1;
break;
case 'R':
lookup_pool_offset = atol(argv[++i]);
case 'V':
validate_lookup = 1;
break;
- case 's':
+ case 't':
sanity_test = 1;
break;
case 'B':
key_bits = atol(argv[++i]);
break;
+ case 'm':
+ key_mul = atoll(argv[++i]);
+ break;
+ case 'u':
+ add_unique = 1;
+ break;
+ case 's':
+ add_replace = 1;
+ break;
+ case 'l':
+ leak_detection = 1;
+ break;
}
}
duration, nr_readers, nr_writers);
printf_verbose("Writer delay : %lu loops.\n", wdelay);
printf_verbose("Reader duration : %lu loops.\n", rduration);
- printf_verbose("Mode:%s.\n",
- add_only ? " add only" : " add/delete");
+ printf_verbose("Add ratio: %u%%.\n", add_ratio);
+ printf_verbose("Mode:%s%s.\n",
+ " add/remove",
+ add_unique ? " uniquify" : ( add_replace ? " replace" : " insert"));
+ printf_verbose("Key multiplication factor: %" PRIu64 ".\n", key_mul);
printf_verbose("Init pool size offset %lu size %lu.\n",
init_pool_offset, init_pool_size);
printf_verbose("Lookup pool size offset %lu size %lu.\n",
lookup_pool_offset, lookup_pool_size);
printf_verbose("Update pool size offset %lu size %lu.\n",
write_pool_offset, write_pool_size);
+ if (validate_lookup)
+ printf_verbose("Validating lookups.\n");
+ if (leak_detection)
+ printf_verbose("Memory leak dection activated.\n");
printf_verbose("thread %-6s, thread id : %lx, tid %lu\n",
"main", pthread_self(), (unsigned long)gettid());
if (sanity_test) {
ret = do_sanity_test();
- if (ret) {
- fprintf(stderr, "Error in sanity test\n");
- }
} else {
- do_mt_test();
+ ret = do_mt_test();
}
+ /* Wait for in-flight call_rcu free to complete for leak detection */
+ rcu_barrier();
+
+ ret |= check_memory_leaks();
+
rcu_unregister_thread();
free_all_cpu_call_rcu_data();
- return 0;
+
+ if (ret) {
+ printf("Test ended with error: %d\n", ret);
+ }
+ return ret;
#if 0
/*