+ return 0;
+}
+
+enum urcu_ja_addremove {
+ AR_RANDOM = 0,
+ AR_ADD = 1,
+ AR_REMOVE = -1,
+}; /* 1: add, -1 remove, 0: random */
+
+static enum urcu_ja_addremove addremove; /* 1: add, -1 remove, 0: random */
+
+static
+void test_ja_rw_sigusr1_handler(int signo)
+{
+ switch (addremove) {
+ case AR_ADD:
+ printf("Add/Remove: random.\n");
+ addremove = AR_RANDOM;
+ break;
+ case AR_RANDOM:
+ printf("Add/Remove: remove only.\n");
+ addremove = AR_REMOVE;
+ break;
+ case AR_REMOVE:
+ printf("Add/Remove: add only.\n");
+ addremove = AR_ADD;
+ break;
+ }
+}
+
+static
+void *test_ja_rw_thr_reader(void *_count)
+{
+ unsigned long long *count = _count;
+ struct cds_ja_node *ja_node;
+ uint64_t key;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+
+ URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL);
+
+ set_affinity();
+
+ rcu_register_thread();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ 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;
+ ja_node = cds_ja_lookup(test_ja, key);
+ if (!ja_node) {
+ if (validate_lookup) {
+ printf("[ERROR] Lookup cannot find initial node.\n");
+ exit(-1);
+ }
+ URCU_TLS(lookup_fail)++;
+ } else {
+ URCU_TLS(lookup_ok)++;
+ }
+ rcu_debug_yield_read();
+ if (caa_unlikely(rduration))
+ loop_sleep(rduration);
+ rcu_read_unlock();
+ URCU_TLS(nr_reads)++;
+ if (caa_unlikely(!test_duration_read()))
+ break;
+ if (caa_unlikely((URCU_TLS(nr_reads) & ((1 << 10) - 1)) == 0))
+ rcu_quiescent_state();
+ }
+
+ rcu_unregister_thread();
+
+ *count = URCU_TLS(nr_reads);
+ printf_verbose("thread_end %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+ printf_verbose("readid : %lx, lookupfail %lu, lookupok %lu\n",
+ pthread_self(), URCU_TLS(lookup_fail),
+ URCU_TLS(lookup_ok));
+ 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)
+{
+ struct wr_count *count = _count;
+ uint64_t key;
+ int ret;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+
+ URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL);
+
+ set_affinity();
+
+ rcu_register_thread();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ 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();
+ if (add_unique) {
+ ret_node = cds_ja_add_unique(test_ja, key, &node->node);
+ if (ret_node != &node->node) {
+ free_test_node(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_test_node(node);
+ } else {
+ URCU_TLS(nr_add)++;
+ }
+ }
+ rcu_read_unlock();
+ } else {
+ struct cds_ja_node *ja_node;
+ 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();
+
+ ja_node = cds_ja_lookup(test_ja, key);
+ /* Remove first entry */
+ if (ja_node) {
+ node = caa_container_of(ja_node,
+ struct ja_test_node, node);
+ ret = cds_ja_del(test_ja, key, &node->node);
+ if (!ret) {
+ rcu_free_test_node(node);
+ URCU_TLS(nr_del)++;
+ } else {
+ URCU_TLS(nr_delnoent)++;
+ }
+ } else {
+ URCU_TLS(nr_delnoent)++;
+ }
+ rcu_read_unlock();
+ }
+
+ URCU_TLS(nr_writes)++;
+ if (caa_unlikely(!test_duration_write()))
+ break;
+ if (caa_unlikely(wdelay))
+ loop_sleep(wdelay);
+ if (caa_unlikely((URCU_TLS(nr_writes) & ((1 << 10) - 1)) == 0))
+ rcu_quiescent_state();
+ }
+
+ rcu_unregister_thread();
+
+ printf_verbose("thread_end %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+ printf_verbose("info id %lx: nr_add %lu, nr_addexist %lu, nr_del %lu, "
+ "nr_delnoent %lu\n", pthread_self(), URCU_TLS(nr_add),
+ URCU_TLS(nr_addexist), URCU_TLS(nr_del),
+ URCU_TLS(nr_delnoent));
+ count->update_ops = URCU_TLS(nr_writes);
+ count->add = URCU_TLS(nr_add);
+ count->add_exist = URCU_TLS(nr_addexist);
+ count->remove = URCU_TLS(nr_del);
+ return ((void*)2);
+}
+
+static
+int do_mt_populate_ja(void)
+{
+ uint64_t iter;
+ int ret;
+
+ if (!init_populate)
+ return 0;
+
+ printf("Starting rw test\n");
+
+ 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) iter;
+ 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)++;
+ URCU_TLS(nr_writes)++;
+ rcu_read_unlock();
+ /* Hash table resize only occurs in call_rcu thread */
+ if (!(iter % 100))
+ rcu_quiescent_state();
+ if (ret) {
+ fprintf(stderr, "Error (%d) adding node %" PRIu64 "\n",
+ ret, key);
+ assert(0);
+ }
+ }
+ return 0;
+}
+
+static
+int do_mt_test(void)
+{
+ pthread_t *tid_reader, *tid_writer;
+ void *tret;
+ int ret, i, err;
+ unsigned long long *count_reader;
+ struct wr_count *count_writer;
+ unsigned long long tot_reads = 0, tot_writes = 0,
+ tot_add = 0, tot_add_exist = 0, tot_remove = 0;
+ unsigned int remain;
+
+ tid_reader = malloc(sizeof(*tid_reader) * nr_readers);
+ tid_writer = malloc(sizeof(*tid_writer) * nr_writers);
+ count_reader = malloc(sizeof(*count_reader) * nr_readers);
+ count_writer = malloc(sizeof(*count_writer) * nr_writers);
+
+ printf("Allocating Judy Array for %u-bit keys\n", key_bits);
+ test_ja = cds_ja_new(key_bits);
+ if (!test_ja) {
+ printf("Error allocating judy array.\n");
+ ret = -1;
+ goto end;
+ }
+
+ do_mt_populate_ja();
+
+ next_aff = 0;
+
+ for (i = 0; i < nr_readers; i++) {
+ err = pthread_create(&tid_reader[i],
+ NULL, test_ja_rw_thr_reader,
+ &count_reader[i]);
+ if (err != 0)
+ exit(1);
+ }
+ for (i = 0; i < nr_writers; i++) {
+ err = pthread_create(&tid_writer[i],
+ NULL, test_ja_rw_thr_writer,
+ &count_writer[i]);
+ if (err != 0)
+ exit(1);
+ }
+
+ cmm_smp_mb();
+
+ test_go = 1;
+
+ rcu_thread_offline_qsbr();
+
+ remain = duration;
+ do {
+ remain = sleep(remain);
+ } while (remain > 0);
+
+ test_stop = 1;
+
+ for (i = 0; i < nr_readers; i++) {
+ err = pthread_join(tid_reader[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_reads += count_reader[i];
+ }
+ for (i = 0; i < nr_writers; i++) {
+ err = pthread_join(tid_writer[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_writes += count_writer[i].update_ops;
+ tot_add += count_writer[i].add;
+ tot_add_exist += count_writer[i].add_exist;
+ tot_remove += count_writer[i].remove;
+ }
+ rcu_thread_online_qsbr();
+
+ ret = test_free_all_nodes(test_ja);
+ if (ret) {
+ fprintf(stderr, "Error freeing all nodes\n");
+ return -1;
+ }
+
+ ret = cds_ja_destroy(test_ja);
+ if (ret) {
+ fprintf(stderr, "Error destroying judy array\n");
+ goto end;
+ }
+
+ free(tid_reader);
+ free(tid_writer);
+ free(count_reader);
+ free(count_writer);
+ ret = 0;
+end:
+ 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;
+ uint64_t key;
+ struct sigaction act;
+
+ if (argc < 4) {
+ show_usage(argc, argv);
+ return -1;
+ }
+
+ err = sscanf(argv[1], "%u", &nr_readers);
+ if (err != 1) {
+ show_usage(argc, argv);
+ return -1;
+ }
+
+ err = sscanf(argv[2], "%u", &nr_writers);
+ if (err != 1) {
+ show_usage(argc, argv);
+ return -1;
+ }
+
+ err = sscanf(argv[3], "%lu", &duration);
+ if (err != 1) {
+ show_usage(argc, argv);
+ return -1;
+ }
+
+ for (i = 4; i < argc; i++) {