urcu/uatomic.h
tests/api.h
-tests/urcutorture
-tests/urcutorture-yield
-tests/urcu-asm.S
-tests/test_rwlock_timing
-tests/test_urcu
-tests/test_urcu_dynamic_link
-tests/test_urcu_timing
-tests/test_urcu_yield
-tests/test_looplen
-tests/test_mutex
-tests/test_perthreadlock
-tests/test_perthreadlock_timing
-tests/test_qsbr
-tests/test_qsbr_dynamic_link
-tests/test_qsbr_gc
-tests/test_qsbr_lgc
-tests/test_qsbr_timing
-tests/test_rwlock
-tests/test_uatomic
-tests/test_urcu_gc
-tests/test_urcu_gc_mb
-tests/test_urcu_lgc
-tests/test_urcu_lgc_mb
-tests/test_urcu_mb
-tests/test_urcu_mb_defer
-tests/test_urcu_assign
-tests/test_urcu_assign_dynamic_link
-tests/test_urcu_bp
-tests/test_urcu_bp_dynamic_link
-tests/rcutorture_qsbr
-tests/rcutorture_urcu
-tests/rcutorture_urcu_bp
-tests/rcutorture_urcu_mb
-tests/rcutorture_urcu_qsbr
-tests/rcutorture_urcu_signal
-tests/test_cycles_per_loop
-tests/test_urcu_defer
-tests/test_urcu_hash
-tests/test_urcu_lfq
-tests/test_urcu_lfq_dynlink
-tests/test_urcu_lfs
-tests/test_urcu_lfs_dynlink
-tests/test_urcu_mb_gc
-tests/test_urcu_mb_lgc
-tests/test_urcu_qsbr
-tests/test_urcu_qsbr_dynamic_link
-tests/test_urcu_qsbr_gc
-tests/test_urcu_qsbr_lgc
-tests/test_urcu_qsbr_timing
-tests/test_urcu_signal
-tests/test_urcu_signal_dynamic_link
-tests/test_urcu_signal_gc
-tests/test_urcu_signal_lgc
-tests/test_urcu_signal_timing
-tests/test_urcu_signal_yield
-tests/test_urcu_wfq
-tests/test_urcu_wfq_dynlink
-tests/test_urcu_wfs
-tests/test_urcu_wfs_dynlink
-tests/test_urcu_fork
-tests/test_urcu_ja
-tests/test_urcu_ja_range
-tests/test_urcu_lfs_rcu
-tests/test_urcu_lfs_rcu_dynlink
-tests/test_urcu_multiflavor
-tests/test_urcu_multiflavor_dynlink
-tests/test_urcu_wfcq
-tests/test_urcu_wfcq_dynlink
-tests/*.log
+
+tests/unit/urcu-asm.S
+tests/unit/test_uatomic
+tests/unit/test_urcu_multiflavor
+tests/unit/test_urcu_multiflavor_dynlink
+
+tests/regression/rcutorture_qsbr
+tests/regression/rcutorture_urcu
+tests/regression/rcutorture_urcu_bp
+tests/regression/rcutorture_urcu_mb
+tests/regression/rcutorture_urcu_qsbr
+tests/regression/rcutorture_urcu_signal
+tests/regression/test_urcu_fork
+
+tests/benchmark/test_rwlock_timing
+tests/benchmark/test_urcu
+tests/benchmark/test_urcu_dynamic_link
+tests/benchmark/test_urcu_timing
+tests/benchmark/test_urcu_yield
+tests/benchmark/test_looplen
+tests/benchmark/test_mutex
+tests/benchmark/test_perthreadlock
+tests/benchmark/test_perthreadlock_timing
+tests/benchmark/test_qsbr
+tests/benchmark/test_qsbr_dynamic_link
+tests/benchmark/test_qsbr_gc
+tests/benchmark/test_qsbr_lgc
+tests/benchmark/test_qsbr_timing
+tests/benchmark/test_rwlock
+tests/benchmark/test_urcu_gc
+tests/benchmark/test_urcu_gc_mb
+tests/benchmark/test_urcu_lgc
+tests/benchmark/test_urcu_lgc_mb
+tests/benchmark/test_urcu_mb
+tests/benchmark/test_urcu_mb_defer
+tests/benchmark/test_urcu_assign
+tests/benchmark/test_urcu_assign_dynamic_link
+tests/benchmark/test_urcu_bp
+tests/benchmark/test_urcu_bp_dynamic_link
+tests/benchmark/test_cycles_per_loop
+tests/benchmark/test_urcu_defer
+tests/benchmark/test_urcu_hash
+tests/benchmark/test_urcu_lfq
+tests/benchmark/test_urcu_lfq_dynlink
+tests/benchmark/test_urcu_lfs
+tests/benchmark/test_urcu_lfs_dynlink
+tests/benchmark/test_urcu_mb_gc
+tests/benchmark/test_urcu_mb_lgc
+tests/benchmark/test_urcu_qsbr
+tests/benchmark/test_urcu_qsbr_dynamic_link
+tests/benchmark/test_urcu_qsbr_gc
+tests/benchmark/test_urcu_qsbr_lgc
+tests/benchmark/test_urcu_qsbr_timing
+tests/benchmark/test_urcu_signal
+tests/benchmark/test_urcu_signal_dynamic_link
+tests/benchmark/test_urcu_signal_gc
+tests/benchmark/test_urcu_signal_lgc
+tests/benchmark/test_urcu_signal_timing
+tests/benchmark/test_urcu_signal_yield
+tests/benchmark/test_urcu_wfq
+tests/benchmark/test_urcu_wfq_dynlink
+tests/benchmark/test_urcu_wfs
+tests/benchmark/test_urcu_wfs_dynlink
+tests/benchmark/test_urcu_ja
+tests/benchmark/test_urcu_ja_range
+tests/benchmark/test_urcu_lfs_rcu
+tests/benchmark/test_urcu_lfs_rcu_dynlink
+tests/benchmark/test_urcu_wfcq
+tests/benchmark/test_urcu_wfcq_dynlink
+
+tests/benchmark/*.log
*.so
doc/examples/urcu-flavors/qsbr
gpl-2.0.txt lgpl-2.1.txt lgpl-relicensing.txt \
LICENSE compat_arch_x86.c \
urcu-call-rcu-impl.h urcu-defer-impl.h \
- rculfhash-internal.h \
- $(top_srcdir)/tests/*.sh
+ rculfhash-internal.h
if COMPAT_ARCH
COMPAT=compat_arch_@ARCHTYPE@.c
dist_doc_DATA = README ChangeLog
dist_noinst_DATA = CodingStyle
+
+.PHONY: bench regtest
+bench:
+ cd tests && $(MAKE) $(AM_MAKEFLAGS) bench
+regtest:
+ cd tests && $(MAKE) $(AM_MAKEFLAGS) regtest
doc/Makefile
doc/examples/Makefile
tests/Makefile
+ tests/common/Makefile
+ tests/unit/Makefile
+ tests/benchmark/Makefile
+ tests/regression/Makefile
liburcu.pc
liburcu-bp.pc
liburcu-cds.pc
-AM_LDFLAGS=-lpthread
-AM_CFLAGS=-I$(top_srcdir) -I$(top_builddir) -g
+SUBDIRS = common unit benchmark regression
-noinst_PROGRAMS = test_urcu test_urcu_dynamic_link test_urcu_timing \
- test_urcu_signal test_urcu_signal_dynamic_link test_urcu_signal_timing \
- test_rwlock_timing test_rwlock test_perthreadlock_timing \
- test_perthreadlock test_urcu_yield test_urcu_signal_yield test_urcu_mb \
- test_urcu_qsbr_timing test_urcu_qsbr rcutorture_urcu rcutorture_urcu_signal \
- rcutorture_urcu_mb rcutorture_urcu_bp rcutorture_urcu_qsbr \
- test_mutex test_looplen test_urcu_gc test_urcu_signal_gc \
- test_urcu_lgc \
- test_urcu_mb_gc test_urcu_qsbr_gc test_urcu_qsbr_lgc test_urcu_signal_lgc \
- test_urcu_mb_lgc test_urcu_qsbr_dynamic_link test_urcu_defer \
- test_uatomic test_urcu_assign test_urcu_assign_dynamic_link \
- test_urcu_bp test_urcu_bp_dynamic_link test_cycles_per_loop \
- test_urcu_lfq test_urcu_wfq test_urcu_lfs test_urcu_wfs \
- test_urcu_lfs_rcu \
- test_urcu_wfcq \
- test_urcu_wfq_dynlink test_urcu_wfs_dynlink \
- test_urcu_wfcq_dynlink \
- test_urcu_lfq_dynlink test_urcu_lfs_dynlink test_urcu_hash \
- test_urcu_lfs_rcu_dynlink \
- test_urcu_multiflavor test_urcu_multiflavor_dynlink \
- test_urcu_fork
-noinst_HEADERS = rcutorture.h test_urcu_multiflavor.h cpuset.h thread-id.h
+.PHONY: bench regtest
-if COMPAT_ARCH
-COMPAT=$(top_srcdir)/compat_arch_@ARCHTYPE@.c
-else
-COMPAT=
-endif
-
-if COMPAT_FUTEX
-COMPAT+=$(top_srcdir)/compat_futex.c
-endif
-
-URCU=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT)
-URCU_QSBR=$(top_srcdir)/urcu-qsbr.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT)
-# URCU_MB uses urcu.c but -DRCU_MB must be defined
-URCU_MB=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT)
-# URCU_SIGNAL uses urcu.c but -DRCU_SIGNAL must be defined
-URCU_SIGNAL=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT)
-URCU_BP=$(top_srcdir)/urcu-bp.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT)
-URCU_DEFER=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT)
-
-URCU_COMMON_LIB=$(top_builddir)/liburcu-common.la
-URCU_LIB=$(top_builddir)/liburcu.la
-URCU_QSBR_LIB=$(top_builddir)/liburcu-qsbr.la
-URCU_MB_LIB=$(top_builddir)/liburcu-mb.la
-URCU_SIGNAL_LIB=$(top_builddir)/liburcu-signal.la
-URCU_BP_LIB=$(top_builddir)/liburcu-bp.la
-URCU_CDS_LIB=$(top_builddir)/liburcu-cds.la
-
-EXTRA_DIST = $(top_srcdir)/tests/api.h runall.sh runhash.sh
-
-test_urcu_SOURCES = test_urcu.c $(URCU)
-
-test_urcu_dynamic_link_SOURCES = test_urcu.c $(URCU)
-test_urcu_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
-
-test_urcu_timing_SOURCES = test_urcu_timing.c $(URCU)
-
-test_urcu_yield_SOURCES = test_urcu.c $(URCU)
-test_urcu_yield_CFLAGS = -DDEBUG_YIELD $(AM_CFLAGS)
-
-
-test_urcu_qsbr_SOURCES = test_urcu_qsbr.c $(URCU_QSBR)
-
-test_urcu_qsbr_timing_SOURCES = test_urcu_qsbr_timing.c $(URCU_QSBR)
-
-
-test_urcu_mb_SOURCES = test_urcu.c $(URCU_MB)
-test_urcu_mb_CFLAGS = -DRCU_MB $(AM_CFLAGS)
-
-
-test_urcu_signal_SOURCES = test_urcu.c $(URCU_SIGNAL)
-test_urcu_signal_CFLAGS = -DRCU_SIGNAL $(AM_CFLAGS)
-
-test_urcu_signal_dynamic_link_SOURCES = test_urcu.c $(URCU_SIGNAL)
-test_urcu_signal_dynamic_link_CFLAGS = -DRCU_SIGNAL -DDYNAMIC_LINK_TEST \
- $(AM_CFLAGS)
-
-test_urcu_signal_timing_SOURCES = test_urcu_timing.c $(URCU_SIGNAL)
-test_urcu_signal_timing_CFLAGS= -DRCU_SIGNAL $(AM_CFLAGS)
-
-test_urcu_signal_yield_SOURCES = test_urcu.c $(URCU_SIGNAL)
-test_urcu_signal_yield_CFLAGS = -DRCU_SIGNAL -DDEBUG_YIELD $(AM_CFLAGS)
-
-test_urcu_fork_SOURCES = test_urcu_fork.c $(URCU)
-
-test_rwlock_timing_SOURCES = test_rwlock_timing.c $(URCU_SIGNAL)
-
-test_rwlock_SOURCES = test_rwlock.c $(URCU_SIGNAL)
-
-test_perthreadlock_timing_SOURCES = test_perthreadlock_timing.c $(URCU_SIGNAL)
-
-test_perthreadlock_SOURCES = test_perthreadlock.c $(URCU_SIGNAL)
-
-
-rcutorture_urcu_SOURCES = urcutorture.c
-rcutorture_urcu_CFLAGS = -DRCU_MEMBARRIER $(AM_CFLAGS)
-rcutorture_urcu_LDADD = $(URCU)
-
-rcutorture_urcu_mb_SOURCES = urcutorture.c
-rcutorture_urcu_mb_CFLAGS = -DRCU_MB $(AM_CFLAGS)
-rcutorture_urcu_mb_LDADD = $(URCU_MB_LIB)
-
-rcutorture_urcu_qsbr_SOURCES = urcutorture.c
-rcutorture_urcu_qsbr_CFLAGS = -DTORTURE_QSBR -DRCU_QSBR $(AM_CFLAGS)
-rcutorture_urcu_qsbr_LDADD = $(URCU_QSBR_LIB)
-
-rcutorture_urcu_signal_SOURCES = urcutorture.c
-rcutorture_urcu_signal_CFLAGS = -DRCU_SIGNAL $(AM_CFLAGS)
-rcutorture_urcu_signal_LDADD = $(URCU_SIGNAL_LIB)
-
-rcutorture_urcu_bp_SOURCES = urcutorture.c
-rcutorture_urcu_bp_CFLAGS = -DRCU_BP $(AM_CFLAGS)
-rcutorture_urcu_bp_LDADD = $(URCU_BP_LIB)
-
-test_mutex_SOURCES = test_mutex.c $(URCU)
-
-test_looplen_SOURCES = test_looplen.c
-
-test_urcu_gc_SOURCES = test_urcu_gc.c $(URCU)
-
-test_urcu_signal_gc_SOURCES = test_urcu_gc.c $(URCU_SIGNAL)
-test_urcu_signal_gc_CFLAGS = -DRCU_SIGNAL $(AM_CFLAGS)
-
-test_urcu_mb_gc_SOURCES = test_urcu_gc.c $(URCU_MB)
-test_urcu_mb_gc_CFLAGS = -DRCU_MB $(AM_CFLAGS)
-
-test_urcu_qsbr_gc_SOURCES = test_urcu_qsbr_gc.c $(URCU_QSBR)
-
-test_urcu_qsbr_lgc_SOURCES = test_urcu_qsbr_gc.c $(URCU_QSBR)
-test_urcu_qsbr_lgc_CFLAGS = -DTEST_LOCAL_GC $(AM_CFLAGS)
-
-test_urcu_lgc_SOURCES = test_urcu_gc.c $(URCU)
-test_urcu_lgc_CFLAGS = -DTEST_LOCAL_GC $(AM_CFLAGS)
-
-test_urcu_signal_lgc_SOURCES = test_urcu_gc.c $(URCU_SIGNAL)
-test_urcu_signal_lgc_CFLAGS = -DRCU_SIGNAL -DTEST_LOCAL_GC $(AM_CFLAGS)
-
-test_urcu_mb_lgc_SOURCES = test_urcu_gc.c $(URCU_MB)
-test_urcu_mb_lgc_CFLAGS = -DTEST_LOCAL_GC -DRCU_MB $(AM_CFLAGS)
-
-test_urcu_qsbr_dynamic_link_SOURCES = test_urcu_qsbr.c $(URCU_QSBR)
-test_urcu_qsbr_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
-
-test_urcu_defer_SOURCES = test_urcu_defer.c $(URCU_DEFER)
-
-test_uatomic_SOURCES = test_uatomic.c $(COMPAT)
-
-test_cycles_per_loop_SOURCES = test_cycles_per_loop.c
-
-test_urcu_assign_SOURCES = test_urcu_assign.c $(URCU)
-
-test_urcu_assign_dynamic_link_SOURCES = test_urcu_assign.c $(URCU)
-test_urcu_assign_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
-
-test_urcu_bp_SOURCES = test_urcu_bp.c $(URCU_BP)
-
-test_urcu_bp_dynamic_link_SOURCES = test_urcu_bp.c $(URCU_BP)
-test_urcu_bp_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
-
-test_urcu_lfq_SOURCES = test_urcu_lfq.c $(URCU)
-test_urcu_lfq_LDADD = $(URCU_CDS_LIB)
-
-test_urcu_lfq_dynlink_SOURCES = test_urcu_lfq.c $(URCU)
-test_urcu_lfq_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
-test_urcu_lfq_dynlink_LDADD = $(URCU_CDS_LIB)
-
-test_urcu_wfq_SOURCES = test_urcu_wfq.c $(COMPAT)
-test_urcu_wfq_LDADD = $(URCU_COMMON_LIB)
-
-test_urcu_wfq_dynlink_SOURCES = test_urcu_wfq.c
-test_urcu_wfq_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
-test_urcu_wfq_dynlink_LDADD = $(URCU_COMMON_LIB)
-
-test_urcu_wfcq_SOURCES = test_urcu_wfcq.c $(COMPAT)
-test_urcu_wfcq_LDADD = $(URCU_COMMON_LIB)
-
-test_urcu_wfcq_dynlink_SOURCES = test_urcu_wfcq.c
-test_urcu_wfcq_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
-test_urcu_wfcq_dynlink_LDADD = $(URCU_COMMON_LIB)
-
-test_urcu_lfs_SOURCES = test_urcu_lfs.c $(URCU)
-test_urcu_lfs_LDADD = $(URCU_CDS_LIB)
-
-test_urcu_lfs_rcu_SOURCES = test_urcu_lfs_rcu.c $(URCU)
-test_urcu_lfs_rcu_LDADD = $(URCU_CDS_LIB)
-
-test_urcu_lfs_dynlink_SOURCES = test_urcu_lfs.c $(URCU)
-test_urcu_lfs_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
-test_urcu_lfs_dynlink_LDADD = $(URCU_CDS_LIB)
-
-test_urcu_lfs_rcu_dynlink_SOURCES = test_urcu_lfs_rcu.c $(URCU)
-test_urcu_lfs_rcu_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
-test_urcu_lfs_rcu_dynlink_LDADD = $(URCU_CDS_LIB)
-
-test_urcu_wfs_SOURCES = test_urcu_wfs.c $(COMPAT)
-test_urcu_wfs_LDADD = $(URCU_COMMON_LIB)
-
-test_urcu_wfs_dynlink_SOURCES = test_urcu_wfs.c
-test_urcu_wfs_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
-test_urcu_wfs_dynlink_LDADD = $(URCU_COMMON_LIB)
-
-test_urcu_hash_SOURCES = test_urcu_hash.c test_urcu_hash.h \
- test_urcu_hash_rw.c test_urcu_hash_unique.c $(COMPAT)
-test_urcu_hash_CFLAGS = -DRCU_QSBR $(AM_CFLAGS)
-test_urcu_hash_LDADD = $(URCU_QSBR_LIB) $(URCU_CDS_LIB)
-
-test_urcu_multiflavor_SOURCES = test_urcu_multiflavor.c \
- test_urcu_multiflavor-memb.c \
- test_urcu_multiflavor-mb.c \
- test_urcu_multiflavor-signal.c \
- test_urcu_multiflavor-qsbr.c \
- test_urcu_multiflavor-bp.c
-test_urcu_multiflavor_LDADD = $(URCU_LIB) $(URCU_MB_LIB) \
- $(URCU_SIGNAL_LIB) $(URCU_QSBR_LIB) $(URCU_BP_LIB)
-
-test_urcu_multiflavor_dynlink_SOURCES = test_urcu_multiflavor.c \
- test_urcu_multiflavor-memb.c \
- test_urcu_multiflavor-mb.c \
- test_urcu_multiflavor-signal.c \
- test_urcu_multiflavor-qsbr.c \
- test_urcu_multiflavor-bp.c
-test_urcu_multiflavor_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
-test_urcu_multiflavor_dynlink_LDADD = $(URCU_LIB) $(URCU_MB_LIB) \
- $(URCU_SIGNAL_LIB) $(URCU_QSBR_LIB) $(URCU_BP_LIB)
-
-urcutorture.c: api.h
-
-check-am:
- ./test_uatomic
- ./runall.sh
+bench:
+ cd benchmark && $(MAKE) $(AM_MAKEFLAGS) bench
+regtest:
+ cd regression && $(MAKE) $(AM_MAKEFLAGS) regtest
+++ /dev/null
-#ifndef _INCLUDE_API_H
-#define _INCLUDE_API_H
-
-#define _GNU_SOURCE
-#include "../config.h"
-
-/*
- * common.h: Common Linux kernel-isms.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; but version 2 of the License only due
- * to code included from the Linux kernel.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (c) 2006 Paul E. McKenney, IBM.
- *
- * Much code taken from the Linux kernel. For such code, the option
- * to redistribute under later versions of GPL might not be available.
- */
-
-#include <urcu/compiler.h>
-#include <urcu/arch.h>
-#include "cpuset.h"
-
-/*
- * Machine parameters.
- */
-
-#define ____cacheline_internodealigned_in_smp \
- __attribute__((__aligned__(CAA_CACHE_LINE_SIZE)))
-
-/*
- * api_pthreads.h: API mapping to pthreads environment.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version. However, please note that much
- * of the code in this file derives from the Linux kernel, and that such
- * code may not be available except under GPLv2.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (c) 2006 Paul E. McKenney, IBM.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <limits.h>
-#include <sys/types.h>
-#include <pthread.h>
-#include <sys/param.h>
-/* #include "atomic.h" */
-
-/*
- * Exclusive locking primitives.
- */
-
-typedef pthread_mutex_t spinlock_t;
-
-#define DEFINE_SPINLOCK(lock) spinlock_t lock = PTHREAD_MUTEX_INITIALIZER;
-#define __SPIN_LOCK_UNLOCKED(lockp) PTHREAD_MUTEX_INITIALIZER
-
-static void spin_lock_init(spinlock_t *sp)
-{
- if (pthread_mutex_init(sp, NULL) != 0) {
- perror("spin_lock_init:pthread_mutex_init");
- exit(-1);
- }
-}
-
-static void spin_lock(spinlock_t *sp)
-{
- if (pthread_mutex_lock(sp) != 0) {
- perror("spin_lock:pthread_mutex_lock");
- exit(-1);
- }
-}
-
-static void spin_unlock(spinlock_t *sp)
-{
- if (pthread_mutex_unlock(sp) != 0) {
- perror("spin_unlock:pthread_mutex_unlock");
- exit(-1);
- }
-}
-
-#define spin_lock_irqsave(l, f) do { f = 1; spin_lock(l); } while (0)
-#define spin_unlock_irqrestore(l, f) do { f = 0; spin_unlock(l); } while (0)
-
-/*
- * Thread creation/destruction primitives.
- */
-
-typedef pthread_t thread_id_t;
-
-#define NR_THREADS 128
-
-#define __THREAD_ID_MAP_EMPTY ((thread_id_t) 0)
-#define __THREAD_ID_MAP_WAITING ((thread_id_t) 1)
-thread_id_t __thread_id_map[NR_THREADS];
-spinlock_t __thread_id_map_mutex;
-
-#define for_each_thread(t) \
- for (t = 0; t < NR_THREADS; t++)
-
-#define for_each_running_thread(t) \
- for (t = 0; t < NR_THREADS; t++) \
- if ((__thread_id_map[t] != __THREAD_ID_MAP_EMPTY) && \
- (__thread_id_map[t] != __THREAD_ID_MAP_WAITING))
-
-#define for_each_tid(t, tid) \
- for (t = 0; t < NR_THREADS; t++) \
- if ((((tid) = __thread_id_map[t]) != __THREAD_ID_MAP_EMPTY) && \
- ((tid) != __THREAD_ID_MAP_WAITING))
-
-pthread_key_t thread_id_key;
-
-static int __smp_thread_id(void)
-{
- int i;
- thread_id_t tid = pthread_self();
-
- for (i = 0; i < NR_THREADS; i++) {
- if (__thread_id_map[i] == tid) {
- long v = i + 1; /* must be non-NULL. */
-
- if (pthread_setspecific(thread_id_key, (void *)v) != 0) {
- perror("pthread_setspecific");
- exit(-1);
- }
- return i;
- }
- }
- spin_lock(&__thread_id_map_mutex);
- for (i = 0; i < NR_THREADS; i++) {
- if (__thread_id_map[i] == tid) {
- spin_unlock(&__thread_id_map_mutex);
- return i;
- }
- }
- spin_unlock(&__thread_id_map_mutex);
- fprintf(stderr, "smp_thread_id: Rogue thread, id: %lu(%#lx)\n",
- (unsigned long) tid, (unsigned long) tid);
- exit(-1);
-}
-
-static int smp_thread_id(void)
-{
- void *id;
-
- id = pthread_getspecific(thread_id_key);
- if (id == NULL)
- return __smp_thread_id();
- return (long)(id - 1);
-}
-
-static thread_id_t create_thread(void *(*func)(void *), void *arg)
-{
- thread_id_t tid;
- int i;
-
- spin_lock(&__thread_id_map_mutex);
- for (i = 0; i < NR_THREADS; i++) {
- if (__thread_id_map[i] == __THREAD_ID_MAP_EMPTY)
- break;
- }
- if (i >= NR_THREADS) {
- spin_unlock(&__thread_id_map_mutex);
- fprintf(stderr, "Thread limit of %d exceeded!\n", NR_THREADS);
- exit(-1);
- }
- __thread_id_map[i] = __THREAD_ID_MAP_WAITING;
- spin_unlock(&__thread_id_map_mutex);
- if (pthread_create(&tid, NULL, func, arg) != 0) {
- perror("create_thread:pthread_create");
- exit(-1);
- }
- __thread_id_map[i] = tid;
- return tid;
-}
-
-static void *wait_thread(thread_id_t tid)
-{
- int i;
- void *vp;
-
- for (i = 0; i < NR_THREADS; i++) {
- if (__thread_id_map[i] == tid)
- break;
- }
- if (i >= NR_THREADS){
- fprintf(stderr, "wait_thread: bad tid = %lu(%#lx)\n",
- (unsigned long)tid, (unsigned long)tid);
- exit(-1);
- }
- if (pthread_join(tid, &vp) != 0) {
- perror("wait_thread:pthread_join");
- exit(-1);
- }
- __thread_id_map[i] = __THREAD_ID_MAP_EMPTY;
- return vp;
-}
-
-static void wait_all_threads(void)
-{
- int i;
- thread_id_t tid;
-
- for (i = 1; i < NR_THREADS; i++) {
- tid = __thread_id_map[i];
- if (tid != __THREAD_ID_MAP_EMPTY &&
- tid != __THREAD_ID_MAP_WAITING)
- (void)wait_thread(tid);
- }
-}
-
-static void run_on(int cpu)
-{
-#if HAVE_SCHED_SETAFFINITY
- cpu_set_t mask;
-
- CPU_ZERO(&mask);
- CPU_SET(cpu, &mask);
-#if SCHED_SETAFFINITY_ARGS == 2
- sched_setaffinity(0, &mask);
-#else
- sched_setaffinity(0, sizeof(mask), &mask);
-#endif
-#endif /* HAVE_SCHED_SETAFFINITY */
-}
-
-/*
- * timekeeping -- very crude -- should use MONOTONIC...
- */
-
-long long get_microseconds(void)
-{
- struct timeval tv;
-
- if (gettimeofday(&tv, NULL) != 0)
- abort();
- return ((long long)tv.tv_sec) * 1000000LL + (long long)tv.tv_usec;
-}
-
-/*
- * Per-thread variables.
- */
-
-#define DEFINE_PER_THREAD(type, name) \
- struct { \
- __typeof__(type) v \
- __attribute__((__aligned__(CAA_CACHE_LINE_SIZE))); \
- } __per_thread_##name[NR_THREADS];
-#define DECLARE_PER_THREAD(type, name) extern DEFINE_PER_THREAD(type, name)
-
-#define per_thread(name, thread) __per_thread_##name[thread].v
-#define __get_thread_var(name) per_thread(name, smp_thread_id())
-
-#define init_per_thread(name, v) \
- do { \
- int __i_p_t_i; \
- for (__i_p_t_i = 0; __i_p_t_i < NR_THREADS; __i_p_t_i++) \
- per_thread(name, __i_p_t_i) = v; \
- } while (0)
-
-DEFINE_PER_THREAD(int, smp_processor_id);
-
-/*
- * Bug checks.
- */
-
-#define BUG_ON(c) do { if (!(c)) abort(); } while (0)
-
-/*
- * Initialization -- Must be called before calling any primitives.
- */
-
-static void smp_init(void)
-{
- int i;
-
- spin_lock_init(&__thread_id_map_mutex);
- __thread_id_map[0] = pthread_self();
- for (i = 1; i < NR_THREADS; i++)
- __thread_id_map[i] = __THREAD_ID_MAP_EMPTY;
- init_per_thread(smp_processor_id, 0);
- if (pthread_key_create(&thread_id_key, NULL) != 0) {
- perror("pthread_key_create");
- exit(-1);
- }
-}
-
-#endif
--- /dev/null
+AM_LDFLAGS=-lpthread
+AM_CFLAGS=-I$(top_srcdir) -I$(top_builddir) -I$(top_srcdir)/tests/common -g
+
+noinst_PROGRAMS = test_urcu test_urcu_dynamic_link test_urcu_timing \
+ test_urcu_signal test_urcu_signal_dynamic_link test_urcu_signal_timing \
+ test_rwlock_timing test_rwlock test_perthreadlock_timing \
+ test_perthreadlock test_urcu_yield test_urcu_signal_yield test_urcu_mb \
+ test_urcu_qsbr_timing test_urcu_qsbr \
+ test_mutex test_looplen test_urcu_gc test_urcu_signal_gc \
+ test_urcu_lgc \
+ test_urcu_mb_gc test_urcu_qsbr_gc test_urcu_qsbr_lgc test_urcu_signal_lgc \
+ test_urcu_mb_lgc test_urcu_qsbr_dynamic_link test_urcu_defer \
+ test_urcu_assign test_urcu_assign_dynamic_link \
+ test_urcu_bp test_urcu_bp_dynamic_link test_cycles_per_loop \
+ test_urcu_lfq test_urcu_wfq test_urcu_lfs test_urcu_wfs \
+ test_urcu_lfs_rcu \
+ test_urcu_wfcq \
+ test_urcu_wfq_dynlink test_urcu_wfs_dynlink \
+ test_urcu_wfcq_dynlink \
+ test_urcu_lfq_dynlink test_urcu_lfs_dynlink test_urcu_hash \
+ test_urcu_lfs_rcu_dynlink
+
+if COMPAT_ARCH
+COMPAT=$(top_srcdir)/compat_arch_@ARCHTYPE@.c
+else
+COMPAT=
+endif
+
+if COMPAT_FUTEX
+COMPAT+=$(top_srcdir)/compat_futex.c
+endif
+
+URCU=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT)
+URCU_QSBR=$(top_srcdir)/urcu-qsbr.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT)
+# URCU_MB uses urcu.c but -DRCU_MB must be defined
+URCU_MB=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT)
+# URCU_SIGNAL uses urcu.c but -DRCU_SIGNAL must be defined
+URCU_SIGNAL=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT)
+URCU_BP=$(top_srcdir)/urcu-bp.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT)
+URCU_DEFER=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT)
+
+URCU_COMMON_LIB=$(top_builddir)/liburcu-common.la
+URCU_LIB=$(top_builddir)/liburcu.la
+URCU_QSBR_LIB=$(top_builddir)/liburcu-qsbr.la
+URCU_MB_LIB=$(top_builddir)/liburcu-mb.la
+URCU_SIGNAL_LIB=$(top_builddir)/liburcu-signal.la
+URCU_BP_LIB=$(top_builddir)/liburcu-bp.la
+URCU_CDS_LIB=$(top_builddir)/liburcu-cds.la
+
+EXTRA_DIST = *.sh
+
+test_urcu_SOURCES = test_urcu.c $(URCU)
+
+test_urcu_dynamic_link_SOURCES = test_urcu.c $(URCU)
+test_urcu_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
+
+test_urcu_timing_SOURCES = test_urcu_timing.c $(URCU)
+
+test_urcu_yield_SOURCES = test_urcu.c $(URCU)
+test_urcu_yield_CFLAGS = -DDEBUG_YIELD $(AM_CFLAGS)
+
+
+test_urcu_qsbr_SOURCES = test_urcu_qsbr.c $(URCU_QSBR)
+
+test_urcu_qsbr_timing_SOURCES = test_urcu_qsbr_timing.c $(URCU_QSBR)
+
+
+test_urcu_mb_SOURCES = test_urcu.c $(URCU_MB)
+test_urcu_mb_CFLAGS = -DRCU_MB $(AM_CFLAGS)
+
+
+test_urcu_signal_SOURCES = test_urcu.c $(URCU_SIGNAL)
+test_urcu_signal_CFLAGS = -DRCU_SIGNAL $(AM_CFLAGS)
+
+test_urcu_signal_dynamic_link_SOURCES = test_urcu.c $(URCU_SIGNAL)
+test_urcu_signal_dynamic_link_CFLAGS = -DRCU_SIGNAL -DDYNAMIC_LINK_TEST \
+ $(AM_CFLAGS)
+
+test_urcu_signal_timing_SOURCES = test_urcu_timing.c $(URCU_SIGNAL)
+test_urcu_signal_timing_CFLAGS= -DRCU_SIGNAL $(AM_CFLAGS)
+
+test_urcu_signal_yield_SOURCES = test_urcu.c $(URCU_SIGNAL)
+test_urcu_signal_yield_CFLAGS = -DRCU_SIGNAL -DDEBUG_YIELD $(AM_CFLAGS)
+
+test_rwlock_timing_SOURCES = test_rwlock_timing.c $(URCU_SIGNAL)
+
+test_rwlock_SOURCES = test_rwlock.c $(URCU_SIGNAL)
+
+test_perthreadlock_timing_SOURCES = test_perthreadlock_timing.c $(URCU_SIGNAL)
+
+test_perthreadlock_SOURCES = test_perthreadlock.c $(URCU_SIGNAL)
+
+test_mutex_SOURCES = test_mutex.c $(URCU)
+
+test_looplen_SOURCES = test_looplen.c
+
+test_urcu_gc_SOURCES = test_urcu_gc.c $(URCU)
+
+test_urcu_signal_gc_SOURCES = test_urcu_gc.c $(URCU_SIGNAL)
+test_urcu_signal_gc_CFLAGS = -DRCU_SIGNAL $(AM_CFLAGS)
+
+test_urcu_mb_gc_SOURCES = test_urcu_gc.c $(URCU_MB)
+test_urcu_mb_gc_CFLAGS = -DRCU_MB $(AM_CFLAGS)
+
+test_urcu_qsbr_gc_SOURCES = test_urcu_qsbr_gc.c $(URCU_QSBR)
+
+test_urcu_qsbr_lgc_SOURCES = test_urcu_qsbr_gc.c $(URCU_QSBR)
+test_urcu_qsbr_lgc_CFLAGS = -DTEST_LOCAL_GC $(AM_CFLAGS)
+
+test_urcu_lgc_SOURCES = test_urcu_gc.c $(URCU)
+test_urcu_lgc_CFLAGS = -DTEST_LOCAL_GC $(AM_CFLAGS)
+
+test_urcu_signal_lgc_SOURCES = test_urcu_gc.c $(URCU_SIGNAL)
+test_urcu_signal_lgc_CFLAGS = -DRCU_SIGNAL -DTEST_LOCAL_GC $(AM_CFLAGS)
+
+test_urcu_mb_lgc_SOURCES = test_urcu_gc.c $(URCU_MB)
+test_urcu_mb_lgc_CFLAGS = -DTEST_LOCAL_GC -DRCU_MB $(AM_CFLAGS)
+
+test_urcu_qsbr_dynamic_link_SOURCES = test_urcu_qsbr.c $(URCU_QSBR)
+test_urcu_qsbr_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
+
+test_urcu_defer_SOURCES = test_urcu_defer.c $(URCU_DEFER)
+
+test_cycles_per_loop_SOURCES = test_cycles_per_loop.c
+
+test_urcu_assign_SOURCES = test_urcu_assign.c $(URCU)
+
+test_urcu_assign_dynamic_link_SOURCES = test_urcu_assign.c $(URCU)
+test_urcu_assign_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
+
+test_urcu_bp_SOURCES = test_urcu_bp.c $(URCU_BP)
+
+test_urcu_bp_dynamic_link_SOURCES = test_urcu_bp.c $(URCU_BP)
+test_urcu_bp_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
+
+test_urcu_lfq_SOURCES = test_urcu_lfq.c $(URCU)
+test_urcu_lfq_LDADD = $(URCU_CDS_LIB)
+
+test_urcu_lfq_dynlink_SOURCES = test_urcu_lfq.c $(URCU)
+test_urcu_lfq_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
+test_urcu_lfq_dynlink_LDADD = $(URCU_CDS_LIB)
+
+test_urcu_wfq_SOURCES = test_urcu_wfq.c $(COMPAT)
+test_urcu_wfq_LDADD = $(URCU_COMMON_LIB)
+
+test_urcu_wfq_dynlink_SOURCES = test_urcu_wfq.c
+test_urcu_wfq_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
+test_urcu_wfq_dynlink_LDADD = $(URCU_COMMON_LIB)
+
+test_urcu_wfcq_SOURCES = test_urcu_wfcq.c $(COMPAT)
+test_urcu_wfcq_LDADD = $(URCU_COMMON_LIB)
+
+test_urcu_wfcq_dynlink_SOURCES = test_urcu_wfcq.c
+test_urcu_wfcq_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
+test_urcu_wfcq_dynlink_LDADD = $(URCU_COMMON_LIB)
+
+test_urcu_lfs_SOURCES = test_urcu_lfs.c $(URCU)
+test_urcu_lfs_LDADD = $(URCU_CDS_LIB)
+
+test_urcu_lfs_rcu_SOURCES = test_urcu_lfs_rcu.c $(URCU)
+test_urcu_lfs_rcu_LDADD = $(URCU_CDS_LIB)
+
+test_urcu_lfs_dynlink_SOURCES = test_urcu_lfs.c $(URCU)
+test_urcu_lfs_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
+test_urcu_lfs_dynlink_LDADD = $(URCU_CDS_LIB)
+
+test_urcu_lfs_rcu_dynlink_SOURCES = test_urcu_lfs_rcu.c $(URCU)
+test_urcu_lfs_rcu_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
+test_urcu_lfs_rcu_dynlink_LDADD = $(URCU_CDS_LIB)
+
+test_urcu_wfs_SOURCES = test_urcu_wfs.c $(COMPAT)
+test_urcu_wfs_LDADD = $(URCU_COMMON_LIB)
+
+test_urcu_wfs_dynlink_SOURCES = test_urcu_wfs.c
+test_urcu_wfs_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
+test_urcu_wfs_dynlink_LDADD = $(URCU_COMMON_LIB)
+
+test_urcu_hash_SOURCES = test_urcu_hash.c test_urcu_hash.h \
+ test_urcu_hash_rw.c test_urcu_hash_unique.c $(COMPAT)
+test_urcu_hash_CFLAGS = -DRCU_QSBR $(AM_CFLAGS)
+test_urcu_hash_LDADD = $(URCU_QSBR_LIB) $(URCU_CDS_LIB)
+
+.PHONY: bench
+
+bench:
+ ./runall.sh
--- /dev/null
+#
+# This file is meant to be sourced from other tests scripts.
+#
+
+if [ -x "$URCU_TEST_TIME_BIN" ]; then
+ test_time_bin="$URCU_TEST_TIME_BIN"
+elif [ -x "/usr/bin/time" ]; then
+ test_time_bin="/usr/bin/time"
+else
+ test_time_bin=""
+fi
+
--- /dev/null
+#!/bin/sh
+
+#run all tests
+
+#set to number of active CPUS
+NUM_CPUS=8
+
+#extra options, e.g. for setting affinity on even CPUs :
+#EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done)
+
+#ppc64 striding, use with NUM_CPUS=8
+
+#stride 1
+#EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done)
+#stride 2
+#EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done)
+#stride 4
+#EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done)
+#stride 8
+#EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done)
+
+#Vary update fraction
+#x: vary update fraction from 0 to 0.0001
+ #fix number of readers and reader C.S. length, vary delay between updates
+#y: ops/s
+
+rm -f runall.log
+rm -fr runall.detail.log
+
+
+echo Executing batch RCU test
+
+DURATION=10
+BATCH_ARRAY="1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536
+ 131072 262144"
+NR_WRITERS=$((${NUM_CPUS} / 2))
+
+rm -f batch-rcu.log
+
+NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS}))
+for BATCH_SIZE in ${BATCH_ARRAY}; do
+ echo "./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log" >> runall.log
+ (./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log) || exit 1
+done
+
+#setting gc each 32768. ** UPDATE FOR YOUR ARCHITECTURE BASED ON TEST ABOVE **
+EXTRA_OPTS="${EXTRA_OPTS} -b 32768"
+
+echo Executing update fraction test
+
+DURATION=10
+WDELAY_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768
+ 65536 131072 262144 524288 1048576 2097152 4194304 8388608
+ 16777216 33554432 67108864 134217728"
+NR_WRITERS=$((${NUM_CPUS} / 2))
+
+rm -f update-fraction.log
+
+NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS}))
+for WDELAY in ${WDELAY_ARRAY}; do
+ echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log" >> runall.log
+ (./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log) || exit 1
+done
+
+#Test scalability :
+# x: vary number of readers from 0 to num cpus
+# y: ops/s
+# 0 writer.
+
+echo Executing scalability test
+
+NR_WRITERS=0
+DURATION=10
+
+rm -f scalability.log
+
+for NR_READERS in $(seq 1 ${NUM_CPUS}); do
+ echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log" >> runall.log
+ (./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log) || exit 1
+done
+
+
+# x: Vary reader C.S. length from 0 to 100 us
+# y: ops/s
+# 8 readers
+# 0 writers
+
+echo Executing reader C.S. length test
+
+NR_READERS=${NUM_CPUS}
+NR_WRITERS=0
+DURATION=10
+#in loops.
+READERCSLEN_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152"
+
+rm -f readercslen.log
+
+for READERCSLEN in ${READERCSLEN_ARRAY}; do
+ echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -c ${READERCSLEN} | tee -a readercslen.log" >> runall.log
+ (./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -c ${READERCSLEN} | tee -a readercslen.log) || exit 1
+done
+
+echo Executing Hash table test
+./runhash.sh || exit 1
--- /dev/null
+#!/bin/sh
+
+# TODO: missing tests:
+# - send kill signals during tests to change the behavior between
+# add/remove/random
+# - validate that "nr_leaked" is always 0 in SUMMARY for all tests
+
+# 30 seconds per test
+TIME_UNITS=30
+
+TESTPROG=./test_urcu_hash
+
+#thread multiplier
+THREAD_MUL=1
+
+EXTRA_PARAMS=-v
+
+# ** test update coherency with single-value table
+
+# rw test, single key, replace and del randomly, 4 threads, auto resize.
+# key range: init, lookup, and update: 0 to 0
+${TESTPROG} 0 $((4*${THREAD_MUL})) ${TIME_UNITS} -A -s -M 1 -N 1 -O 1 ${EXTRA_PARAMS} || exit 1
+
+# rw test, single key, add unique and del randomly, 4 threads, auto resize.
+# key range: init, lookup, and update: 0 to 0
+${TESTPROG} 0 $((4*${THREAD_MUL})) ${TIME_UNITS} -A -u -M 1 -N 1 -O 1 ${EXTRA_PARAMS} || exit 1
+
+# rw test, single key, replace and del randomly, 2 lookup threads, 2 update threads, auto resize.
+# key range: init, lookup, and update: 0 to 0
+${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -s -M 1 -N 1 -O 1 ${EXTRA_PARAMS} || exit 1
+
+# rw test, single key, add and del randomly, 2 lookup threads, 2 update threads, auto resize.
+# key range: init, lookup, and update: 0 to 0
+${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -M 1 -N 1 -O 1 ${EXTRA_PARAMS} || exit 1
+
+
+# ** test updates vs lookups with default table
+
+# rw test, 2 lookup, 2 update threads, add and del randomly, auto resize.
+# max 1048576 buckets
+# key range: init, lookup, and update: 0 to 999999
+${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A ${EXTRA_PARAMS} || exit 1
+
+# rw test, 2 lookup, 2 update threads, add_replace and del randomly, auto resize.
+# max 1048576 buckets
+# key range: init, lookup, and update: 0 to 999999
+${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -s ${EXTRA_PARAMS} || exit 1
+
+# rw test, 2 lookup, 2 update threads, add_unique and del randomly, auto resize.
+# max 1048576 buckets
+# key range: init, lookup, and update: 0 to 999999
+${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -u ${EXTRA_PARAMS} || exit 1
+
+
+# test memory management backends
+
+# rw test, 2 lookup, 2 update threads, add only, auto resize.
+# max buckets: 1048576
+# key range: init, lookup, and update: 0 to 99999999
+# mm backend: "order"
+${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -m 1 -n 1048576 -i \
+ -M 100000000 -N 100000000 -O 100000000 -B order ${EXTRA_PARAMS} || exit 1
+
+# rw test, 2 lookup, 2 update threads, add only, auto resize.
+# max buckets: 1048576
+# key range: init, lookup, and update: 0 to 99999999
+# mm backend: "chunk"
+${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -m 1 -n 1048576 -i \
+ -M 100000000 -N 100000000 -O 100000000 -B chunk ${EXTRA_PARAMS} || exit 1
+
+# rw test, 2 lookup, 2 update threads, add only, auto resize.
+# max buckets: 1048576
+# key range: init, lookup, and update: 0 to 99999999
+# mm backend: "mmap"
+${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -m 1 -n 1048576 -i \
+ -M 100000000 -N 100000000 -O 100000000 -B mmap ${EXTRA_PARAMS} || exit 1
+
+
+# ** key range tests
+
+# rw test, 2 lookup, 2 update threads, add and del randomly, auto resize.
+# max 1048576 buckets
+# key range: init, and update: 0 to 999999
+# key range: lookup: 1000000 to 1999999
+# NOTE: reader threads in this test should never have a successful
+# lookup. TODO
+${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \
+ -R 1000000 ${EXTRA_PARAMS} || exit 1
+
+# ** small key range
+
+# rw test, 2 lookup, 2 update threads, add and del randomly, auto resize.
+# max 1048576 buckets
+# key range: init, update, and lookups: 0 to 9
+${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \
+ -M 10 -N 10 -O 10 ${EXTRA_PARAMS} || exit 1
+
+# rw test, 2 lookup, 2 update threads, add_unique and del randomly, auto resize.
+# max 1048576 buckets
+# key range: init, update, and lookups: 0 to 9
+${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \
+ -M 10 -N 10 -O 10 -u ${EXTRA_PARAMS} || exit 1
+
+# rw test, 2 lookup, 2 update threads, add_replace and del randomly, auto resize.
+# max 1048576 buckets
+# key range: init, update, and lookups: 0 to 9
+${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \
+ -M 10 -N 10 -O 10 -s ${EXTRA_PARAMS} || exit 1
+
+# ** lookup for known keys
+
+# rw test, 2 lookup, 2 update threads, add_replace and del randomly, auto resize.
+# max 1048576 buckets
+# lookup range is entirely populated.
+# key range: init, and lookups: 0 to 9
+# key range: updates: 10 to 19
+# NOTE: reader threads in this test should always have successful
+# lookups. TODO
+${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \
+ -M 10 -N 10 -O 10 -R 0 -T 0 -S 10 -k 10 -s ${EXTRA_PARAMS} || exit 1
+
+# ** Uniqueness test
+
+# rw test, 2 lookup, 2 update threads, add_unique, add_replace and del randomly, auto resize.
+# max 1048576 buckets
+# asserts that no duplicates are observed by reader threads
+# standard length hash chains
+${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \
+ -U ${EXTRA_PARAMS} || exit 1
+
+# rw test, 2 lookup, 2 update threads, add_unique, add_replace and del randomly, auto resize.
+# max 1048576 buckets
+# asserts that no duplicates are observed by reader threads
+# create long hash chains: using modulo 4 on keys as hash
+${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \
+ -U -C 4 ${EXTRA_PARAMS} || exit 1
--- /dev/null
+#!/bin/sh
+
+#run all tests
+
+#set to number of active CPUS
+NUM_CPUS=64
+
+#extra options, e.g. for setting affinity on even CPUs :
+EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done)
+
+#ppc64 striding, use with NUM_CPUS=8
+
+#stride 1
+#EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done)
+#stride 2
+#EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done)
+#stride 4
+#EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done)
+#stride 8
+#EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done)
+
+#Vary update fraction
+#x: vary update fraction from 0 to 0.0001
+ #fix number of readers and reader C.S. length, vary delay between updates
+#y: ops/s
+
+rm -f runall.log
+rm -fr runall.detail.log
+
+
+echo Executing batch RCU test
+
+DURATION=10
+BATCH_ARRAY="1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536
+ 131072 262144"
+NR_WRITERS=$((${NUM_CPUS} / 2))
+
+rm -f batch-rcu.log
+
+NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS}))
+for BATCH_SIZE in ${BATCH_ARRAY}; do
+ echo "./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log" >> runall.log
+ ./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log
+done
--- /dev/null
+#!/bin/sh
+
+#run all tests
+
+#set to number of active CPUS
+NUM_CPUS=64
+
+#extra options, e.g. for setting affinity on even CPUs :
+EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done)
+
+#ppc64 striding, use with NUM_CPUS=8
+
+#stride 1
+#EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done)
+#stride 2
+#EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done)
+#stride 4
+#EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done)
+#stride 8
+#EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done)
+
+#Vary update fraction
+#x: vary update fraction from 0 to 0.0001
+ #fix number of readers and reader C.S. length, vary delay between updates
+#y: ops/s
+
+rm -f runall.log
+rm -fr runall.detail.log
+
+#setting gc each 32768. ** UPDATE FOR YOUR ARCHITECTURE BASED ON PHASE 1 RESULT **
+EXTRA_OPTS="${EXTRA_OPTS} -b 32768"
+
+echo Executing update fraction test
+
+DURATION=10
+WDELAY_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768
+ 65536 131072 262144 524288 1048576 2097152 4194304 8388608
+ 16777216 33554432 67108864 134217728"
+NR_WRITERS=$((${NUM_CPUS} / 2))
+
+rm -f update-fraction.log
+
+NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS}))
+for WDELAY in ${WDELAY_ARRAY}; do
+ echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log" >> runall.log
+ ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log
+done
--- /dev/null
+#!/bin/sh
+
+#run all tests
+
+#set to number of active CPUS
+NUM_CPUS=64
+
+#extra options, e.g. for setting affinity on even CPUs :
+EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done)
+
+#ppc64 striding, use with NUM_CPUS=8
+
+#stride 1
+#EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done)
+#stride 2
+#EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done)
+#stride 4
+#EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done)
+#stride 8
+#EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done)
+
+#Vary update fraction
+#x: vary update fraction from 0 to 0.0001
+ #fix number of readers and reader C.S. length, vary delay between updates
+#y: ops/s
+
+rm -f runall.log
+rm -fr runall.detail.log
+
+#setting gc each 32768. ** UPDATE FOR YOUR ARCHITECTURE BASED ON PHASE 1 RESULT **
+EXTRA_OPTS="${EXTRA_OPTS} -b 32768"
+
+#Test scalability :
+# x: vary number of readers from 0 to num cpus
+# y: ops/s
+# 0 writer.
+
+echo Executing scalability test
+
+NR_WRITERS=0
+DURATION=10
+
+rm -f scalability.log
+
+for NR_READERS in $(seq 1 ${NUM_CPUS}); do
+ echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log" >> runall.log
+ ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log
+done
+
+
--- /dev/null
+#!/bin/sh
+
+#run all tests
+
+#set to number of active CPUS
+export NUM_CPUS=8
+
+#extra options, e.g. for setting affinity on even CPUs :
+#EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done)
+
+#ppc64 striding, use with NUM_CPUS=8
+
+rm -f *.log
+
+#stride 1
+export EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done)
+sh subphase4.sh $*
+mkdir ppc64-8cores-stride1
+mv *.log ppc64-8cores-stride1/
+
+
+#stride 2
+export EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done)
+sh subphase4.sh $*
+mkdir ppc64-8cores-stride2
+mv *.log ppc64-8cores-stride2/
+
+
+#stride 4
+export EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done)
+sh subphase4.sh $*
+mkdir ppc64-8cores-stride4
+mv *.log ppc64-8cores-stride4/
+
+
+#stride 8
+export EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done)
+sh subphase4.sh $*
+mkdir ppc64-8cores-stride8
+mv *.log ppc64-8cores-stride8/
--- /dev/null
+# test run after write-size update
+
+sh runpaul-phase1.sh
+mkdir runpaul-phase1
+mv *.log runpaul-phase1/
+
+sh runpaul-phase2.sh
+mkdir runpaul-phase2
+mv *.log runpaul-phase2/
+
+sh runpaul-phase4.sh
--- /dev/null
+sh runpaul-phase1.sh
+mkdir runpaul-phase1
+mv *.log runpaul-phase1/
+
+sh runpaul-phase2.sh
+mkdir runpaul-phase2
+mv *.log runpaul-phase2/
--- /dev/null
+#!/bin/sh
+
+#run all tests
+
+#set to number of active CPUS
+export NUM_CPUS=64
+#export NUM_CPUS=8
+
+#extra options, e.g. for setting affinity on even CPUs :
+EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done)
+#EXTRA_OPTS=$(for a in $(seq 0 1 7); do echo -n "-a ${a} "; done)
+
+rm -f *.log
+
+# x: Vary writer C.S. length from 0 to 100 us
+# y: reads/s
+# 4 readers
+# 4 writers
+
+echo Executing writer C.S. length test
+
+NR_READERS=$((${NUM_CPUS} / 2))
+NR_WRITERS=$((${NUM_CPUS} / 2))
+DURATION=10
+WDELAY=10
+#in loops.
+WRITERCSLEN_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152"
+
+rm -f writercslen.log
+
+for WRITERCSLEN in ${WRITERCSLEN_ARRAY}; do
+ echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -d ${WDELAY} -e ${WRITERCSLEN} | tee -a writercslen.log" >> runall.log
+ ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -d ${WDELAY} -e ${WRITERCSLEN} | tee -a writercslen.log
+done
+
+
+
+mkdir ppc64-writercslen
+mv *.log ppc64-writercslen/
+#mkdir xeon-writercslen
+#mv *.log xeon-writercslen/
--- /dev/null
+#!/bin/sh
+
+. ./common.sh
+
+log_file="runall.detail.log"
+
+# Check if time bin is non-empty
+if [ -n "$test_time_bin" ]; then
+ time_command="$test_time_bin -a -o $log_file"
+else
+ time_command=""
+fi
+
+#for a in test_urcu_gc test_urcu_gc_mb test_urcu_qsbr_gc; do
+for a in test_urcu_gc; do
+ echo "./${a} $*" | tee -a "$log_file"
+ $time_command ./${a} $*
+done
+
--- /dev/null
+#!/bin/sh
+
+. ./common.sh
+
+log_file="runall.detail.log"
+
+# Check if time bin is non-empty
+if [ -n "$test_time_bin" ]; then
+ time_command="$test_time_bin -a -o $log_file"
+else
+ time_command=""
+fi
+
+for a in test_urcu_gc test_urcu_signal_gc test_urcu_mb_gc test_urcu_qsbr_gc \
+ test_urcu_lgc test_urcu_signal_lgc test_urcu_mb_lgc test_urcu_qsbr_lgc \
+ test_urcu test_urcu_signal test_urcu_mb test_urcu_qsbr \
+ test_rwlock test_perthreadlock test_mutex; do
+ echo "./${a} $*" | tee -a "$log_file"
+ $time_command ./${a} $*
+done
+
--- /dev/null
+#!/bin/sh
+
+#run all tests
+
+#set to number of active CPUS
+#NUM_CPUS=8
+
+#extra options, e.g. for setting affinity on even CPUs :
+#EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done)
+
+#ppc64 striding, use with NUM_CPUS=8
+
+#stride 1
+#EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done)
+#stride 2
+#EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done)
+#stride 4
+#EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done)
+#stride 8
+#EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done)
+
+#Vary update fraction
+#x: vary update fraction from 0 to 0.0001
+ #fix number of readers and reader C.S. length, vary delay between updates
+#y: ops/s
+
+rm -f runall.log
+rm -fr runall.detail.log
+
+
+echo Executing batch RCU test
+
+DURATION=10
+BATCH_ARRAY="1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536
+ 131072 262144"
+NR_WRITERS=$((${NUM_CPUS} / 2))
+
+rm -f batch-rcu.log
+
+NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS}))
+for BATCH_SIZE in ${BATCH_ARRAY}; do
+ echo "./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log" >> runall.log
+ ./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log
+done
+
+#setting gc each 4096. ** UPDATE FOR YOUR ARCHITECTURE BASED ON TEST ABOVE **
+EXTRA_OPTS="${EXTRA_OPTS} -b 32768"
+
+echo Executing update fraction test
+
+DURATION=10
+WDELAY_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768
+ 65536 131072 262144 524288 1048576 2097152 4194304 8388608
+ 16777216 33554432 67108864 134217728"
+NR_WRITERS=$((${NUM_CPUS} / 2))
+
+rm -f update-fraction.log
+
+NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS}))
+for WDELAY in ${WDELAY_ARRAY}; do
+ echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log" >> runall.log
+ ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log
+done
+
+#Test scalability :
+# x: vary number of readers from 0 to num cpus
+# y: ops/s
+# 0 writer.
+
+echo Executing scalability test
+
+NR_WRITERS=0
+DURATION=10
+
+rm -f scalability.log
+
+for NR_READERS in $(seq 1 ${NUM_CPUS}); do
+ echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log" >> runall.log
+ ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log
+done
+
+
+# x: Vary reader C.S. length from 0 to 100 us
+# y: ops/s
+# 8 readers
+# 0 writers
+
+echo Executing reader C.S. length test
+
+NR_READERS=${NUM_CPUS}
+NR_WRITERS=0
+DURATION=10
+#in loops.
+READERCSLEN_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152"
+
+rm -f readercslen.log
+
+for READERCSLEN in ${READERCSLEN_ARRAY}; do
+ echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -c ${READERCSLEN} | tee -a readercslen.log" >> runall.log
+ ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -c ${READERCSLEN} | tee -a readercslen.log
+done
--- /dev/null
+/*
+ * test_cycles_per_loop.c
+ *
+ * Userspace RCU library - test cycles per loop
+ *
+ * Copyright February 2009 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <urcu/arch.h>
+#include <stdio.h>
+
+#define NR_LOOPS 1000000UL
+
+static inline void loop_sleep(unsigned long loops)
+{
+ while (loops-- != 0)
+ caa_cpu_relax();
+}
+
+int main()
+{
+ cycles_t time1, time2;
+
+ time1 = caa_get_cycles();
+ loop_sleep(NR_LOOPS);
+ time2 = caa_get_cycles();
+ printf("CPU clock cycles per loop: %g\n", (time2 - time1) /
+ (double)NR_LOOPS);
+ return 0;
+}
--- /dev/null
+/*
+ * test_looplen.c
+ *
+ * Userspace RCU library - test program
+ *
+ * Copyright February 2009 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <sched.h>
+#include <errno.h>
+
+#include <urcu/arch.h>
+
+#ifndef DYNAMIC_LINK_TEST
+#define _LGPL_SOURCE
+#else
+#define debug_yield_read()
+#endif
+#include <urcu.h>
+
+static inline void loop_sleep(unsigned long loops)
+{
+ while (loops-- != 0)
+ caa_cpu_relax();
+}
+
+#define LOOPS 1048576
+#define TESTS 10
+
+int main(int argc, char **argv)
+{
+ unsigned long i;
+ cycles_t time1, time2;
+ cycles_t time_tot = 0;
+ double cpl;
+
+ for (i = 0; i < TESTS; i++) {
+ time1 = caa_get_cycles();
+ loop_sleep(LOOPS);
+ time2 = caa_get_cycles();
+ time_tot += time2 - time1;
+ }
+ cpl = ((double)time_tot) / (double)TESTS / (double)LOOPS;
+
+ printf("CALIBRATION : %g cycles per loop\n", cpl);
+ printf("time_tot = %llu, LOOPS = %d, TESTS = %d\n",
+ time_tot, LOOPS, TESTS);
+
+ return 0;
+}
--- /dev/null
+/*
+ * test_urcu.c
+ *
+ * Userspace RCU library - test program
+ *
+ * Copyright February 2009 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "config.h"
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <urcu/arch.h>
+#include <urcu/tls-compat.h>
+#include "cpuset.h"
+#include "thread-id.h"
+
+/* hardcoded number of CPUs */
+#define NR_CPUS 16384
+
+#ifndef DYNAMIC_LINK_TEST
+#define _LGPL_SOURCE
+#else
+#define debug_yield_read()
+#endif
+#include <urcu.h>
+
+struct test_array {
+ int a;
+};
+
+static pthread_mutex_t lock;
+
+static volatile int test_go, test_stop;
+
+static unsigned long wdelay;
+
+static volatile struct test_array test_array = { 8 };
+
+static unsigned long duration;
+
+/* read-side C.S. duration, in loops */
+static unsigned long rduration;
+
+/* write-side C.S. duration, in loops */
+static unsigned long wduration;
+
+static inline void loop_sleep(unsigned long loops)
+{
+ while (loops-- != 0)
+ caa_cpu_relax();
+}
+
+static int verbose_mode;
+
+#define printf_verbose(fmt, args...) \
+ do { \
+ if (verbose_mode) \
+ printf(fmt, args); \
+ } while (0)
+
+static unsigned int cpu_affinities[NR_CPUS];
+static unsigned int next_aff = 0;
+static int use_affinity = 0;
+
+pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void set_affinity(void)
+{
+#if HAVE_SCHED_SETAFFINITY
+ cpu_set_t mask;
+ int cpu, ret;
+#endif /* HAVE_SCHED_SETAFFINITY */
+
+ if (!use_affinity)
+ return;
+
+#if HAVE_SCHED_SETAFFINITY
+ ret = pthread_mutex_lock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+ cpu = cpu_affinities[next_aff++];
+ ret = pthread_mutex_unlock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+#if SCHED_SETAFFINITY_ARGS == 2
+ sched_setaffinity(0, &mask);
+#else
+ sched_setaffinity(0, sizeof(mask), &mask);
+#endif
+#endif /* HAVE_SCHED_SETAFFINITY */
+}
+
+/*
+ * returns 0 if test should end.
+ */
+static int test_duration_write(void)
+{
+ return !test_stop;
+}
+
+static int test_duration_read(void)
+{
+ return !test_stop;
+}
+
+static DEFINE_URCU_TLS(unsigned long long, nr_writes);
+static DEFINE_URCU_TLS(unsigned long long, nr_reads);
+
+static
+unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes;
+static
+unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_reads;
+
+static unsigned int nr_readers;
+static unsigned int nr_writers;
+
+pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void rcu_copy_mutex_lock(void)
+{
+ int ret;
+ ret = pthread_mutex_lock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+}
+
+void rcu_copy_mutex_unlock(void)
+{
+ int ret;
+
+ ret = pthread_mutex_unlock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+}
+
+void *thr_reader(void *data)
+{
+ unsigned long tidx = (unsigned long)data;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+
+ set_affinity();
+
+ while (!test_go)
+ {
+ }
+
+ for (;;) {
+ pthread_mutex_lock(&lock);
+ assert(test_array.a == 8);
+ if (caa_unlikely(rduration))
+ loop_sleep(rduration);
+ pthread_mutex_unlock(&lock);
+ URCU_TLS(nr_reads)++;
+ if (caa_unlikely(!test_duration_read()))
+ break;
+ }
+
+ tot_nr_reads[tidx] = URCU_TLS(nr_reads);
+ printf_verbose("thread_end %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+ return ((void*)1);
+
+}
+
+void *thr_writer(void *data)
+{
+ unsigned long wtidx = (unsigned long)data;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+
+ set_affinity();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ pthread_mutex_lock(&lock);
+ test_array.a = 0;
+ test_array.a = 8;
+ if (caa_unlikely(wduration))
+ loop_sleep(wduration);
+ pthread_mutex_unlock(&lock);
+ URCU_TLS(nr_writes)++;
+ if (caa_unlikely(!test_duration_write()))
+ break;
+ if (caa_unlikely(wdelay))
+ loop_sleep(wdelay);
+ }
+
+ printf_verbose("thread_end %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+ tot_nr_writes[wtidx] = URCU_TLS(nr_writes);
+ return ((void*)2);
+}
+
+void show_usage(int argc, char **argv)
+{
+ printf("Usage : %s nr_readers nr_writers duration (s) <OPTIONS>\n",
+ argv[0]);
+ printf("OPTIONS:\n");
+#ifdef DEBUG_YIELD
+ printf(" [-r] [-w] (yield reader and/or writer)\n");
+#endif
+ printf(" [-d delay] (writer period (us))\n");
+ printf(" [-c duration] (reader C.S. duration (in loops))\n");
+ printf(" [-e duration] (writer C.S. duration (in loops))\n");
+ printf(" [-v] (verbose output)\n");
+ printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ int err;
+ pthread_t *tid_reader, *tid_writer;
+ void *tret;
+ unsigned long long *count_reader, *count_writer;
+ unsigned long long tot_reads = 0, tot_writes = 0;
+ int i, a;
+
+ if (argc < 4) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ cmm_smp_mb();
+
+ 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++) {
+ if (argv[i][0] != '-')
+ continue;
+ switch (argv[i][1]) {
+#ifdef DEBUG_YIELD
+ case 'r':
+ yield_active |= YIELD_READ;
+ break;
+ case 'w':
+ yield_active |= YIELD_WRITE;
+ break;
+#endif
+ case 'a':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ a = atoi(argv[++i]);
+ cpu_affinities[next_aff++] = a;
+ use_affinity = 1;
+ printf_verbose("Adding CPU %d affinity\n", a);
+ break;
+ case 'c':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ rduration = atol(argv[++i]);
+ break;
+ case 'd':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wdelay = atol(argv[++i]);
+ break;
+ case 'e':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wduration = atol(argv[++i]);
+ break;
+ case 'v':
+ verbose_mode = 1;
+ break;
+ }
+ }
+
+ printf_verbose("running test for %lu seconds, %u readers, %u writers.\n",
+ duration, nr_readers, nr_writers);
+ printf_verbose("Writer delay : %lu loops.\n", wdelay);
+ printf_verbose("Reader duration : %lu loops.\n", rduration);
+ printf_verbose("thread %-6s, tid %lu\n",
+ "main", urcu_get_thread_id());
+
+ tid_reader = calloc(nr_readers, sizeof(*tid_reader));
+ tid_writer = calloc(nr_writers, sizeof(*tid_writer));
+ count_reader = calloc(nr_readers, sizeof(*count_reader));
+ count_writer = calloc(nr_writers, sizeof(*count_writer));
+ tot_nr_reads = calloc(nr_readers, sizeof(*tot_nr_reads));
+ tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes));
+
+ next_aff = 0;
+
+ for (i = 0; i < nr_readers; i++) {
+ err = pthread_create(&tid_reader[i], NULL, thr_reader,
+ (void *)(long)i);
+ if (err != 0)
+ exit(1);
+ }
+ for (i = 0; i < nr_writers; i++) {
+ err = pthread_create(&tid_writer[i], NULL, thr_writer,
+ (void *)(long)i);
+ if (err != 0)
+ exit(1);
+ }
+
+ cmm_smp_mb();
+
+ test_go = 1;
+
+ sleep(duration);
+
+ test_stop = 1;
+
+ for (i = 0; i < nr_readers; i++) {
+ err = pthread_join(tid_reader[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_reads += tot_nr_reads[i];
+ }
+ for (i = 0; i < nr_writers; i++) {
+ err = pthread_join(tid_writer[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_writes += tot_nr_writes[i];
+ }
+
+ printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads,
+ tot_writes);
+ printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu "
+ "nr_writers %3u "
+ "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n",
+ argv[0], duration, nr_readers, rduration, wduration,
+ nr_writers, wdelay, tot_reads, tot_writes,
+ tot_reads + tot_writes);
+
+ free(tid_reader);
+ free(tid_writer);
+ free(count_reader);
+ free(count_writer);
+ free(tot_nr_reads);
+ free(tot_nr_writes);
+ return 0;
+}
--- /dev/null
+/*
+ * test_urcu.c
+ *
+ * Userspace RCU library - test program
+ *
+ * Copyright February 2009 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "config.h"
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <urcu/arch.h>
+#include <urcu/tls-compat.h>
+#include "cpuset.h"
+#include "thread-id.h"
+
+/* hardcoded number of CPUs */
+#define NR_CPUS 16384
+
+#ifndef DYNAMIC_LINK_TEST
+#define _LGPL_SOURCE
+#else
+#define debug_yield_read()
+#endif
+#include <urcu.h>
+
+struct test_array {
+ int a;
+};
+
+struct per_thread_lock {
+ pthread_mutex_t lock;
+} __attribute__((aligned(CAA_CACHE_LINE_SIZE))); /* cache-line aligned */
+
+static struct per_thread_lock *per_thread_lock;
+
+static volatile int test_go, test_stop;
+
+static unsigned long wdelay;
+
+static volatile struct test_array test_array = { 8 };
+
+static unsigned long duration;
+
+/* read-side C.S. duration, in loops */
+static unsigned long rduration;
+
+/* write-side C.S. duration, in loops */
+static unsigned long wduration;
+
+static inline void loop_sleep(unsigned long loops)
+{
+ while (loops-- != 0)
+ caa_cpu_relax();
+}
+
+static int verbose_mode;
+
+#define printf_verbose(fmt, args...) \
+ do { \
+ if (verbose_mode) \
+ printf(fmt, args); \
+ } while (0)
+
+static unsigned int cpu_affinities[NR_CPUS];
+static unsigned int next_aff = 0;
+static int use_affinity = 0;
+
+pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void set_affinity(void)
+{
+#if HAVE_SCHED_SETAFFINITY
+ cpu_set_t mask;
+ int cpu, ret;
+#endif /* HAVE_SCHED_SETAFFINITY */
+
+ if (!use_affinity)
+ return;
+
+#if HAVE_SCHED_SETAFFINITY
+ ret = pthread_mutex_lock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+ cpu = cpu_affinities[next_aff++];
+ ret = pthread_mutex_unlock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+#if SCHED_SETAFFINITY_ARGS == 2
+ sched_setaffinity(0, &mask);
+#else
+ sched_setaffinity(0, sizeof(mask), &mask);
+#endif
+#endif /* HAVE_SCHED_SETAFFINITY */
+}
+
+/*
+ * returns 0 if test should end.
+ */
+static int test_duration_write(void)
+{
+ return !test_stop;
+}
+
+static int test_duration_read(void)
+{
+ return !test_stop;
+}
+
+static DEFINE_URCU_TLS(unsigned long long, nr_writes);
+static DEFINE_URCU_TLS(unsigned long long, nr_reads);
+
+static
+unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes;
+static
+unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_reads;
+
+static unsigned int nr_readers;
+static unsigned int nr_writers;
+
+pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void rcu_copy_mutex_lock(void)
+{
+ int ret;
+ ret = pthread_mutex_lock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+}
+
+void rcu_copy_mutex_unlock(void)
+{
+ int ret;
+
+ ret = pthread_mutex_unlock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+}
+
+void *thr_reader(void *data)
+{
+ unsigned long tidx = (unsigned long)data;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+
+ set_affinity();
+
+ while (!test_go)
+ {
+ }
+
+ for (;;) {
+ pthread_mutex_lock(&per_thread_lock[tidx].lock);
+ assert(test_array.a == 8);
+ if (caa_unlikely(rduration))
+ loop_sleep(rduration);
+ pthread_mutex_unlock(&per_thread_lock[tidx].lock);
+ URCU_TLS(nr_reads)++;
+ if (caa_unlikely(!test_duration_read()))
+ break;
+ }
+
+ tot_nr_reads[tidx] = URCU_TLS(nr_reads);
+ printf_verbose("thread_end %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+ return ((void*)1);
+
+}
+
+void *thr_writer(void *data)
+{
+ unsigned long wtidx = (unsigned long)data;
+ long tidx;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+
+ set_affinity();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ for (tidx = 0; tidx < nr_readers; tidx++) {
+ pthread_mutex_lock(&per_thread_lock[tidx].lock);
+ }
+ test_array.a = 0;
+ test_array.a = 8;
+ if (caa_unlikely(wduration))
+ loop_sleep(wduration);
+ for (tidx = (long)nr_readers - 1; tidx >= 0; tidx--) {
+ pthread_mutex_unlock(&per_thread_lock[tidx].lock);
+ }
+ URCU_TLS(nr_writes)++;
+ if (caa_unlikely(!test_duration_write()))
+ break;
+ if (caa_unlikely(wdelay))
+ loop_sleep(wdelay);
+ }
+
+ printf_verbose("thread_end %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+ tot_nr_writes[wtidx] = URCU_TLS(nr_writes);
+ return ((void*)2);
+}
+
+void show_usage(int argc, char **argv)
+{
+ printf("Usage : %s nr_readers nr_writers duration (s) <OPTIONS>\n",
+ argv[0]);
+ printf("OPTIONS:\n");
+#ifdef DEBUG_YIELD
+ printf(" [-r] [-w] (yield reader and/or writer)\n");
+#endif
+ printf(" [-d delay] (writer period (us))\n");
+ printf(" [-c duration] (reader C.S. duration (in loops))\n");
+ printf(" [-e duration] (writer C.S. duration (in loops))\n");
+ printf(" [-v] (verbose output)\n");
+ printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ int err;
+ pthread_t *tid_reader, *tid_writer;
+ void *tret;
+ unsigned long long tot_reads = 0, tot_writes = 0;
+ int i, a;
+
+ if (argc < 4) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ cmm_smp_mb();
+
+ 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++) {
+ if (argv[i][0] != '-')
+ continue;
+ switch (argv[i][1]) {
+#ifdef DEBUG_YIELD
+ case 'r':
+ yield_active |= YIELD_READ;
+ break;
+ case 'w':
+ yield_active |= YIELD_WRITE;
+ break;
+#endif
+ case 'a':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ a = atoi(argv[++i]);
+ cpu_affinities[next_aff++] = a;
+ use_affinity = 1;
+ printf_verbose("Adding CPU %d affinity\n", a);
+ break;
+ case 'c':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ rduration = atol(argv[++i]);
+ break;
+ case 'd':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wdelay = atol(argv[++i]);
+ break;
+ case 'e':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wduration = atol(argv[++i]);
+ break;
+ case 'v':
+ verbose_mode = 1;
+ break;
+ }
+ }
+
+ printf_verbose("running test for %lu seconds, %u readers, %u writers.\n",
+ duration, nr_readers, nr_writers);
+ printf_verbose("Writer delay : %lu loops.\n", wdelay);
+ printf_verbose("Reader duration : %lu loops.\n", rduration);
+ printf_verbose("thread %-6s, tid %lu\n",
+ "main", urcu_get_thread_id());
+
+ tid_reader = calloc(nr_readers, sizeof(*tid_reader));
+ tid_writer = calloc(nr_writers, sizeof(*tid_writer));
+ tot_nr_reads = calloc(nr_readers, sizeof(*tot_nr_reads));
+ tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes));
+ per_thread_lock = calloc(nr_readers, sizeof(*per_thread_lock));
+ for (i = 0; i < nr_readers; i++) {
+ err = pthread_mutex_init(&per_thread_lock[i].lock, NULL);
+ if (err != 0)
+ exit(1);
+ }
+
+ next_aff = 0;
+
+ for (i = 0; i < nr_readers; i++) {
+ err = pthread_create(&tid_reader[i], NULL, thr_reader,
+ (void *)(long)i);
+ if (err != 0)
+ exit(1);
+ }
+ for (i = 0; i < nr_writers; i++) {
+ err = pthread_create(&tid_writer[i], NULL, thr_writer,
+ (void *)(long)i);
+ if (err != 0)
+ exit(1);
+ }
+
+ cmm_smp_mb();
+
+ test_go = 1;
+
+ sleep(duration);
+
+ test_stop = 1;
+
+ for (i = 0; i < nr_readers; i++) {
+ err = pthread_join(tid_reader[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_reads += tot_nr_reads[i];
+ }
+ for (i = 0; i < nr_writers; i++) {
+ err = pthread_join(tid_writer[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_writes += tot_nr_writes[i];
+ }
+
+ printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads,
+ tot_writes);
+ printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu "
+ "nr_writers %3u "
+ "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n",
+ argv[0], duration, nr_readers, rduration, wduration,
+ nr_writers, wdelay, tot_reads, tot_writes,
+ tot_reads + tot_writes);
+
+ free(tid_reader);
+ free(tid_writer);
+ free(tot_nr_reads);
+ free(tot_nr_writes);
+ free(per_thread_lock);
+ return 0;
+}
--- /dev/null
+/*
+ * test_perthreadloc_timing.c
+ *
+ * Per thread locks - test program
+ *
+ * Copyright February 2009 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <pthread.h>
+#include <errno.h>
+
+#include <urcu/arch.h>
+
+#include "thread-id.h"
+
+#include <urcu.h>
+
+struct test_array {
+ int a;
+};
+
+static struct test_array test_array = { 8 };
+
+struct per_thread_lock {
+ pthread_mutex_t lock;
+} __attribute__((aligned(CAA_CACHE_LINE_SIZE))); /* cache-line aligned */
+
+static struct per_thread_lock *per_thread_lock;
+
+#define OUTER_READ_LOOP 200U
+#define INNER_READ_LOOP 100000U
+#define READ_LOOP ((unsigned long long)OUTER_READ_LOOP * INNER_READ_LOOP)
+
+#define OUTER_WRITE_LOOP 10U
+#define INNER_WRITE_LOOP 200U
+#define WRITE_LOOP ((unsigned long long)OUTER_WRITE_LOOP * INNER_WRITE_LOOP)
+
+static int num_read;
+static int num_write;
+
+#define NR_READ num_read
+#define NR_WRITE num_write
+
+static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *reader_time;
+static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *writer_time;
+
+void *thr_reader(void *arg)
+{
+ int i, j;
+ cycles_t time1, time2;
+ long tidx = (long)arg;
+
+ printf("thread_begin %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+ sleep(2);
+
+ time1 = caa_get_cycles();
+ for (i = 0; i < OUTER_READ_LOOP; i++) {
+ for (j = 0; j < INNER_READ_LOOP; j++) {
+ pthread_mutex_lock(&per_thread_lock[tidx].lock);
+ assert(test_array.a == 8);
+ pthread_mutex_unlock(&per_thread_lock[tidx].lock);
+ }
+ }
+ time2 = caa_get_cycles();
+
+ reader_time[tidx] = time2 - time1;
+
+ sleep(2);
+ printf("thread_end %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+ return ((void*)1);
+
+}
+
+void *thr_writer(void *arg)
+{
+ int i, j;
+ long tidx;
+ cycles_t time1, time2;
+
+ printf("thread_begin %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+ sleep(2);
+
+ for (i = 0; i < OUTER_WRITE_LOOP; i++) {
+ for (j = 0; j < INNER_WRITE_LOOP; j++) {
+ time1 = caa_get_cycles();
+ for (tidx = 0; tidx < NR_READ; tidx++) {
+ pthread_mutex_lock(&per_thread_lock[tidx].lock);
+ }
+ test_array.a = 8;
+ for (tidx = NR_READ - 1; tidx >= 0; tidx--) {
+ pthread_mutex_unlock(&per_thread_lock[tidx].lock);
+ }
+ time2 = caa_get_cycles();
+ writer_time[(unsigned long)arg] += time2 - time1;
+ usleep(1);
+ }
+ }
+
+ printf("thread_end %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+ return ((void*)2);
+}
+
+int main(int argc, char **argv)
+{
+ int err;
+ pthread_t *tid_reader, *tid_writer;
+ void *tret;
+ int i;
+ cycles_t tot_rtime = 0;
+ cycles_t tot_wtime = 0;
+
+ if (argc < 2) {
+ printf("Usage : %s nr_readers nr_writers\n", argv[0]);
+ exit(-1);
+ }
+ num_read = atoi(argv[1]);
+ num_write = atoi(argv[2]);
+
+ reader_time = calloc(num_read, sizeof(*reader_time));
+ writer_time = calloc(num_write, sizeof(*writer_time));
+ tid_reader = calloc(num_read, sizeof(*tid_reader));
+ tid_writer = calloc(num_write, sizeof(*tid_writer));
+
+ printf("thread %-6s, tid %lu\n",
+ "main", urcu_get_thread_id());
+
+ per_thread_lock = malloc(sizeof(struct per_thread_lock) * NR_READ);
+
+ for (i = 0; i < NR_READ; i++) {
+ pthread_mutex_init(&per_thread_lock[i].lock, NULL);
+ }
+ for (i = 0; i < NR_READ; i++) {
+ err = pthread_create(&tid_reader[i], NULL, thr_reader,
+ (void *)(long)i);
+ if (err != 0)
+ exit(1);
+ }
+ for (i = 0; i < NR_WRITE; i++) {
+ err = pthread_create(&tid_writer[i], NULL, thr_writer,
+ (void *)(long)i);
+ if (err != 0)
+ exit(1);
+ }
+
+ sleep(10);
+
+ for (i = 0; i < NR_READ; i++) {
+ err = pthread_join(tid_reader[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_rtime += reader_time[i];
+ }
+ for (i = 0; i < NR_WRITE; i++) {
+ err = pthread_join(tid_writer[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_wtime += writer_time[i];
+ }
+ printf("Time per read : %g cycles\n",
+ (double)tot_rtime / ((double)NR_READ * (double)READ_LOOP));
+ printf("Time per write : %g cycles\n",
+ (double)tot_wtime / ((double)NR_WRITE * (double)WRITE_LOOP));
+ free(per_thread_lock);
+
+ free(reader_time);
+ free(writer_time);
+ free(tid_reader);
+ free(tid_writer);
+
+ return 0;
+}
--- /dev/null
+/*
+ * test_urcu.c
+ *
+ * Userspace RCU library - test program
+ *
+ * Copyright February 2009 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "config.h"
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <urcu/arch.h>
+#include <urcu/tls-compat.h>
+#include "cpuset.h"
+#include "thread-id.h"
+
+/* hardcoded number of CPUs */
+#define NR_CPUS 16384
+
+#ifndef DYNAMIC_LINK_TEST
+#define _LGPL_SOURCE
+#else
+#define debug_yield_read()
+#endif
+#include <urcu.h>
+
+struct test_array {
+ int a;
+};
+
+pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER;
+
+static volatile int test_go, test_stop;
+
+static unsigned long wdelay;
+
+static volatile struct test_array test_array = { 8 };
+
+static unsigned long duration;
+
+/* read-side C.S. duration, in loops */
+static unsigned long rduration;
+
+/* write-side C.S. duration, in loops */
+static unsigned long wduration;
+
+static inline void loop_sleep(unsigned long loops)
+{
+ while (loops-- != 0)
+ caa_cpu_relax();
+}
+
+static int verbose_mode;
+
+#define printf_verbose(fmt, args...) \
+ do { \
+ if (verbose_mode) \
+ printf(fmt, args); \
+ } while (0)
+
+static unsigned int cpu_affinities[NR_CPUS];
+static unsigned int next_aff = 0;
+static int use_affinity = 0;
+
+pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void set_affinity(void)
+{
+#if HAVE_SCHED_SETAFFINITY
+ cpu_set_t mask;
+ int cpu, ret;
+#endif /* HAVE_SCHED_SETAFFINITY */
+
+ if (!use_affinity)
+ return;
+
+#if HAVE_SCHED_SETAFFINITY
+ ret = pthread_mutex_lock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+ cpu = cpu_affinities[next_aff++];
+ ret = pthread_mutex_unlock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+#if SCHED_SETAFFINITY_ARGS == 2
+ sched_setaffinity(0, &mask);
+#else
+ sched_setaffinity(0, sizeof(mask), &mask);
+#endif
+#endif /* HAVE_SCHED_SETAFFINITY */
+}
+
+/*
+ * returns 0 if test should end.
+ */
+static int test_duration_write(void)
+{
+ return !test_stop;
+}
+
+static int test_duration_read(void)
+{
+ return !test_stop;
+}
+
+static DEFINE_URCU_TLS(unsigned long long, nr_writes);
+static DEFINE_URCU_TLS(unsigned long long, nr_reads);
+
+static unsigned int nr_readers;
+static unsigned int nr_writers;
+
+pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void rcu_copy_mutex_lock(void)
+{
+ int ret;
+ ret = pthread_mutex_lock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+}
+
+void rcu_copy_mutex_unlock(void)
+{
+ int ret;
+
+ ret = pthread_mutex_unlock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+}
+
+void *thr_reader(void *_count)
+{
+ unsigned long long *count = _count;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+
+ set_affinity();
+
+ while (!test_go)
+ {
+ }
+
+ for (;;) {
+ pthread_rwlock_rdlock(&lock);
+ assert(test_array.a == 8);
+ if (caa_unlikely(rduration))
+ loop_sleep(rduration);
+ pthread_rwlock_unlock(&lock);
+ URCU_TLS(nr_reads)++;
+ if (caa_unlikely(!test_duration_read()))
+ break;
+ }
+
+ *count = URCU_TLS(nr_reads);
+ printf_verbose("thread_end %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+ return ((void*)1);
+
+}
+
+void *thr_writer(void *_count)
+{
+ unsigned long long *count = _count;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+
+ set_affinity();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ pthread_rwlock_wrlock(&lock);
+ test_array.a = 0;
+ test_array.a = 8;
+ if (caa_unlikely(wduration))
+ loop_sleep(wduration);
+ pthread_rwlock_unlock(&lock);
+ URCU_TLS(nr_writes)++;
+ if (caa_unlikely(!test_duration_write()))
+ break;
+ if (caa_unlikely(wdelay))
+ loop_sleep(wdelay);
+ }
+
+ printf_verbose("thread_end %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+ *count = URCU_TLS(nr_writes);
+ return ((void*)2);
+}
+
+void show_usage(int argc, char **argv)
+{
+ printf("Usage : %s nr_readers nr_writers duration (s) <OPTIONS>\n",
+ argv[0]);
+ printf("OPTIONS:\n");
+#ifdef DEBUG_YIELD
+ printf(" [-r] [-w] (yield reader and/or writer)\n");
+#endif
+ printf(" [-d delay] (writer period (us))\n");
+ printf(" [-c duration] (reader C.S. duration (in loops))\n");
+ printf(" [-e duration] (writer C.S. duration (in loops))\n");
+ printf(" [-v] (verbose output)\n");
+ printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ int err;
+ pthread_t *tid_reader, *tid_writer;
+ void *tret;
+ unsigned long long *count_reader, *count_writer;
+ unsigned long long tot_reads = 0, tot_writes = 0;
+ int i, a;
+
+ if (argc < 4) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ cmm_smp_mb();
+
+ 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++) {
+ if (argv[i][0] != '-')
+ continue;
+ switch (argv[i][1]) {
+#ifdef DEBUG_YIELD
+ case 'r':
+ yield_active |= YIELD_READ;
+ break;
+ case 'w':
+ yield_active |= YIELD_WRITE;
+ break;
+#endif
+ case 'a':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ a = atoi(argv[++i]);
+ cpu_affinities[next_aff++] = a;
+ use_affinity = 1;
+ printf_verbose("Adding CPU %d affinity\n", a);
+ break;
+ case 'c':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ rduration = atol(argv[++i]);
+ break;
+ case 'd':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wdelay = atol(argv[++i]);
+ break;
+ case 'e':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wduration = atol(argv[++i]);
+ break;
+ case 'v':
+ verbose_mode = 1;
+ break;
+ }
+ }
+
+ printf_verbose("running test for %lu seconds, %u readers, %u writers.\n",
+ duration, nr_readers, nr_writers);
+ printf_verbose("Writer delay : %lu loops.\n", wdelay);
+ printf_verbose("Reader duration : %lu loops.\n", rduration);
+ printf_verbose("thread %-6s, tid %lu\n",
+ "main", urcu_get_thread_id());
+
+ tid_reader = calloc(nr_readers, sizeof(*tid_reader));
+ tid_writer = calloc(nr_writers, sizeof(*tid_writer));
+ count_reader = calloc(nr_readers, sizeof(*count_reader));
+ count_writer = calloc(nr_writers, sizeof(*count_writer));
+
+ next_aff = 0;
+
+ for (i = 0; i < nr_readers; i++) {
+ err = pthread_create(&tid_reader[i], NULL, thr_reader,
+ &count_reader[i]);
+ if (err != 0)
+ exit(1);
+ }
+ for (i = 0; i < nr_writers; i++) {
+ err = pthread_create(&tid_writer[i], NULL, thr_writer,
+ &count_writer[i]);
+ if (err != 0)
+ exit(1);
+ }
+
+ cmm_smp_mb();
+
+ test_go = 1;
+
+ sleep(duration);
+
+ 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];
+ }
+
+ printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads,
+ tot_writes);
+ printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu "
+ "nr_writers %3u "
+ "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n",
+ argv[0], duration, nr_readers, rduration, wduration,
+ nr_writers, wdelay, tot_reads, tot_writes,
+ tot_reads + tot_writes);
+
+ free(tid_reader);
+ free(tid_writer);
+ free(count_reader);
+ free(count_writer);
+ return 0;
+}
--- /dev/null
+/*
+ * test_urcu.c
+ *
+ * Userspace RCU library - test program
+ *
+ * Copyright February 2009 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <pthread.h>
+#include <errno.h>
+
+#include <urcu/arch.h>
+
+#include "thread-id.h"
+
+#include <urcu.h>
+
+struct test_array {
+ int a;
+};
+
+pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER;
+
+static struct test_array test_array = { 8 };
+
+#define OUTER_READ_LOOP 200U
+#define INNER_READ_LOOP 100000U
+#define READ_LOOP ((unsigned long long)OUTER_READ_LOOP * INNER_READ_LOOP)
+
+#define OUTER_WRITE_LOOP 10U
+#define INNER_WRITE_LOOP 200U
+#define WRITE_LOOP ((unsigned long long)OUTER_WRITE_LOOP * INNER_WRITE_LOOP)
+
+static int num_read;
+static int num_write;
+
+#define NR_READ num_read
+#define NR_WRITE num_write
+
+static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *reader_time;
+static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *writer_time;
+
+void *thr_reader(void *arg)
+{
+ int i, j;
+ cycles_t time1, time2;
+
+ printf("thread_begin %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+ sleep(2);
+
+ time1 = caa_get_cycles();
+ for (i = 0; i < OUTER_READ_LOOP; i++) {
+ for (j = 0; j < INNER_READ_LOOP; j++) {
+ pthread_rwlock_rdlock(&lock);
+ assert(test_array.a == 8);
+ pthread_rwlock_unlock(&lock);
+ }
+ }
+ time2 = caa_get_cycles();
+
+ reader_time[(unsigned long)arg] = time2 - time1;
+
+ sleep(2);
+ printf("thread_end %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+ return ((void*)1);
+
+}
+
+void *thr_writer(void *arg)
+{
+ int i, j;
+ cycles_t time1, time2;
+
+ printf("thread_begin %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+ sleep(2);
+
+ for (i = 0; i < OUTER_WRITE_LOOP; i++) {
+ for (j = 0; j < INNER_WRITE_LOOP; j++) {
+ time1 = caa_get_cycles();
+ pthread_rwlock_wrlock(&lock);
+ test_array.a = 8;
+ pthread_rwlock_unlock(&lock);
+ time2 = caa_get_cycles();
+ writer_time[(unsigned long)arg] += time2 - time1;
+ usleep(1);
+ }
+ }
+
+ printf("thread_end %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+ return ((void*)2);
+}
+
+int main(int argc, char **argv)
+{
+ int err;
+ pthread_t *tid_reader, *tid_writer;
+ void *tret;
+ int i;
+ cycles_t tot_rtime = 0;
+ cycles_t tot_wtime = 0;
+
+ if (argc < 2) {
+ printf("Usage : %s nr_readers nr_writers\n", argv[0]);
+ exit(-1);
+ }
+ num_read = atoi(argv[1]);
+ num_write = atoi(argv[2]);
+
+ reader_time = calloc(num_read, sizeof(*reader_time));
+ writer_time = calloc(num_write, sizeof(*writer_time));
+ tid_reader = calloc(num_read, sizeof(*tid_reader));
+ tid_writer = calloc(num_write, sizeof(*tid_writer));
+
+ printf("thread %-6s, tid %lu\n",
+ "main", urcu_get_thread_id());
+
+ for (i = 0; i < NR_READ; i++) {
+ err = pthread_create(&tid_reader[i], NULL, thr_reader,
+ (void *)(long)i);
+ if (err != 0)
+ exit(1);
+ }
+ for (i = 0; i < NR_WRITE; i++) {
+ err = pthread_create(&tid_writer[i], NULL, thr_writer,
+ (void *)(long)i);
+ if (err != 0)
+ exit(1);
+ }
+
+ sleep(10);
+
+ for (i = 0; i < NR_READ; i++) {
+ err = pthread_join(tid_reader[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_rtime += reader_time[i];
+ }
+ for (i = 0; i < NR_WRITE; i++) {
+ err = pthread_join(tid_writer[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_wtime += writer_time[i];
+ }
+ printf("Time per read : %g cycles\n",
+ (double)tot_rtime / ((double)NR_READ * (double)READ_LOOP));
+ printf("Time per write : %g cycles\n",
+ (double)tot_wtime / ((double)NR_WRITE * (double)WRITE_LOOP));
+
+ free(reader_time);
+ free(writer_time);
+ free(tid_reader);
+ free(tid_writer);
+
+ return 0;
+}
--- /dev/null
+/*
+ * test_urcu.c
+ *
+ * Userspace RCU library - test program
+ *
+ * Copyright February 2009 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "config.h"
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <urcu/arch.h>
+#include <urcu/tls-compat.h>
+#include "cpuset.h"
+#include "thread-id.h"
+
+/* hardcoded number of CPUs */
+#define NR_CPUS 16384
+
+#ifndef DYNAMIC_LINK_TEST
+#define _LGPL_SOURCE
+#else
+#define rcu_debug_yield_read()
+#endif
+#include <urcu.h>
+
+static volatile int test_go, test_stop;
+
+static unsigned long wdelay;
+
+static int *test_rcu_pointer;
+
+static unsigned long duration;
+
+/* read-side C.S. duration, in loops */
+static unsigned long rduration;
+
+/* write-side C.S. duration, in loops */
+static unsigned long wduration;
+
+static inline void loop_sleep(unsigned long loops)
+{
+ while (loops-- != 0)
+ caa_cpu_relax();
+}
+
+static int verbose_mode;
+
+#define printf_verbose(fmt, args...) \
+ do { \
+ if (verbose_mode) \
+ printf(fmt, args); \
+ } while (0)
+
+static unsigned int cpu_affinities[NR_CPUS];
+static unsigned int next_aff = 0;
+static int use_affinity = 0;
+
+pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void set_affinity(void)
+{
+#if HAVE_SCHED_SETAFFINITY
+ cpu_set_t mask;
+ int cpu, ret;
+#endif /* HAVE_SCHED_SETAFFINITY */
+
+ if (!use_affinity)
+ return;
+
+#if HAVE_SCHED_SETAFFINITY
+ ret = pthread_mutex_lock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+ cpu = cpu_affinities[next_aff++];
+ ret = pthread_mutex_unlock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+#if SCHED_SETAFFINITY_ARGS == 2
+ sched_setaffinity(0, &mask);
+#else
+ sched_setaffinity(0, sizeof(mask), &mask);
+#endif
+#endif /* HAVE_SCHED_SETAFFINITY */
+}
+
+/*
+ * returns 0 if test should end.
+ */
+static int test_duration_write(void)
+{
+ return !test_stop;
+}
+
+static int test_duration_read(void)
+{
+ return !test_stop;
+}
+
+static DEFINE_URCU_TLS(unsigned long long, nr_writes);
+static DEFINE_URCU_TLS(unsigned long long, nr_reads);
+
+static unsigned int nr_readers;
+static unsigned int nr_writers;
+
+pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void rcu_copy_mutex_lock(void)
+{
+ int ret;
+ ret = pthread_mutex_lock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+}
+
+void rcu_copy_mutex_unlock(void)
+{
+ int ret;
+
+ ret = pthread_mutex_unlock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+}
+
+void *thr_reader(void *_count)
+{
+ unsigned long long *count = _count;
+ int *local_ptr;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+
+ set_affinity();
+
+ rcu_register_thread();
+ assert(!rcu_read_ongoing());
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ rcu_read_lock();
+ assert(rcu_read_ongoing());
+ local_ptr = rcu_dereference(test_rcu_pointer);
+ rcu_debug_yield_read();
+ if (local_ptr)
+ assert(*local_ptr == 8);
+ if (caa_unlikely(rduration))
+ loop_sleep(rduration);
+ rcu_read_unlock();
+ URCU_TLS(nr_reads)++;
+ if (caa_unlikely(!test_duration_read()))
+ break;
+ }
+
+ rcu_unregister_thread();
+
+ /* test extra thread registration */
+ rcu_register_thread();
+ rcu_unregister_thread();
+
+ *count = URCU_TLS(nr_reads);
+ printf_verbose("thread_end %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+ return ((void*)1);
+
+}
+
+void *thr_writer(void *_count)
+{
+ unsigned long long *count = _count;
+ int *new, *old;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+
+ set_affinity();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ new = malloc(sizeof(int));
+ assert(new);
+ *new = 8;
+ old = rcu_xchg_pointer(&test_rcu_pointer, new);
+ if (caa_unlikely(wduration))
+ loop_sleep(wduration);
+ synchronize_rcu();
+ if (old)
+ *old = 0;
+ free(old);
+ URCU_TLS(nr_writes)++;
+ if (caa_unlikely(!test_duration_write()))
+ break;
+ if (caa_unlikely(wdelay))
+ loop_sleep(wdelay);
+ }
+
+ printf_verbose("thread_end %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+ *count = URCU_TLS(nr_writes);
+ return ((void*)2);
+}
+
+void show_usage(int argc, char **argv)
+{
+ printf("Usage : %s nr_readers nr_writers duration (s) <OPTIONS>\n",
+ argv[0]);
+ printf("OPTIONS:\n");
+#ifdef DEBUG_YIELD
+ printf(" [-r] [-w] (yield reader and/or writer)\n");
+#endif
+ printf(" [-d delay] (writer period (us))\n");
+ printf(" [-c duration] (reader C.S. duration (in loops))\n");
+ printf(" [-e duration] (writer C.S. duration (in loops))\n");
+ printf(" [-v] (verbose output)\n");
+ printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ int err;
+ pthread_t *tid_reader, *tid_writer;
+ void *tret;
+ unsigned long long *count_reader, *count_writer;
+ unsigned long long tot_reads = 0, tot_writes = 0;
+ int i, a;
+
+ 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++) {
+ if (argv[i][0] != '-')
+ continue;
+ switch (argv[i][1]) {
+#ifdef DEBUG_YIELD
+ case 'r':
+ rcu_yield_active |= RCU_YIELD_READ;
+ break;
+ case 'w':
+ rcu_yield_active |= RCU_YIELD_WRITE;
+ break;
+#endif
+ case 'a':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ a = atoi(argv[++i]);
+ cpu_affinities[next_aff++] = a;
+ use_affinity = 1;
+ printf_verbose("Adding CPU %d affinity\n", a);
+ break;
+ case 'c':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ rduration = atol(argv[++i]);
+ break;
+ case 'd':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wdelay = atol(argv[++i]);
+ break;
+ case 'e':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wduration = atol(argv[++i]);
+ break;
+ case 'v':
+ verbose_mode = 1;
+ break;
+ }
+ }
+
+ printf_verbose("running test for %lu seconds, %u readers, %u writers.\n",
+ duration, nr_readers, nr_writers);
+ printf_verbose("Writer delay : %lu loops.\n", wdelay);
+ printf_verbose("Reader duration : %lu loops.\n", rduration);
+ printf_verbose("thread %-6s, tid %lu\n",
+ "main", urcu_get_thread_id());
+
+ tid_reader = calloc(nr_readers, sizeof(*tid_reader));
+ tid_writer = calloc(nr_writers, sizeof(*tid_writer));
+ count_reader = calloc(nr_readers, sizeof(*count_reader));
+ count_writer = calloc(nr_writers, sizeof(*count_writer));
+
+ next_aff = 0;
+
+ for (i = 0; i < nr_readers; i++) {
+ err = pthread_create(&tid_reader[i], NULL, thr_reader,
+ &count_reader[i]);
+ if (err != 0)
+ exit(1);
+ }
+ for (i = 0; i < nr_writers; i++) {
+ err = pthread_create(&tid_writer[i], NULL, thr_writer,
+ &count_writer[i]);
+ if (err != 0)
+ exit(1);
+ }
+
+ cmm_smp_mb();
+
+ test_go = 1;
+
+ sleep(duration);
+
+ 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];
+ }
+
+ printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads,
+ tot_writes);
+ printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu "
+ "nr_writers %3u "
+ "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n",
+ argv[0], duration, nr_readers, rduration, wduration,
+ nr_writers, wdelay, tot_reads, tot_writes,
+ tot_reads + tot_writes);
+ free(test_rcu_pointer);
+ free(tid_reader);
+ free(tid_writer);
+ free(count_reader);
+ free(count_writer);
+ return 0;
+}
--- /dev/null
+/*
+ * test_urcu_assign.c
+ *
+ * Userspace RCU library - test program
+ *
+ * Copyright February 2009 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "config.h"
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <urcu/arch.h>
+#include <urcu/tls-compat.h>
+#include "cpuset.h"
+#include "thread-id.h"
+
+/* hardcoded number of CPUs */
+#define NR_CPUS 16384
+
+#ifndef DYNAMIC_LINK_TEST
+#define _LGPL_SOURCE
+#else
+#define rcu_debug_yield_read()
+#endif
+#include <urcu.h>
+
+struct test_array {
+ int a;
+};
+
+static volatile int test_go, test_stop;
+
+static unsigned long wdelay;
+
+static struct test_array *test_rcu_pointer;
+
+static unsigned long duration;
+
+/* read-side C.S. duration, in loops */
+static unsigned long rduration;
+
+/* write-side C.S. duration, in loops */
+static unsigned long wduration;
+
+static inline void loop_sleep(unsigned long loops)
+{
+ while (loops-- != 0)
+ caa_cpu_relax();
+}
+
+static int verbose_mode;
+
+#define printf_verbose(fmt, args...) \
+ do { \
+ if (verbose_mode) \
+ printf(fmt, args); \
+ } while (0)
+
+static unsigned int cpu_affinities[NR_CPUS];
+static unsigned int next_aff = 0;
+static int use_affinity = 0;
+
+pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void set_affinity(void)
+{
+#if HAVE_SCHED_SETAFFINITY
+ cpu_set_t mask;
+ int cpu, ret;
+#endif /* HAVE_SCHED_SETAFFINITY */
+
+ if (!use_affinity)
+ return;
+
+#if HAVE_SCHED_SETAFFINITY
+ ret = pthread_mutex_lock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+ cpu = cpu_affinities[next_aff++];
+ ret = pthread_mutex_unlock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+#if SCHED_SETAFFINITY_ARGS == 2
+ sched_setaffinity(0, &mask);
+#else
+ sched_setaffinity(0, sizeof(mask), &mask);
+#endif
+#endif /* HAVE_SCHED_SETAFFINITY */
+}
+
+/*
+ * returns 0 if test should end.
+ */
+static int test_duration_write(void)
+{
+ return !test_stop;
+}
+
+static int test_duration_read(void)
+{
+ return !test_stop;
+}
+
+static DEFINE_URCU_TLS(unsigned long long, nr_writes);
+static DEFINE_URCU_TLS(unsigned long long, nr_reads);
+
+static unsigned int nr_readers;
+static unsigned int nr_writers;
+
+pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void rcu_copy_mutex_lock(void)
+{
+ int ret;
+ ret = pthread_mutex_lock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+}
+
+void rcu_copy_mutex_unlock(void)
+{
+ int ret;
+
+ ret = pthread_mutex_unlock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+}
+
+/*
+ * malloc/free are reusing memory areas too quickly, which does not let us
+ * test races appropriately. Use a large circular array for allocations.
+ * ARRAY_SIZE is larger than nr_writers, and we keep the mutex across
+ * both alloc and free, which insures we never run over our tail.
+ */
+#define ARRAY_SIZE (1048576 * nr_writers)
+#define ARRAY_POISON 0xDEADBEEF
+static int array_index;
+static struct test_array *test_array;
+
+static struct test_array *test_array_alloc(void)
+{
+ struct test_array *ret;
+ int index;
+
+ index = array_index % ARRAY_SIZE;
+ assert(test_array[index].a == ARRAY_POISON ||
+ test_array[index].a == 0);
+ ret = &test_array[index];
+ array_index++;
+ if (array_index == ARRAY_SIZE)
+ array_index = 0;
+ return ret;
+}
+
+static void test_array_free(struct test_array *ptr)
+{
+ if (!ptr)
+ return;
+ ptr->a = ARRAY_POISON;
+}
+
+void *thr_reader(void *_count)
+{
+ unsigned long long *count = _count;
+ struct test_array *local_ptr;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+
+ set_affinity();
+
+ rcu_register_thread();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ rcu_read_lock();
+ local_ptr = rcu_dereference(test_rcu_pointer);
+ rcu_debug_yield_read();
+ if (local_ptr)
+ assert(local_ptr->a == 8);
+ if (caa_unlikely(rduration))
+ loop_sleep(rduration);
+ rcu_read_unlock();
+ URCU_TLS(nr_reads)++;
+ if (caa_unlikely(!test_duration_read()))
+ break;
+ }
+
+ rcu_unregister_thread();
+
+ *count = URCU_TLS(nr_reads);
+ printf_verbose("thread_end %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+ return ((void*)1);
+
+}
+
+void *thr_writer(void *_count)
+{
+ unsigned long long *count = _count;
+ struct test_array *new, *old;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+
+ set_affinity();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ rcu_copy_mutex_lock();
+ new = test_array_alloc();
+ new->a = 8;
+ old = test_rcu_pointer;
+ rcu_assign_pointer(test_rcu_pointer, new);
+ if (caa_unlikely(wduration))
+ loop_sleep(wduration);
+ synchronize_rcu();
+ if (old)
+ old->a = 0;
+ test_array_free(old);
+ rcu_copy_mutex_unlock();
+ URCU_TLS(nr_writes)++;
+ if (caa_unlikely(!test_duration_write()))
+ break;
+ if (caa_unlikely(wdelay))
+ loop_sleep(wdelay);
+ }
+
+ printf_verbose("thread_end %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+ *count = URCU_TLS(nr_writes);
+ return ((void*)2);
+}
+
+void show_usage(int argc, char **argv)
+{
+ printf("Usage : %s nr_readers nr_writers duration (s) <OPTIONS>\n",
+ argv[0]);
+ printf("OPTIONS:\n");
+#ifdef DEBUG_YIELD
+ printf(" [-r] [-w] (yield reader and/or writer)\n");
+#endif
+ printf(" [-d delay] (writer period (us))\n");
+ printf(" [-c duration] (reader C.S. duration (in loops))\n");
+ printf(" [-e duration] (writer C.S. duration (in loops))\n");
+ printf(" [-v] (verbose output)\n");
+ printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ int err;
+ pthread_t *tid_reader, *tid_writer;
+ void *tret;
+ unsigned long long *count_reader, *count_writer;
+ unsigned long long tot_reads = 0, tot_writes = 0;
+ int i, a;
+
+ 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++) {
+ if (argv[i][0] != '-')
+ continue;
+ switch (argv[i][1]) {
+#ifdef DEBUG_YIELD
+ case 'r':
+ rcu_yield_active |= RCU_YIELD_READ;
+ break;
+ case 'w':
+ rcu_yield_active |= RCU_YIELD_WRITE;
+ break;
+#endif
+ case 'a':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ a = atoi(argv[++i]);
+ cpu_affinities[next_aff++] = a;
+ use_affinity = 1;
+ printf_verbose("Adding CPU %d affinity\n", a);
+ break;
+ case 'c':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ rduration = atol(argv[++i]);
+ break;
+ case 'd':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wdelay = atol(argv[++i]);
+ break;
+ case 'e':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wduration = atol(argv[++i]);
+ break;
+ case 'v':
+ verbose_mode = 1;
+ break;
+ }
+ }
+
+ printf_verbose("running test for %lu seconds, %u readers, %u writers.\n",
+ duration, nr_readers, nr_writers);
+ printf_verbose("Writer delay : %lu loops.\n", wdelay);
+ printf_verbose("Reader duration : %lu loops.\n", rduration);
+ printf_verbose("thread %-6s, tid %lu\n",
+ "main", urcu_get_thread_id());
+
+ test_array = calloc(1, sizeof(*test_array) * ARRAY_SIZE);
+ tid_reader = calloc(nr_readers, sizeof(*tid_reader));
+ tid_writer = calloc(nr_writers, sizeof(*tid_writer));
+ count_reader = calloc(nr_readers, sizeof(*count_reader));
+ count_writer = calloc(nr_writers, sizeof(*count_writer));
+
+ next_aff = 0;
+
+ for (i = 0; i < nr_readers; i++) {
+ err = pthread_create(&tid_reader[i], NULL, thr_reader,
+ &count_reader[i]);
+ if (err != 0)
+ exit(1);
+ }
+ for (i = 0; i < nr_writers; i++) {
+ err = pthread_create(&tid_writer[i], NULL, thr_writer,
+ &count_writer[i]);
+ if (err != 0)
+ exit(1);
+ }
+
+ cmm_smp_mb();
+
+ test_go = 1;
+
+ sleep(duration);
+
+ 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];
+ }
+
+ printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads,
+ tot_writes);
+ printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu "
+ "nr_writers %3u "
+ "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n",
+ argv[0], duration, nr_readers, rduration, wduration,
+ nr_writers, wdelay, tot_reads, tot_writes,
+ tot_reads + tot_writes);
+ test_array_free(test_rcu_pointer);
+ free(test_array);
+ free(tid_reader);
+ free(tid_writer);
+ free(count_reader);
+ free(count_writer);
+ return 0;
+}
--- /dev/null
+/*
+ * test_urcu.c
+ *
+ * Userspace RCU library - test program
+ *
+ * Copyright February 2009 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "config.h"
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <urcu/arch.h>
+#include <urcu/tls-compat.h>
+#include "cpuset.h"
+#include "thread-id.h"
+
+/* hardcoded number of CPUs */
+#define NR_CPUS 16384
+
+#ifndef DYNAMIC_LINK_TEST
+#define _LGPL_SOURCE
+#else
+#define rcu_debug_yield_read()
+#endif
+#include <urcu-bp.h>
+
+static volatile int test_go, test_stop;
+
+static unsigned long wdelay;
+
+static int *test_rcu_pointer;
+
+static unsigned long duration;
+
+/* read-side C.S. duration, in loops */
+static unsigned long rduration;
+
+/* write-side C.S. duration, in loops */
+static unsigned long wduration;
+
+static inline void loop_sleep(unsigned long loops)
+{
+ while (loops-- != 0)
+ caa_cpu_relax();
+}
+
+static int verbose_mode;
+
+#define printf_verbose(fmt, args...) \
+ do { \
+ if (verbose_mode) \
+ printf(fmt, args); \
+ } while (0)
+
+static unsigned int cpu_affinities[NR_CPUS];
+static unsigned int next_aff = 0;
+static int use_affinity = 0;
+
+pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void set_affinity(void)
+{
+#if HAVE_SCHED_SETAFFINITY
+ cpu_set_t mask;
+ int cpu, ret;
+#endif /* HAVE_SCHED_SETAFFINITY */
+
+ if (!use_affinity)
+ return;
+
+#if HAVE_SCHED_SETAFFINITY
+ ret = pthread_mutex_lock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+ cpu = cpu_affinities[next_aff++];
+ ret = pthread_mutex_unlock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+#if SCHED_SETAFFINITY_ARGS == 2
+ sched_setaffinity(0, &mask);
+#else
+ sched_setaffinity(0, sizeof(mask), &mask);
+#endif
+#endif /* HAVE_SCHED_SETAFFINITY */
+}
+
+/*
+ * returns 0 if test should end.
+ */
+static int test_duration_write(void)
+{
+ return !test_stop;
+}
+
+static int test_duration_read(void)
+{
+ return !test_stop;
+}
+
+static DEFINE_URCU_TLS(unsigned long long, nr_writes);
+static DEFINE_URCU_TLS(unsigned long long, nr_reads);
+
+static unsigned int nr_readers;
+static unsigned int nr_writers;
+
+pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void rcu_copy_mutex_lock(void)
+{
+ int ret;
+ ret = pthread_mutex_lock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+}
+
+void rcu_copy_mutex_unlock(void)
+{
+ int ret;
+
+ ret = pthread_mutex_unlock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+}
+
+void *thr_reader(void *_count)
+{
+ unsigned long long *count = _count;
+ int *local_ptr;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+
+ set_affinity();
+
+ rcu_register_thread();
+ assert(!rcu_read_ongoing());
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ rcu_read_lock();
+ assert(rcu_read_ongoing());
+ local_ptr = rcu_dereference(test_rcu_pointer);
+ rcu_debug_yield_read();
+ if (local_ptr)
+ assert(*local_ptr == 8);
+ if (caa_unlikely(rduration))
+ loop_sleep(rduration);
+ rcu_read_unlock();
+ URCU_TLS(nr_reads)++;
+ if (caa_unlikely(!test_duration_read()))
+ break;
+ }
+
+ rcu_unregister_thread();
+
+ *count = URCU_TLS(nr_reads);
+ printf_verbose("thread_end %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+ return ((void*)1);
+
+}
+
+void *thr_writer(void *_count)
+{
+ unsigned long long *count = _count;
+ int *new, *old;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+
+ set_affinity();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ new = malloc(sizeof(int));
+ *new = 8;
+ old = rcu_xchg_pointer(&test_rcu_pointer, new);
+ if (caa_unlikely(wduration))
+ loop_sleep(wduration);
+ synchronize_rcu();
+ if (old)
+ *old = 0;
+ free(old);
+ URCU_TLS(nr_writes)++;
+ if (caa_unlikely(!test_duration_write()))
+ break;
+ if (caa_unlikely(wdelay))
+ loop_sleep(wdelay);
+ }
+
+ printf_verbose("thread_end %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+ *count = URCU_TLS(nr_writes);
+ return ((void*)2);
+}
+
+void show_usage(int argc, char **argv)
+{
+ printf("Usage : %s nr_readers nr_writers duration (s) <OPTIONS>\n",
+ argv[0]);
+ printf("OPTIONS:\n");
+#ifdef DEBUG_YIELD
+ printf(" [-r] [-w] (yield reader and/or writer)\n");
+#endif
+ printf(" [-d delay] (writer period (us))\n");
+ printf(" [-c duration] (reader C.S. duration (in loops))\n");
+ printf(" [-e duration] (writer C.S. duration (in loops))\n");
+ printf(" [-v] (verbose output)\n");
+ printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ int err;
+ pthread_t *tid_reader, *tid_writer;
+ void *tret;
+ unsigned long long *count_reader, *count_writer;
+ unsigned long long tot_reads = 0, tot_writes = 0;
+ int i, a;
+
+ 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++) {
+ if (argv[i][0] != '-')
+ continue;
+ switch (argv[i][1]) {
+#ifdef DEBUG_YIELD
+ case 'r':
+ rcu_yield_active |= RCU_YIELD_READ;
+ break;
+ case 'w':
+ rcu_yield_active |= RCU_YIELD_WRITE;
+ break;
+#endif
+ case 'a':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ a = atoi(argv[++i]);
+ cpu_affinities[next_aff++] = a;
+ use_affinity = 1;
+ printf_verbose("Adding CPU %d affinity\n", a);
+ break;
+ case 'c':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ rduration = atol(argv[++i]);
+ break;
+ case 'd':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wdelay = atol(argv[++i]);
+ break;
+ case 'e':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wduration = atol(argv[++i]);
+ break;
+ case 'v':
+ verbose_mode = 1;
+ break;
+ }
+ }
+
+ printf_verbose("running test for %lu seconds, %u readers, %u writers.\n",
+ duration, nr_readers, nr_writers);
+ printf_verbose("Writer delay : %lu loops.\n", wdelay);
+ printf_verbose("Reader duration : %lu loops.\n", rduration);
+ printf_verbose("thread %-6s, tid %lu\n",
+ "main", urcu_get_thread_id());
+
+ tid_reader = calloc(nr_readers, sizeof(*tid_reader));
+ tid_writer = calloc(nr_writers, sizeof(*tid_writer));
+ count_reader = calloc(nr_readers, sizeof(*count_reader));
+ count_writer = calloc(nr_writers, sizeof(*count_writer));
+
+ next_aff = 0;
+
+ for (i = 0; i < nr_readers; i++) {
+ err = pthread_create(&tid_reader[i], NULL, thr_reader,
+ &count_reader[i]);
+ if (err != 0)
+ exit(1);
+ }
+ for (i = 0; i < nr_writers; i++) {
+ err = pthread_create(&tid_writer[i], NULL, thr_writer,
+ &count_writer[i]);
+ if (err != 0)
+ exit(1);
+ }
+
+ cmm_smp_mb();
+
+ test_go = 1;
+
+ sleep(duration);
+
+ 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];
+ }
+
+ printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads,
+ tot_writes);
+ printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu "
+ "nr_writers %3u "
+ "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n",
+ argv[0], duration, nr_readers, rduration, wduration,
+ nr_writers, wdelay, tot_reads, tot_writes,
+ tot_reads + tot_writes);
+ free(test_rcu_pointer);
+ free(tid_reader);
+ free(tid_writer);
+ free(count_reader);
+ free(count_writer);
+ return 0;
+}
--- /dev/null
+/*
+ * test_urcu_defer.c
+ *
+ * Userspace RCU library - test program (with automatic reclamation)
+ *
+ * Copyright February 2009 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "config.h"
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <urcu/arch.h>
+#include <urcu/tls-compat.h>
+#include "cpuset.h"
+#include "thread-id.h"
+
+/* hardcoded number of CPUs */
+#define NR_CPUS 16384
+
+#ifndef DYNAMIC_LINK_TEST
+#define _LGPL_SOURCE
+#else
+#define rcu_debug_yield_read()
+#endif
+#include <urcu.h>
+#include <urcu-defer.h>
+
+struct test_array {
+ int a;
+};
+
+static volatile int test_go, test_stop;
+
+static unsigned long wdelay;
+
+static struct test_array *test_rcu_pointer;
+
+static unsigned long duration;
+
+/* read-side C.S. duration, in loops */
+static unsigned long rduration;
+
+/* write-side C.S. duration, in loops */
+static unsigned long wduration;
+
+static inline void loop_sleep(unsigned long loops)
+{
+ while (loops-- != 0)
+ caa_cpu_relax();
+}
+
+static int verbose_mode;
+
+#define printf_verbose(fmt, args...) \
+ do { \
+ if (verbose_mode) \
+ printf(fmt, args); \
+ } while (0)
+
+static unsigned int cpu_affinities[NR_CPUS];
+static unsigned int next_aff = 0;
+static int use_affinity = 0;
+
+pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void set_affinity(void)
+{
+#if HAVE_SCHED_SETAFFINITY
+ cpu_set_t mask;
+ int cpu, ret;
+#endif /* HAVE_SCHED_SETAFFINITY */
+
+ if (!use_affinity)
+ return;
+
+#if HAVE_SCHED_SETAFFINITY
+ ret = pthread_mutex_lock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+ cpu = cpu_affinities[next_aff++];
+ ret = pthread_mutex_unlock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+#if SCHED_SETAFFINITY_ARGS == 2
+ sched_setaffinity(0, &mask);
+#else
+ sched_setaffinity(0, sizeof(mask), &mask);
+#endif
+#endif /* HAVE_SCHED_SETAFFINITY */
+}
+
+/*
+ * returns 0 if test should end.
+ */
+static int test_duration_write(void)
+{
+ return !test_stop;
+}
+
+static int test_duration_read(void)
+{
+ return !test_stop;
+}
+
+static DEFINE_URCU_TLS(unsigned long long, nr_writes);
+static DEFINE_URCU_TLS(unsigned long long, nr_reads);
+
+static
+unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes;
+
+static unsigned int nr_readers;
+static unsigned int nr_writers;
+
+pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void rcu_copy_mutex_lock(void)
+{
+ int ret;
+ ret = pthread_mutex_lock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+}
+
+void rcu_copy_mutex_unlock(void)
+{
+ int ret;
+
+ ret = pthread_mutex_unlock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+}
+
+void *thr_reader(void *_count)
+{
+ unsigned long long *count = _count;
+ struct test_array *local_ptr;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+
+ set_affinity();
+
+ rcu_register_thread();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ rcu_read_lock();
+ local_ptr = rcu_dereference(test_rcu_pointer);
+ rcu_debug_yield_read();
+ if (local_ptr)
+ assert(local_ptr->a == 8);
+ if (caa_unlikely(rduration))
+ loop_sleep(rduration);
+ rcu_read_unlock();
+ URCU_TLS(nr_reads)++;
+ if (caa_unlikely(!test_duration_read()))
+ break;
+ }
+
+ rcu_unregister_thread();
+
+ *count = URCU_TLS(nr_reads);
+ printf_verbose("thread_end %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+ return ((void*)1);
+
+}
+
+static void test_cb2(void *data)
+{
+}
+
+static void test_cb1(void *data)
+{
+}
+
+void *thr_writer(void *data)
+{
+ unsigned long wtidx = (unsigned long)data;
+ struct test_array *new, *old = NULL;
+ int ret;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+
+ set_affinity();
+
+ ret = rcu_defer_register_thread();
+ if (ret) {
+ printf("Error in rcu_defer_register_thread\n");
+ exit(-1);
+ }
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ new = malloc(sizeof(*new));
+ new->a = 8;
+ old = rcu_xchg_pointer(&test_rcu_pointer, new);
+ if (caa_unlikely(wduration))
+ loop_sleep(wduration);
+ defer_rcu(free, old);
+ defer_rcu(test_cb1, old);
+ defer_rcu(test_cb1, (void *)-2L);
+ defer_rcu(test_cb1, (void *)-2L);
+ defer_rcu(test_cb1, old);
+ defer_rcu(test_cb2, (void *)-2L);
+ defer_rcu(test_cb2, (void *)-4L);
+ defer_rcu(test_cb2, (void *)-2L);
+ URCU_TLS(nr_writes)++;
+ if (caa_unlikely(!test_duration_write()))
+ break;
+ if (caa_unlikely(wdelay))
+ loop_sleep(wdelay);
+ }
+
+ rcu_defer_unregister_thread();
+
+ printf_verbose("thread_end %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+ tot_nr_writes[wtidx] = URCU_TLS(nr_writes);
+ return ((void*)2);
+}
+
+void show_usage(int argc, char **argv)
+{
+ printf("Usage : %s nr_readers nr_writers duration (s) <OPTIONS>\n",
+ argv[0]);
+ printf("OPTIONS:\n");
+#ifdef DEBUG_YIELD
+ printf(" [-r] [-w] (yield reader and/or writer)\n");
+#endif
+ printf(" [-d delay] (writer period (us))\n");
+ printf(" [-c duration] (reader C.S. duration (in loops))\n");
+ printf(" [-e duration] (writer C.S. duration (in loops))\n");
+ printf(" [-v] (verbose output)\n");
+ printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ int err;
+ pthread_t *tid_reader, *tid_writer;
+ void *tret;
+ unsigned long long *count_reader;
+ unsigned long long tot_reads = 0, tot_writes = 0;
+ int i, a;
+
+ 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++) {
+ if (argv[i][0] != '-')
+ continue;
+ switch (argv[i][1]) {
+#ifdef DEBUG_YIELD
+ case 'r':
+ rcu_yield_active |= RCU_YIELD_READ;
+ break;
+ case 'w':
+ rcu_yield_active |= RCU_YIELD_WRITE;
+ break;
+#endif
+ case 'a':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ a = atoi(argv[++i]);
+ cpu_affinities[next_aff++] = a;
+ use_affinity = 1;
+ printf_verbose("Adding CPU %d affinity\n", a);
+ break;
+ case 'c':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ rduration = atol(argv[++i]);
+ break;
+ case 'd':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wdelay = atol(argv[++i]);
+ break;
+ case 'e':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wduration = atol(argv[++i]);
+ break;
+ case 'v':
+ verbose_mode = 1;
+ break;
+ }
+ }
+
+ printf_verbose("running test for %lu seconds, %u readers, %u writers.\n",
+ duration, nr_readers, nr_writers);
+ printf_verbose("Writer delay : %lu loops.\n", wdelay);
+ printf_verbose("Reader duration : %lu loops.\n", rduration);
+ printf_verbose("thread %-6s, tid %lu\n",
+ "main", urcu_get_thread_id());
+
+ tid_reader = calloc(nr_readers, sizeof(*tid_reader));
+ tid_writer = calloc(nr_writers, sizeof(*tid_writer));
+ count_reader = calloc(nr_readers, sizeof(*count_reader));
+ tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes));
+
+ next_aff = 0;
+
+ for (i = 0; i < nr_readers; i++) {
+ err = pthread_create(&tid_reader[i], NULL, thr_reader,
+ &count_reader[i]);
+ if (err != 0)
+ exit(1);
+ }
+ for (i = 0; i < nr_writers; i++) {
+ err = pthread_create(&tid_writer[i], NULL, thr_writer,
+ (void *)(long)i);
+ if (err != 0)
+ exit(1);
+ }
+
+ cmm_smp_mb();
+
+ test_go = 1;
+
+ sleep(duration);
+
+ 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 += tot_nr_writes[i];
+ }
+
+ printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads,
+ tot_writes);
+ printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu "
+ "nr_writers %3u "
+ "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n",
+ argv[0], duration, nr_readers, rduration, wduration,
+ nr_writers, wdelay, tot_reads, tot_writes,
+ tot_reads + tot_writes);
+ free(tid_reader);
+ free(tid_writer);
+ free(count_reader);
+ free(tot_nr_writes);
+
+ return 0;
+}
--- /dev/null
+/*
+ * test_urcu_gc.c
+ *
+ * Userspace RCU library - test program (with batch reclamation)
+ *
+ * Copyright February 2009 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "config.h"
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <urcu/arch.h>
+#include <urcu/tls-compat.h>
+#include "cpuset.h"
+#include "thread-id.h"
+
+/* hardcoded number of CPUs */
+#define NR_CPUS 16384
+
+#ifndef DYNAMIC_LINK_TEST
+#define _LGPL_SOURCE
+#else
+#define rcu_debug_yield_read()
+#endif
+#include <urcu.h>
+
+struct test_array {
+ int a;
+};
+
+static volatile int test_go, test_stop;
+
+static unsigned long wdelay;
+
+static struct test_array *test_rcu_pointer;
+
+static unsigned int reclaim_batch = 1;
+
+struct reclaim_queue {
+ void **queue; /* Beginning of queue */
+ void **head; /* Insert position */
+};
+
+static struct reclaim_queue *pending_reclaims;
+
+static unsigned long duration;
+
+/* read-side C.S. duration, in loops */
+static unsigned long rduration;
+
+/* write-side C.S. duration, in loops */
+static unsigned long wduration;
+
+static inline void loop_sleep(unsigned long loops)
+{
+ while (loops-- != 0)
+ caa_cpu_relax();
+}
+
+static int verbose_mode;
+
+#define printf_verbose(fmt, args...) \
+ do { \
+ if (verbose_mode) \
+ printf(fmt, args); \
+ } while (0)
+
+static unsigned int cpu_affinities[NR_CPUS];
+static unsigned int next_aff = 0;
+static int use_affinity = 0;
+
+pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void set_affinity(void)
+{
+#if HAVE_SCHED_SETAFFINITY
+ cpu_set_t mask;
+ int cpu, ret;
+#endif /* HAVE_SCHED_SETAFFINITY */
+
+ if (!use_affinity)
+ return;
+
+#if HAVE_SCHED_SETAFFINITY
+ ret = pthread_mutex_lock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+ cpu = cpu_affinities[next_aff++];
+ ret = pthread_mutex_unlock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+#if SCHED_SETAFFINITY_ARGS == 2
+ sched_setaffinity(0, &mask);
+#else
+ sched_setaffinity(0, sizeof(mask), &mask);
+#endif
+#endif /* HAVE_SCHED_SETAFFINITY */
+}
+
+/*
+ * returns 0 if test should end.
+ */
+static int test_duration_write(void)
+{
+ return !test_stop;
+}
+
+static int test_duration_read(void)
+{
+ return !test_stop;
+}
+
+static DEFINE_URCU_TLS(unsigned long long, nr_writes);
+static DEFINE_URCU_TLS(unsigned long long, nr_reads);
+
+static
+unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes;
+
+static unsigned int nr_readers;
+static unsigned int nr_writers;
+
+pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void rcu_copy_mutex_lock(void)
+{
+ int ret;
+ ret = pthread_mutex_lock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+}
+
+void rcu_copy_mutex_unlock(void)
+{
+ int ret;
+
+ ret = pthread_mutex_unlock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+}
+
+void *thr_reader(void *_count)
+{
+ unsigned long long *count = _count;
+ struct test_array *local_ptr;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+
+ set_affinity();
+
+ rcu_register_thread();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ rcu_read_lock();
+ local_ptr = rcu_dereference(test_rcu_pointer);
+ rcu_debug_yield_read();
+ if (local_ptr)
+ assert(local_ptr->a == 8);
+ if (caa_unlikely(rduration))
+ loop_sleep(rduration);
+ rcu_read_unlock();
+ URCU_TLS(nr_reads)++;
+ if (caa_unlikely(!test_duration_read()))
+ break;
+ }
+
+ rcu_unregister_thread();
+
+ *count = URCU_TLS(nr_reads);
+ printf_verbose("thread_end %s, tid %lu\n",
+ "reader", urcu_get_thread_id());
+ return ((void*)1);
+
+}
+
+static void rcu_gc_clear_queue(unsigned long wtidx)
+{
+ void **p;
+
+ /* Wait for Q.S and empty queue */
+ synchronize_rcu();
+
+ for (p = pending_reclaims[wtidx].queue;
+ p < pending_reclaims[wtidx].head; p++) {
+ /* poison */
+ if (*p)
+ ((struct test_array *)*p)->a = 0;
+ free(*p);
+ }
+ pending_reclaims[wtidx].head = pending_reclaims[wtidx].queue;
+}
+
+/* Using per-thread queue */
+static void rcu_gc_reclaim(unsigned long wtidx, void *old)
+{
+ /* Queue pointer */
+ *pending_reclaims[wtidx].head = old;
+ pending_reclaims[wtidx].head++;
+
+ if (caa_likely(pending_reclaims[wtidx].head - pending_reclaims[wtidx].queue
+ < reclaim_batch))
+ return;
+
+ rcu_gc_clear_queue(wtidx);
+}
+
+void *thr_writer(void *data)
+{
+ unsigned long wtidx = (unsigned long)data;
+#ifdef TEST_LOCAL_GC
+ struct test_array *old = NULL;
+#else
+ struct test_array *new, *old;
+#endif
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+
+ set_affinity();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+#ifndef TEST_LOCAL_GC
+ new = malloc(sizeof(*new));
+ new->a = 8;
+ old = rcu_xchg_pointer(&test_rcu_pointer, new);
+#endif
+ if (caa_unlikely(wduration))
+ loop_sleep(wduration);
+ rcu_gc_reclaim(wtidx, old);
+ URCU_TLS(nr_writes)++;
+ if (caa_unlikely(!test_duration_write()))
+ break;
+ if (caa_unlikely(wdelay))
+ loop_sleep(wdelay);
+ }
+
+ printf_verbose("thread_end %s, tid %lu\n",
+ "writer", urcu_get_thread_id());
+ tot_nr_writes[wtidx] = URCU_TLS(nr_writes);
+ return ((void*)2);
+}
+
+void show_usage(int argc, char **argv)
+{
+ printf("Usage : %s nr_readers nr_writers duration (s) <OPTIONS>\n",
+ argv[0]);
+ printf("OPTIONS:\n");
+#ifdef DEBUG_YIELD
+ printf(" [-r] [-w] (yield reader and/or writer)\n");
+#endif
+ printf(" [-d delay] (writer period (us))\n");
+ printf(" [-c duration] (reader C.S. duration (in loops))\n");
+ printf(" [-e duration] (writer C.S. duration (in loops))\n");
+ printf(" [-v] (verbose output)\n");
+ printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ int err;
+ pthread_t *tid_reader, *tid_writer;
+ void *tret;
+ unsigned long long *count_reader;
+ unsigned long long tot_reads = 0, tot_writes = 0;
+ int i, a;
+
+ 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++) {
+ if (argv[i][0] != '-')
+ continue;
+ switch (argv[i][1]) {
+#ifdef DEBUG_YIELD
+ case 'r':
+ rcu_yield_active |= RCU_YIELD_READ;
+ break;
+ case 'w':
+ rcu_yield_active |= RCU_YIELD_WRITE;
+ break;
+#endif
+ case 'a':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ a = atoi(argv[++i]);
+ cpu_affinities[next_aff++] = a;
+ use_affinity = 1;
+ printf_verbose("Adding CPU %d affinity\n", a);
+ break;
+ case 'b':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ reclaim_batch = atol(argv[++i]);
+ break;
+ case 'c':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ rduration = atol(argv[++i]);
+ break;
+ case 'd':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wdelay = atol(argv[++i]);
+ break;
+ case 'e':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wduration = atol(argv[++i]);
+ break;
+ case 'v':
+ verbose_mode = 1;
+ break;
+ }
+ }
+
+ printf_verbose("running test for %lu seconds, %u readers, %u writers.\n",
+ duration, nr_readers, nr_writers);
+ printf_verbose("Writer delay : %lu loops.\n", wdelay);
+ printf_verbose("Reader duration : %lu loops.\n", rduration);
+ printf_verbose("thread %-6s, tid %lu\n",
+ "main", urcu_get_thread_id());
+
+ tid_reader = calloc(nr_readers, sizeof(*tid_reader));
+ tid_writer = calloc(nr_writers, sizeof(*tid_writer));
+ count_reader = calloc(nr_readers, sizeof(*count_reader));
+ tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes));
+ pending_reclaims = calloc(nr_writers, sizeof(*pending_reclaims));
+ if (reclaim_batch * sizeof(*pending_reclaims[i].queue)
+ < CAA_CACHE_LINE_SIZE)
+ for (i = 0; i < nr_writers; i++)
+ pending_reclaims[i].queue = calloc(1, CAA_CACHE_LINE_SIZE);
+ else
+ for (i = 0; i < nr_writers; i++)
+ pending_reclaims[i].queue = calloc(reclaim_batch,
+ sizeof(*pending_reclaims[i].queue));
+ for (i = 0; i < nr_writers; i++)
+ pending_reclaims[i].head = pending_reclaims[i].queue;
+
+ next_aff = 0;
+
+ for (i = 0; i < nr_readers; i++) {
+ err = pthread_create(&tid_reader[i], NULL, thr_reader,
+ &count_reader[i]);
+ if (err != 0)
+ exit(1);
+ }
+ for (i = 0; i < nr_writers; i++) {
+ err = pthread_create(&tid_writer[i], NULL, thr_writer,
+ (void *)(long)i);
+ if (err != 0)
+ exit(1);
+ }
+
+ cmm_smp_mb();
+
+ test_go = 1;
+
+ sleep(duration);
+
+ 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 += tot_nr_writes[i];
+ rcu_gc_clear_queue(i);
+ }
+
+ printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads,
+ tot_writes);
+ printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu "
+ "nr_writers %3u "
+ "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu "
+ "batch %u\n",
+ argv[0], duration, nr_readers, rduration, wduration,
+ nr_writers, wdelay, tot_reads, tot_writes,
+ tot_reads + tot_writes, reclaim_batch);
+ free(tid_reader);
+ free(tid_writer);
+ free(count_reader);
+ free(tot_nr_writes);
+ for (i = 0; i < nr_writers; i++)
+ free(pending_reclaims[i].queue);
+ free(pending_reclaims);
+
+ return 0;
+}
--- /dev/null
+/*
+ * test_urcu_hash.c
+ *
+ * Userspace RCU library - test program
+ *
+ * Copyright 2009-2012 - Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "test_urcu_hash.h"
+
+enum test_hash {
+ TEST_HASH_RW,
+ TEST_HASH_UNIQUE,
+};
+
+struct test_hash_cb {
+ void (*sigusr1)(int signo);
+ void (*sigusr2)(int signo);
+ void *(*thr_reader)(void *_count);
+ void *(*thr_writer)(void *_count);
+ int (*populate_hash)(void);
+};
+
+static
+struct test_hash_cb test_hash_cb[] = {
+ [TEST_HASH_RW] = {
+ test_hash_rw_sigusr1_handler,
+ test_hash_rw_sigusr2_handler,
+ test_hash_rw_thr_reader,
+ test_hash_rw_thr_writer,
+ test_hash_rw_populate_hash,
+ },
+ [TEST_HASH_UNIQUE] = {
+ test_hash_unique_sigusr1_handler,
+ test_hash_unique_sigusr2_handler,
+ test_hash_unique_thr_reader,
+ test_hash_unique_thr_writer,
+ test_hash_unique_populate_hash,
+ },
+
+};
+
+static enum test_hash test_choice = TEST_HASH_RW;
+
+void (*get_sigusr1_cb(void))(int)
+{
+ return test_hash_cb[test_choice].sigusr1;
+}
+
+void (*get_sigusr2_cb(void))(int)
+{
+ return test_hash_cb[test_choice].sigusr2;
+}
+
+void *(*get_thr_reader_cb(void))(void *)
+{
+ return test_hash_cb[test_choice].thr_reader;
+}
+
+void *(*get_thr_writer_cb(void))(void *)
+{
+ return test_hash_cb[test_choice].thr_writer;
+}
+
+int (*get_populate_hash_cb(void))(void)
+{
+ return test_hash_cb[test_choice].populate_hash;
+}
+
+DEFINE_URCU_TLS(unsigned int, rand_lookup);
+DEFINE_URCU_TLS(unsigned long, nr_add);
+DEFINE_URCU_TLS(unsigned long, nr_addexist);
+DEFINE_URCU_TLS(unsigned long, nr_del);
+DEFINE_URCU_TLS(unsigned long, nr_delnoent);
+DEFINE_URCU_TLS(unsigned long, lookup_fail);
+DEFINE_URCU_TLS(unsigned long, lookup_ok);
+
+struct cds_lfht *test_ht;
+
+volatile int test_go, test_stop;
+
+unsigned long wdelay;
+
+unsigned long duration;
+
+/* read-side C.S. duration, in loops */
+unsigned long rduration;
+
+unsigned long init_hash_size = DEFAULT_HASH_SIZE;
+unsigned long min_hash_alloc_size = DEFAULT_MIN_ALLOC_SIZE;
+unsigned long max_hash_buckets_size = (1UL << 20);
+unsigned long init_populate;
+int opt_auto_resize;
+int add_only, add_unique, add_replace;
+const struct cds_lfht_mm_type *memory_backend;
+
+unsigned long init_pool_offset, lookup_pool_offset, write_pool_offset;
+unsigned long init_pool_size = DEFAULT_RAND_POOL,
+ lookup_pool_size = DEFAULT_RAND_POOL,
+ write_pool_size = DEFAULT_RAND_POOL;
+int validate_lookup;
+unsigned long nr_hash_chains; /* 0: normal table, other: number of hash chains */
+
+int count_pipe[2];
+
+int verbose_mode;
+
+unsigned int cpu_affinities[NR_CPUS];
+unsigned int next_aff = 0;
+int use_affinity = 0;
+
+pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+DEFINE_URCU_TLS(unsigned long long, nr_writes);
+DEFINE_URCU_TLS(unsigned long long, nr_reads);
+
+unsigned int nr_readers;
+unsigned int nr_writers;
+
+static pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void set_affinity(void)
+{
+#if HAVE_SCHED_SETAFFINITY
+ cpu_set_t mask;
+ int cpu, ret;
+#endif /* HAVE_SCHED_SETAFFINITY */
+
+ if (!use_affinity)
+ return;
+
+#if HAVE_SCHED_SETAFFINITY
+ ret = pthread_mutex_lock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+ cpu = cpu_affinities[next_aff++];
+ ret = pthread_mutex_unlock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+#if SCHED_SETAFFINITY_ARGS == 2
+ sched_setaffinity(0, &mask);
+#else
+ sched_setaffinity(0, sizeof(mask), &mask);
+#endif
+#endif /* HAVE_SCHED_SETAFFINITY */
+}
+
+void rcu_copy_mutex_lock(void)
+{
+ int ret;
+ ret = pthread_mutex_lock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+}
+
+void rcu_copy_mutex_unlock(void)
+{
+ int ret;
+
+ ret = pthread_mutex_unlock(&rcu_copy_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+}
+
+unsigned long test_compare(const void *key1, size_t key1_len,
+ const void *key2, size_t key2_len)
+{
+ if (caa_unlikely(key1_len != key2_len))
+ return -1;
+ assert(key1_len == sizeof(unsigned long));
+ if (key1 == key2)
+ return 0;
+ else
+ return 1;
+}
+
+void *thr_count(void *arg)
+{
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "counter", urcu_get_thread_id());
+
+ rcu_register_thread();
+
+ for (;;) {
+ unsigned long count;
+ long approx_before, approx_after;
+ ssize_t len;
+ char buf[1];
+
+ rcu_thread_offline();
+ len = read(count_pipe[0], buf, 1);
+ rcu_thread_online();
+ if (caa_unlikely(!test_duration_read()))
+ break;
+ if (len != 1)
+ continue;
+ /* Accounting */
+ printf("Counting nodes... ");
+ fflush(stdout);
+ rcu_read_lock();
+ cds_lfht_count_nodes(test_ht, &approx_before, &count,
+ &approx_after);
+ rcu_read_unlock();
+ printf("done.\n");
+ printf("Approximation before node accounting: %ld nodes.\n",
+ approx_before);
+ printf("Accounting of nodes in the hash table: "
+ "%lu nodes.\n",
+ count);
+ printf("Approximation after node accounting: %ld nodes.\n",
+ approx_after);
+ }
+ rcu_unregister_thread();
+ return NULL;
+}
+
+void free_node_cb(struct rcu_head *head)
+{
+ struct lfht_test_node *node =
+ caa_container_of(head, struct lfht_test_node, head);
+ free(node);
+}
+
+static
+void test_delete_all_nodes(struct cds_lfht *ht)
+{
+ struct cds_lfht_iter iter;
+ struct lfht_test_node *node;
+ unsigned long count = 0;
+
+ cds_lfht_for_each_entry(ht, &iter, node, node) {
+ int ret;
+
+ ret = cds_lfht_del(test_ht, cds_lfht_iter_get_node(&iter));
+ assert(!ret);
+ call_rcu(&node->head, free_node_cb);
+ count++;
+ }
+ printf("deleted %lu nodes.\n", count);
+}
+
+void show_usage(int argc, char **argv)
+{
+ printf("Usage : %s nr_readers nr_writers duration (s) <OPTIONS>\n",
+ argv[0]);
+ printf("OPTIONS:\n");
+#ifdef DEBUG_YIELD
+ printf(" [-r] [-w] (yield reader and/or writer)\n");
+#endif
+ printf(" [-d delay] (writer period (us))\n");
+ printf(" [-c duration] (reader C.S. duration (in loops))\n");
+ printf(" [-v] (verbose output)\n");
+ printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
+ printf(" [-h size] (initial number of buckets)\n");
+ printf(" [-m size] (minimum number of allocated buckets)\n");
+ printf(" [-n size] (maximum number of buckets)\n");
+printf(" [not -u nor -s] Add entries (supports redundant keys).\n");
+ printf(" [-u] Uniquify add (no redundant keys).\n");
+ printf(" [-s] Replace (swap) entries.\n");
+ printf(" [-i] Add only (no removal).\n");
+ printf(" [-k nr_nodes] Number of nodes to insert initially.\n");
+ printf(" [-A] Automatically resize hash table.\n");
+ printf(" [-B order|chunk|mmap] Specify the memory backend.\n");
+ printf(" [-R offset] Lookup pool offset.\n");
+ printf(" [-S offset] Write pool offset.\n");
+ printf(" [-T offset] Init pool offset.\n");
+ printf(" [-M size] Lookup pool size.\n");
+ printf(" [-N size] Write pool size.\n");
+ printf(" [-O size] Init pool size.\n");
+ printf(" [-V] Validate lookups of init values.\n");
+ printf(" (use with filled init pool, same lookup range,\n");
+ printf(" with different write range)\n");
+ printf(" [-U] Uniqueness test.\n");
+ printf(" [-C] Number of hash chains.\n");
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ pthread_t *tid_reader, *tid_writer;
+ pthread_t tid_count;
+ void *tret;
+ 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 long count;
+ long approx_before, approx_after;
+ int i, a, ret, err, mainret = 0;
+ struct sigaction act;
+ unsigned int remain;
+ unsigned int nr_readers_created = 0, nr_writers_created = 0;
+ long long nr_leaked;
+
+ if (argc < 4) {
+ show_usage(argc, argv);
+ mainret = 1;
+ goto end;
+ }
+
+ err = sscanf(argv[1], "%u", &nr_readers);
+ if (err != 1) {
+ show_usage(argc, argv);
+ mainret = 1;
+ goto end;
+ }
+
+ err = sscanf(argv[2], "%u", &nr_writers);
+ if (err != 1) {
+ show_usage(argc, argv);
+ mainret = 1;
+ goto end;
+ }
+
+ err = sscanf(argv[3], "%lu", &duration);
+ if (err != 1) {
+ show_usage(argc, argv);
+ mainret = 1;
+ goto end;
+ }
+
+ for (i = 4; i < argc; i++) {
+ if (argv[i][0] != '-')
+ continue;
+ switch (argv[i][1]) {
+#ifdef DEBUG_YIELD
+ case 'r':
+ yield_active |= YIELD_READ;
+ break;
+ case 'w':
+ yield_active |= YIELD_WRITE;
+ break;
+#endif
+ case 'a':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ mainret = 1;
+ goto end;
+ }
+ a = atoi(argv[++i]);
+ cpu_affinities[next_aff++] = a;
+ use_affinity = 1;
+ printf_verbose("Adding CPU %d affinity\n", a);
+ break;
+ case 'c':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ mainret = 1;
+ goto end;
+ }
+ rduration = atol(argv[++i]);
+ break;
+ case 'd':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ mainret = 1;
+ goto end;
+ }
+ wdelay = atol(argv[++i]);
+ break;
+ case 'v':
+ verbose_mode = 1;
+ break;
+ case 'h':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ mainret = 1;
+ goto end;
+ }
+ init_hash_size = atol(argv[++i]);
+ break;
+ case 'm':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ mainret = 1;
+ goto end;
+ }
+ min_hash_alloc_size = atol(argv[++i]);
+ break;
+ case 'n':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ mainret = 1;
+ goto end;
+ }
+ max_hash_buckets_size = atol(argv[++i]);
+ break;
+ case 'u':
+ if (add_replace) {
+ printf("Please specify at most one of -s or -u.\n");
+ exit(-1);
+ }
+ add_unique = 1;
+ break;
+ case 's':
+ if (add_unique) {
+ printf("Please specify at most one of -s or -u.\n");
+ exit(-1);
+ }
+ add_replace = 1;
+ break;
+ case 'i':
+ add_only = 1;
+ break;
+ case 'k':
+ init_populate = atol(argv[++i]);
+ break;
+ case 'A':
+ opt_auto_resize = 1;
+ break;
+ case 'B':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ mainret = 1;
+ goto end;
+ }
+ i++;
+ if (!strcmp("order", argv[i]))
+ memory_backend = &cds_lfht_mm_order;
+ else if (!strcmp("chunk", argv[i]))
+ memory_backend = &cds_lfht_mm_chunk;
+ else if (!strcmp("mmap", argv[i]))
+ memory_backend = &cds_lfht_mm_mmap;
+ else {
+ printf("Please specify memory backend with order|chunk|mmap.\n");
+ mainret = 1;
+ goto end;
+ }
+ break;
+ case 'R':
+ lookup_pool_offset = atol(argv[++i]);
+ break;
+ case 'S':
+ write_pool_offset = atol(argv[++i]);
+ break;
+ case 'T':
+ init_pool_offset = atol(argv[++i]);
+ break;
+ case 'M':
+ lookup_pool_size = atol(argv[++i]);
+ break;
+ case 'N':
+ write_pool_size = atol(argv[++i]);
+ break;
+ case 'O':
+ init_pool_size = atol(argv[++i]);
+ break;
+ case 'V':
+ validate_lookup = 1;
+ break;
+ case 'U':
+ test_choice = TEST_HASH_UNIQUE;
+ break;
+ case 'C':
+ nr_hash_chains = atol(argv[++i]);
+ break;
+ }
+ }
+
+ /* Check if hash size is power of 2 */
+ if (init_hash_size && init_hash_size & (init_hash_size - 1)) {
+ printf("Error: Initial number of buckets (%lu) is not a power of 2.\n",
+ init_hash_size);
+ mainret = 1;
+ goto end;
+ }
+
+ if (min_hash_alloc_size && min_hash_alloc_size & (min_hash_alloc_size - 1)) {
+ printf("Error: Minimum number of allocated buckets (%lu) is not a power of 2.\n",
+ min_hash_alloc_size);
+ mainret = 1;
+ goto end;
+ }
+
+ if (max_hash_buckets_size && max_hash_buckets_size & (max_hash_buckets_size - 1)) {
+ printf("Error: Maximum number of buckets (%lu) is not a power of 2.\n",
+ max_hash_buckets_size);
+ mainret = 1;
+ goto end;
+ }
+
+ memset(&act, 0, sizeof(act));
+ ret = sigemptyset(&act.sa_mask);
+ if (ret == -1) {
+ perror("sigemptyset");
+ mainret = 1;
+ goto end;
+ }
+ act.sa_handler = get_sigusr1_cb();
+ act.sa_flags = SA_RESTART;
+ ret = sigaction(SIGUSR1, &act, NULL);
+ if (ret == -1) {
+ perror("sigaction");
+ mainret = 1;
+ goto end;
+ }
+
+ act.sa_handler = get_sigusr2_cb();
+ act.sa_flags = SA_RESTART;
+ ret = sigaction(SIGUSR2, &act, NULL);
+ if (ret == -1) {
+ perror("sigaction");
+ mainret = 1;
+ goto end;
+ }
+
+ printf_verbose("running test for %lu seconds, %u readers, %u writers.\n",
+ 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%s.\n",
+ add_only ? " add only" : " add/remove",
+ add_unique ? " uniquify" : ( add_replace ? " replace" : " insert"));
+ printf_verbose("Initial number of buckets: %lu buckets.\n", init_hash_size);
+ printf_verbose("Minimum number of allocated buckets: %lu buckets.\n", min_hash_alloc_size);
+ printf_verbose("Maximum number of buckets: %lu buckets.\n", max_hash_buckets_size);
+ 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);
+ printf_verbose("Number of hash chains: %lu.\n",
+ nr_hash_chains);
+ printf_verbose("thread %-6s, tid %lu\n",
+ "main", urcu_get_thread_id());
+
+ tid_reader = calloc(nr_readers, sizeof(*tid_reader));
+ if (!tid_reader) {
+ mainret = 1;
+ goto end;
+ }
+ tid_writer = calloc(nr_writers, sizeof(*tid_writer));
+ if (!tid_writer) {
+ mainret = 1;
+ goto end_free_tid_reader;
+ }
+ count_reader = calloc(nr_readers, sizeof(*count_reader));
+ if (!count_reader) {
+ mainret = 1;
+ goto end_free_tid_writer;
+ }
+ count_writer = calloc(nr_writers, sizeof(*count_writer));
+ if (!count_writer) {
+ mainret = 1;
+ goto end_free_count_reader;
+ }
+
+ err = create_all_cpu_call_rcu_data(0);
+ if (err) {
+ printf("Per-CPU call_rcu() worker threads unavailable. Using default global worker thread.\n");
+ }
+
+ if (memory_backend) {
+ test_ht = _cds_lfht_new(init_hash_size, min_hash_alloc_size,
+ max_hash_buckets_size,
+ (opt_auto_resize ? CDS_LFHT_AUTO_RESIZE : 0) |
+ CDS_LFHT_ACCOUNTING, memory_backend,
+ &rcu_flavor, NULL);
+ } else {
+ test_ht = cds_lfht_new(init_hash_size, min_hash_alloc_size,
+ max_hash_buckets_size,
+ (opt_auto_resize ? CDS_LFHT_AUTO_RESIZE : 0) |
+ CDS_LFHT_ACCOUNTING, NULL);
+ }
+ if (!test_ht) {
+ printf("Error allocating hash table.\n");
+ mainret = 1;
+ goto end_free_call_rcu_data;
+ }
+
+ /*
+ * Hash Population needs to be seen as a RCU reader
+ * thread from the point of view of resize.
+ */
+ rcu_register_thread();
+ ret = (get_populate_hash_cb())();
+ assert(!ret);
+
+ rcu_thread_offline();
+
+ next_aff = 0;
+
+ ret = pipe(count_pipe);
+ if (ret == -1) {
+ perror("pipe");
+ mainret = 1;
+ goto end_online;
+ }
+
+ /* spawn counter thread */
+ err = pthread_create(&tid_count, NULL, thr_count,
+ NULL);
+ if (err != 0) {
+ errno = err;
+ mainret = 1;
+ perror("pthread_create");
+ goto end_close_pipe;
+ }
+
+ for (i = 0; i < nr_readers; i++) {
+ err = pthread_create(&tid_reader[i],
+ NULL, get_thr_reader_cb(),
+ &count_reader[i]);
+ if (err != 0) {
+ errno = err;
+ mainret = 1;
+ perror("pthread_create");
+ goto end_pthread_join;
+ }
+ nr_readers_created++;
+ }
+ for (i = 0; i < nr_writers; i++) {
+ err = pthread_create(&tid_writer[i],
+ NULL, get_thr_writer_cb(),
+ &count_writer[i]);
+ if (err != 0) {
+ errno = err;
+ mainret = 1;
+ perror("pthread_create");
+ goto end_pthread_join;
+ }
+ nr_writers_created++;
+ }
+
+ cmm_smp_mb();
+
+ test_go = 1;
+
+ remain = duration;
+ do {
+ remain = sleep(remain);
+ } while (remain > 0);
+
+ test_stop = 1;
+
+end_pthread_join:
+ for (i = 0; i < nr_readers_created; i++) {
+ err = pthread_join(tid_reader[i], &tret);
+ if (err != 0) {
+ errno = err;
+ mainret = 1;
+ perror("pthread_join");
+ }
+ tot_reads += count_reader[i];
+ }
+ for (i = 0; i < nr_writers_created; i++) {
+ err = pthread_join(tid_writer[i], &tret);
+ if (err != 0) {
+ errno = err;
+ mainret = 1;
+ perror("pthread_join");
+ }
+ 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;
+ }
+
+ /* teardown counter thread */
+ act.sa_handler = SIG_IGN;
+ act.sa_flags = SA_RESTART;
+ ret = sigaction(SIGUSR2, &act, NULL);
+ if (ret == -1) {
+ mainret = 1;
+ perror("sigaction");
+ }
+ {
+ char msg[1] = { 0x42 };
+ ssize_t ret;
+
+ do {
+ ret = write(count_pipe[1], msg, 1); /* wakeup thread */
+ } while (ret == -1L && errno == EINTR);
+ }
+ err = pthread_join(tid_count, &tret);
+ if (err != 0) {
+ errno = err;
+ mainret = 1;
+ perror("pthread_join");
+ }
+
+end_close_pipe:
+ for (i = 0; i < 2; i++) {
+ err = close(count_pipe[i]);
+ if (err) {
+ mainret = 1;
+ perror("close pipe");
+ }
+ }
+ fflush(stdout);
+end_online:
+ rcu_thread_online();
+ rcu_read_lock();
+ printf("Counting nodes... ");
+ cds_lfht_count_nodes(test_ht, &approx_before, &count, &approx_after);
+ printf("done.\n");
+ test_delete_all_nodes(test_ht);
+ rcu_read_unlock();
+ rcu_thread_offline();
+ if (count) {
+ printf("Approximation before node accounting: %ld nodes.\n",
+ approx_before);
+ printf("Nodes deleted from hash table before destroy: "
+ "%lu nodes.\n",
+ count);
+ printf("Approximation after node accounting: %ld nodes.\n",
+ approx_after);
+ }
+
+ ret = cds_lfht_destroy(test_ht, NULL);
+ if (ret) {
+ printf_verbose("final delete aborted\n");
+ mainret = 1;
+ } else {
+ printf_verbose("final delete success\n");
+ }
+ printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads,
+ tot_writes);
+ nr_leaked = (long long) tot_add + init_populate - tot_remove - count;
+ printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu "
+ "nr_writers %3u "
+ "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu "
+ "nr_add %12llu nr_add_fail %12llu nr_remove %12llu nr_leaked %12lld\n",
+ argv[0], duration, nr_readers, rduration,
+ nr_writers, wdelay, tot_reads, tot_writes,
+ tot_reads + tot_writes, tot_add, tot_add_exist, tot_remove,
+ nr_leaked);
+ if (nr_leaked != 0) {
+ mainret = 1;
+ printf("WARNING: %lld nodes were leaked!\n", nr_leaked);
+ }
+
+ rcu_unregister_thread();
+end_free_call_rcu_data:
+ free_all_cpu_call_rcu_data();
+ free(count_writer);
+end_free_count_reader:
+ free(count_reader);
+end_free_tid_writer:
+ free(tid_writer);
+end_free_tid_reader:
+ free(tid_reader);
+end:
+ if (!mainret)
+ exit(EXIT_SUCCESS);
+ else
+ exit(EXIT_FAILURE);
+}
--- /dev/null
+#ifndef _TEST_URCU_HASH_H
+#define _TEST_URCU_HASH_H
+
+/*
+ * test_urcu_hash.h
+ *
+ * Userspace RCU library - test program
+ *
+ * Copyright 2009-2012 - Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <urcu/tls-compat.h>
+#include "cpuset.h"
+#include "thread-id.h"
+
+#define DEFAULT_HASH_SIZE 32
+#define DEFAULT_MIN_ALLOC_SIZE 1
+#define DEFAULT_RAND_POOL 1000000
+
+/*
+ * Note: the hash seed should be a random value for hash tables
+ * targeting production environments to provide protection against
+ * denial of service attacks. We keep it a static value within this test
+ * program to compare identical benchmark runs.
+ */
+#define TEST_HASH_SEED 0x42UL
+
+/* hardcoded number of CPUs */
+#define NR_CPUS 16384
+
+#ifdef POISON_FREE
+#define poison_free(ptr) \
+ do { \
+ memset(ptr, 0x42, sizeof(*(ptr))); \
+ free(ptr); \
+ } while (0)
+#else
+#define poison_free(ptr) free(ptr)
+#endif
+
+#ifndef DYNAMIC_LINK_TEST
+#define _LGPL_SOURCE
+#else
+#define debug_yield_read()
+#endif
+#include <urcu-qsbr.h>
+#include <urcu/rculfhash.h>
+#include <urcu-call-rcu.h>
+
+struct wr_count {
+ unsigned long update_ops;
+ unsigned long add;
+ unsigned long add_exist;
+ unsigned long remove;
+};
+
+extern DECLARE_URCU_TLS(unsigned int, rand_lookup);
+extern DECLARE_URCU_TLS(unsigned long, nr_add);
+extern DECLARE_URCU_TLS(unsigned long, nr_addexist);
+extern DECLARE_URCU_TLS(unsigned long, nr_del);
+extern DECLARE_URCU_TLS(unsigned long, nr_delnoent);
+extern DECLARE_URCU_TLS(unsigned long, lookup_fail);
+extern DECLARE_URCU_TLS(unsigned long, lookup_ok);
+
+extern struct cds_lfht *test_ht;
+
+struct test_data {
+ int a;
+ int b;
+};
+
+struct lfht_test_node {
+ struct cds_lfht_node node;
+ void *key;
+ unsigned int key_len;
+ /* cache-cold for iteration */
+ struct rcu_head head;
+};
+
+static inline struct lfht_test_node *
+to_test_node(struct cds_lfht_node *node)
+{
+ return caa_container_of(node, struct lfht_test_node, node);
+}
+
+static inline
+void lfht_test_node_init(struct lfht_test_node *node, void *key,
+ size_t key_len)
+{
+ cds_lfht_node_init(&node->node);
+ node->key = key;
+ node->key_len = key_len;
+}
+
+static inline struct lfht_test_node *
+cds_lfht_iter_get_test_node(struct cds_lfht_iter *iter)
+{
+ return to_test_node(cds_lfht_iter_get_node(iter));
+}
+
+extern volatile int test_go, test_stop;
+
+extern unsigned long wdelay;
+
+extern unsigned long duration;
+
+/* read-side C.S. duration, in loops */
+extern unsigned long rduration;
+
+extern unsigned long init_hash_size;
+extern unsigned long min_hash_alloc_size;
+extern unsigned long max_hash_buckets_size;
+extern unsigned long init_populate;
+extern int opt_auto_resize;
+extern int add_only, add_unique, add_replace;
+extern const struct cds_lfht_mm_type *memory_backend;
+
+extern unsigned long init_pool_offset, lookup_pool_offset, write_pool_offset;
+extern unsigned long init_pool_size,
+ lookup_pool_size,
+ write_pool_size;
+extern int validate_lookup;
+
+extern unsigned long nr_hash_chains;
+
+extern int count_pipe[2];
+
+static inline void loop_sleep(unsigned long loops)
+{
+ while (loops-- != 0)
+ caa_cpu_relax();
+}
+
+extern int verbose_mode;
+
+#define printf_verbose(fmt, args...) \
+ do { \
+ if (verbose_mode) \
+ printf(fmt, ## args); \
+ } while (0)
+
+extern unsigned int cpu_affinities[NR_CPUS];
+extern unsigned int next_aff;
+extern int use_affinity;
+
+extern pthread_mutex_t affinity_mutex;
+
+void set_affinity(void);
+
+/*
+ * returns 0 if test should end.
+ */
+static inline int test_duration_write(void)
+{
+ return !test_stop;
+}
+
+static inline int test_duration_read(void)
+{
+ return !test_stop;
+}
+
+extern DECLARE_URCU_TLS(unsigned long long, nr_writes);
+extern DECLARE_URCU_TLS(unsigned long long, nr_reads);
+
+extern unsigned int nr_readers;
+extern unsigned int nr_writers;
+
+void rcu_copy_mutex_lock(void);
+void rcu_copy_mutex_unlock(void);
+
+/*
+ * Hash function
+ * Source: http://burtleburtle.net/bob/c/lookup3.c
+ * Originally Public Domain
+ */
+
+#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
+
+#define mix(a, b, c) \
+do { \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c, 16); c += b; \
+ b -= a; b ^= rot(a, 19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+} while (0)
+
+#define final(a, b, c) \
+{ \
+ c ^= b; c -= rot(b, 14); \
+ a ^= c; a -= rot(c, 11); \
+ b ^= a; b -= rot(a, 25); \
+ c ^= b; c -= rot(b, 16); \
+ a ^= c; a -= rot(c, 4);\
+ b ^= a; b -= rot(a, 14); \
+ c ^= b; c -= rot(b, 24); \
+}
+
+static inline __attribute__((unused))
+uint32_t hash_u32(
+ const uint32_t *k, /* the key, an array of uint32_t values */
+ size_t length, /* the length of the key, in uint32_ts */
+ uint32_t initval) /* the previous hash, or an arbitrary value */
+{
+ uint32_t a, b, c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + (((uint32_t) length) << 2) + initval;
+
+ /*----------------------------------------- handle most of the key */
+ while (length > 3) {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a, b, c);
+ length -= 3;
+ k += 3;
+ }
+
+ /*----------------------------------- handle the last 3 uint32_t's */
+ switch (length) { /* all the case statements fall through */
+ case 3: c += k[2];
+ case 2: b += k[1];
+ case 1: a += k[0];
+ final(a, b, c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /*---------------------------------------------- report the result */
+ return c;
+}
+
+static inline
+void hashword2(
+ const uint32_t *k, /* the key, an array of uint32_t values */
+ size_t length, /* the length of the key, in uint32_ts */
+ uint32_t *pc, /* IN: seed OUT: primary hash value */
+ uint32_t *pb) /* IN: more seed OUT: secondary hash value */
+{
+ uint32_t a, b, c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t) (length << 2)) + *pc;
+ c += *pb;
+
+ /*----------------------------------------- handle most of the key */
+ while (length > 3) {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a, b, c);
+ length -= 3;
+ k += 3;
+ }
+
+ /*----------------------------------- handle the last 3 uint32_t's */
+ switch (length) { /* all the case statements fall through */
+ case 3: c += k[2];
+ case 2: b += k[1];
+ case 1: a += k[0];
+ final(a, b, c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /*---------------------------------------------- report the result */
+ *pc = c;
+ *pb = b;
+}
+
+#if (CAA_BITS_PER_LONG == 32)
+static inline
+unsigned long test_hash_mix(const void *_key, size_t length, unsigned long seed)
+{
+ unsigned int key = (unsigned int) _key;
+
+ assert(length == sizeof(unsigned int));
+ return hash_u32(&key, 1, seed);
+}
+#else
+static inline
+unsigned long test_hash_mix(const void *_key, size_t length, unsigned long seed)
+{
+ union {
+ uint64_t v64;
+ uint32_t v32[2];
+ } v;
+ union {
+ uint64_t v64;
+ uint32_t v32[2];
+ } key;
+
+ assert(length == sizeof(unsigned long));
+ v.v64 = (uint64_t) seed;
+ key.v64 = (uint64_t) _key;
+ hashword2(key.v32, 2, &v.v32[0], &v.v32[1]);
+ return v.v64;
+}
+#endif
+
+/*
+ * Hash function with nr_hash_chains != 0 for testing purpose only!
+ * Creates very long hash chains, deteriorating the hash table into a
+ * few linked lists, depending on the nr_hash_chains value. The purpose
+ * of this test is to check how the hash table behaves with hash chains
+ * containing different values, which is a rare case in a normal hash
+ * table.
+ */
+static inline
+unsigned long test_hash(const void *_key, size_t length,
+ unsigned long seed)
+{
+ if (nr_hash_chains == 0) {
+ return test_hash_mix(_key, length, seed);
+ } else {
+ unsigned long v;
+
+ assert(length == sizeof(unsigned long));
+ v = (unsigned long) _key;
+ return v % nr_hash_chains;
+ }
+}
+
+unsigned long test_compare(const void *key1, size_t key1_len,
+ const void *key2, size_t key2_len);
+
+static inline
+int test_match(struct cds_lfht_node *node, const void *key)
+{
+ struct lfht_test_node *test_node = to_test_node(node);
+
+ return !test_compare(test_node->key, test_node->key_len,
+ key, sizeof(unsigned long));
+}
+
+static inline
+void cds_lfht_test_lookup(struct cds_lfht *ht, void *key, size_t key_len,
+ struct cds_lfht_iter *iter)
+{
+ assert(key_len == sizeof(unsigned long));
+
+ cds_lfht_lookup(ht, test_hash(key, key_len, TEST_HASH_SEED),
+ test_match, key, iter);
+}
+
+void free_node_cb(struct rcu_head *head);
+
+/* rw test */
+void test_hash_rw_sigusr1_handler(int signo);
+void test_hash_rw_sigusr2_handler(int signo);
+void *test_hash_rw_thr_reader(void *_count);
+void *test_hash_rw_thr_writer(void *_count);
+int test_hash_rw_populate_hash(void);
+
+/* unique test */
+void test_hash_unique_sigusr1_handler(int signo);
+void test_hash_unique_sigusr2_handler(int signo);
+void *test_hash_unique_thr_reader(void *_count);
+void *test_hash_unique_thr_writer(void *_count);
+int test_hash_unique_populate_hash(void);
+
+#endif /* _TEST_URCU_HASH_H */
--- /dev/null
+/*
+ * test_urcu_hash_rw.c
+ *
+ * Userspace RCU library - test program
+ *
+ * Copyright 2009-2012 - Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "test_urcu_hash.h"
+
+enum urcu_hash_addremove {
+ AR_RANDOM = 0,
+ AR_ADD = 1,
+ AR_REMOVE = -1,
+}; /* 1: add, -1 remove, 0: random */
+
+static enum urcu_hash_addremove addremove; /* 1: add, -1 remove, 0: random */
+
+void test_hash_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;
+ }
+}
+
+void test_hash_rw_sigusr2_handler(int signo)
+{
+ char msg[1] = { 0x42 };
+ ssize_t ret;
+
+ do {
+ ret = write(count_pipe[1], msg, 1); /* wakeup thread */
+ } while (ret == -1L && errno == EINTR);
+}
+
+void *test_hash_rw_thr_reader(void *_count)
+{
+ unsigned long long *count = _count;
+ struct lfht_test_node *node;
+ struct cds_lfht_iter iter;
+
+ 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();
+ cds_lfht_test_lookup(test_ht,
+ (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % lookup_pool_size) + lookup_pool_offset),
+ sizeof(void *), &iter);
+ node = cds_lfht_iter_get_test_node(&iter);
+ if (node == NULL) {
+ 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("read tid : %lx, lookupfail %lu, lookupok %lu\n",
+ urcu_get_thread_id(),
+ URCU_TLS(lookup_fail),
+ URCU_TLS(lookup_ok));
+ return ((void*)1);
+
+}
+
+void *test_hash_rw_thr_writer(void *_count)
+{
+ struct lfht_test_node *node;
+ struct cds_lfht_node *ret_node;
+ struct cds_lfht_iter iter;
+ struct wr_count *count = _count;
+ 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 || add_only)
+ || (addremove == AR_RANDOM && rand_r(&URCU_TLS(rand_lookup)) & 1)) {
+ node = malloc(sizeof(struct lfht_test_node));
+ lfht_test_node_init(node,
+ (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % write_pool_size) + write_pool_offset),
+ sizeof(void *));
+ rcu_read_lock();
+ if (add_unique) {
+ ret_node = cds_lfht_add_unique(test_ht,
+ test_hash(node->key, node->key_len, TEST_HASH_SEED),
+ test_match, node->key, &node->node);
+ } else {
+ if (add_replace)
+ ret_node = cds_lfht_add_replace(test_ht,
+ test_hash(node->key, node->key_len, TEST_HASH_SEED),
+ test_match, node->key, &node->node);
+ else
+ cds_lfht_add(test_ht,
+ test_hash(node->key, node->key_len, TEST_HASH_SEED),
+ &node->node);
+ }
+ rcu_read_unlock();
+ if (add_unique && ret_node != &node->node) {
+ free(node);
+ URCU_TLS(nr_addexist)++;
+ } else {
+ if (add_replace && ret_node) {
+ call_rcu(&to_test_node(ret_node)->head,
+ free_node_cb);
+ URCU_TLS(nr_addexist)++;
+ } else {
+ URCU_TLS(nr_add)++;
+ }
+ }
+ } else {
+ /* May delete */
+ rcu_read_lock();
+ cds_lfht_test_lookup(test_ht,
+ (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % write_pool_size) + write_pool_offset),
+ sizeof(void *), &iter);
+ ret = cds_lfht_del(test_ht, cds_lfht_iter_get_node(&iter));
+ rcu_read_unlock();
+ if (ret == 0) {
+ node = cds_lfht_iter_get_test_node(&iter);
+ call_rcu(&node->head, free_node_cb);
+ URCU_TLS(nr_del)++;
+ } else
+ URCU_TLS(nr_delnoent)++;
+ }
+#if 0
+ //if (URCU_TLS(nr_writes) % 100000 == 0) {
+ if (URCU_TLS(nr_writes) % 1000 == 0) {
+ rcu_read_lock();
+ if (rand_r(&URCU_TLS(rand_lookup)) & 1) {
+ ht_resize(test_ht, 1);
+ } else {
+ ht_resize(test_ht, -1);
+ }
+ rcu_read_unlock();
+ }
+#endif //0
+ 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 tid %lu: nr_add %lu, nr_addexist %lu, nr_del %lu, "
+ "nr_delnoent %lu\n", urcu_get_thread_id(),
+ 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);
+}
+
+int test_hash_rw_populate_hash(void)
+{
+ struct lfht_test_node *node;
+ struct cds_lfht_node *ret_node;
+
+ if (!init_populate)
+ return 0;
+
+ printf("Starting rw test\n");
+
+ URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL);
+
+ if ((add_unique || add_replace) && init_populate * 10 > init_pool_size) {
+ printf("WARNING: required to populate %lu nodes (-k), but random "
+"pool is quite small (%lu values) and we are in add_unique (-u) or add_replace (-s) mode. Try with a "
+"larger random pool (-p option). This may take a while...\n", init_populate, init_pool_size);
+ }
+
+ while (URCU_TLS(nr_add) < init_populate) {
+ node = malloc(sizeof(struct lfht_test_node));
+ lfht_test_node_init(node,
+ (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % init_pool_size) + init_pool_offset),
+ sizeof(void *));
+ rcu_read_lock();
+ if (add_unique) {
+ ret_node = cds_lfht_add_unique(test_ht,
+ test_hash(node->key, node->key_len, TEST_HASH_SEED),
+ test_match, node->key, &node->node);
+ } else {
+ if (add_replace)
+ ret_node = cds_lfht_add_replace(test_ht,
+ test_hash(node->key, node->key_len, TEST_HASH_SEED),
+ test_match, node->key, &node->node);
+ else
+ cds_lfht_add(test_ht,
+ test_hash(node->key, node->key_len, TEST_HASH_SEED),
+ &node->node);
+ }
+ rcu_read_unlock();
+ if (add_unique && ret_node != &node->node) {
+ free(node);
+ URCU_TLS(nr_addexist)++;
+ } else {
+ if (add_replace && ret_node) {
+ call_rcu(&to_test_node(ret_node)->head, free_node_cb);
+ URCU_TLS(nr_addexist)++;
+ } else {
+ URCU_TLS(nr_add)++;
+ }
+ }
+ URCU_TLS(nr_writes)++;
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * test_urcu_hash_unique.c
+ *
+ * Userspace RCU library - test program
+ *
+ * Copyright 2009-2012 - Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "test_urcu_hash.h"
+
+enum urcu_hash_addremove {
+ AR_RANDOM = 0,
+ AR_ADD = 1,
+ AR_REMOVE = -1,
+}; /* 1: add, -1 remove, 0: random */
+
+static enum urcu_hash_addremove addremove; /* 1: add, -1 remove, 0: random */
+
+void test_hash_unique_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;
+ }
+}
+
+void test_hash_unique_sigusr2_handler(int signo)
+{
+ char msg[1] = { 0x42 };
+ ssize_t ret;
+
+ do {
+ ret = write(count_pipe[1], msg, 1); /* wakeup thread */
+ } while (ret == -1L && errno == EINTR);
+}
+
+void *test_hash_unique_thr_reader(void *_count)
+{
+ unsigned long long *count = _count;
+
+ 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 (;;) {
+ struct lfht_test_node *node;
+ struct cds_lfht_iter iter;
+ /*
+ * iterate on whole table, ensuring that no duplicate is
+ * found.
+ */
+ rcu_read_lock();
+ cds_lfht_for_each_entry(test_ht, &iter, node, node) {
+ struct cds_lfht_iter dup_iter;
+
+ dup_iter = iter;
+ cds_lfht_next_duplicate(test_ht, test_match,
+ node->key, &dup_iter);
+ if (dup_iter.node != NULL) {
+ printf("[ERROR] Duplicate key %p found\n", node->key);
+ }
+ }
+ rcu_read_unlock();
+
+ rcu_debug_yield_read();
+ if (caa_unlikely(rduration))
+ loop_sleep(rduration);
+ 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("read tid : %lu, lookupfail %lu, lookupok %lu\n",
+ urcu_get_thread_id(), URCU_TLS(lookup_fail),
+ URCU_TLS(lookup_ok));
+ return ((void*)1);
+
+}
+
+void *test_hash_unique_thr_writer(void *_count)
+{
+ struct lfht_test_node *node;
+ struct cds_lfht_node *ret_node;
+ struct cds_lfht_iter iter;
+ struct wr_count *count = _count;
+ int ret;
+ int loc_add_unique;
+
+ 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 (;;) {
+ /*
+ * add unique/add replace with new node key from range.
+ */
+ if (1 || (addremove == AR_ADD || add_only)
+ || (addremove == AR_RANDOM && rand_r(&URCU_TLS(rand_lookup)) & 1)) {
+ node = malloc(sizeof(struct lfht_test_node));
+ lfht_test_node_init(node,
+ (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % write_pool_size) + write_pool_offset),
+ sizeof(void *));
+ rcu_read_lock();
+ loc_add_unique = rand_r(&URCU_TLS(rand_lookup)) & 1;
+ if (loc_add_unique) {
+ ret_node = cds_lfht_add_unique(test_ht,
+ test_hash(node->key, node->key_len, TEST_HASH_SEED),
+ test_match, node->key, &node->node);
+ } else {
+ ret_node = cds_lfht_add_replace(test_ht,
+ test_hash(node->key, node->key_len, TEST_HASH_SEED),
+ test_match, node->key, &node->node);
+#if 0 //generate an error on purpose
+ cds_lfht_add(test_ht,
+ test_hash(node->key, node->key_len, TEST_HASH_SEED),
+ &node->node);
+ ret_node = NULL;
+#endif //0
+ }
+ rcu_read_unlock();
+ if (loc_add_unique) {
+ if (ret_node != &node->node) {
+ free(node);
+ URCU_TLS(nr_addexist)++;
+ } else {
+ URCU_TLS(nr_add)++;
+ }
+ } else {
+ if (ret_node) {
+ call_rcu(&to_test_node(ret_node)->head,
+ free_node_cb);
+ URCU_TLS(nr_addexist)++;
+ } else {
+ URCU_TLS(nr_add)++;
+ }
+ }
+ } else {
+ /* May delete */
+ rcu_read_lock();
+ cds_lfht_test_lookup(test_ht,
+ (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % write_pool_size) + write_pool_offset),
+ sizeof(void *), &iter);
+ ret = cds_lfht_del(test_ht, cds_lfht_iter_get_node(&iter));
+ rcu_read_unlock();
+ if (ret == 0) {
+ node = cds_lfht_iter_get_test_node(&iter);
+ call_rcu(&node->head, free_node_cb);
+ URCU_TLS(nr_del)++;
+ } else
+ URCU_TLS(nr_delnoent)++;
+ }
+#if 0
+ //if (URCU_TLS(nr_writes) % 100000 == 0) {
+ if (URCU_TLS(nr_writes) % 1000 == 0) {
+ rcu_read_lock();
+ if (rand_r(&URCU_TLS(rand_lookup)) & 1) {
+ ht_resize(test_ht, 1);
+ } else {
+ ht_resize(test_ht, -1);
+ }
+ rcu_read_unlock();
+ }
+#endif //0
+ 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 tid %lu: nr_add %lu, nr_addexist %lu, nr_del %lu, "
+ "nr_delnoent %lu\n", urcu_get_thread_id(),
+ 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);
+}
+
+int test_hash_unique_populate_hash(void)
+{
+ struct lfht_test_node *node;
+ struct cds_lfht_node *ret_node;
+
+ printf("Starting uniqueness test.\n");
+
+ URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL);
+
+ if (!init_populate)
+ return 0;
+
+ if (init_populate * 10 > init_pool_size) {
+ printf("WARNING: required to populate %lu nodes (-k), but random "
+"pool is quite small (%lu values) and we are in add_unique (-u) or add_replace (-s) mode. Try with a "
+"larger random pool (-p option). This may take a while...\n", init_populate, init_pool_size);
+ }
+
+ while (URCU_TLS(nr_add) < init_populate) {
+ node = malloc(sizeof(struct lfht_test_node));
+ lfht_test_node_init(node,
+ (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % init_pool_size) + init_pool_offset),
+ sizeof(void *));
+ rcu_read_lock();
+ ret_node = cds_lfht_add_replace(test_ht,
+ test_hash(node->key, node->key_len, TEST_HASH_SEED),
+ test_match, node->key, &node->node);
+ rcu_read_unlock();
+ if (ret_node) {
+ call_rcu(&to_test_node(ret_node)->head, free_node_cb);
+ URCU_TLS(nr_addexist)++;
+ } else {
+ URCU_TLS(nr_add)++;
+ }
+ URCU_TLS(nr_writes)++;
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * test_urcu_lfq.c
+ *
+ * Userspace RCU library - example RCU-based lock-free queue
+ *
+ * Copyright February 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright February 2010 - Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "config.h"
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <urcu/arch.h>
+#include <urcu/tls-compat.h>
+#include "cpuset.h"
+#include "thread-id.h"
+
+/* hardcoded number of CPUs */
+#define NR_CPUS 16384
+
+#ifndef DYNAMIC_LINK_TEST
+#define _LGPL_SOURCE
+#endif
+#include <urcu.h>
+#include <urcu/cds.h>
+
+static volatile int test_go, test_stop;
+
+static unsigned long rduration;
+
+static unsigned long duration;
+
+/* read-side C.S. duration, in loops */
+static unsigned long wdelay;
+
+static inline void loop_sleep(unsigned long loops)
+{
+ while (loops-- != 0)
+ caa_cpu_relax();
+}
+
+static int verbose_mode;
+
+#define printf_verbose(fmt, args...) \
+ do { \
+ if (verbose_mode) \
+ printf(fmt, args); \
+ } while (0)
+
+static unsigned int cpu_affinities[NR_CPUS];
+static unsigned int next_aff = 0;
+static int use_affinity = 0;
+
+pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void set_affinity(void)
+{
+#if HAVE_SCHED_SETAFFINITY
+ cpu_set_t mask;
+ int cpu, ret;
+#endif /* HAVE_SCHED_SETAFFINITY */
+
+ if (!use_affinity)
+ return;
+
+#if HAVE_SCHED_SETAFFINITY
+ ret = pthread_mutex_lock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+ cpu = cpu_affinities[next_aff++];
+ ret = pthread_mutex_unlock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+#if SCHED_SETAFFINITY_ARGS == 2
+ sched_setaffinity(0, &mask);
+#else
+ sched_setaffinity(0, sizeof(mask), &mask);
+#endif
+#endif /* HAVE_SCHED_SETAFFINITY */
+}
+
+/*
+ * returns 0 if test should end.
+ */
+static int test_duration_dequeue(void)
+{
+ return !test_stop;
+}
+
+static int test_duration_enqueue(void)
+{
+ return !test_stop;
+}
+
+static DEFINE_URCU_TLS(unsigned long long, nr_dequeues);
+static DEFINE_URCU_TLS(unsigned long long, nr_enqueues);
+
+static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues);
+static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues);
+
+static unsigned int nr_enqueuers;
+static unsigned int nr_dequeuers;
+
+struct test {
+ struct cds_lfq_node_rcu list;
+ struct rcu_head rcu;
+};
+
+static struct cds_lfq_queue_rcu q;
+
+void *thr_enqueuer(void *_count)
+{
+ unsigned long long *count = _count;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "enqueuer", urcu_get_thread_id());
+
+ set_affinity();
+
+ rcu_register_thread();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ struct test *node = malloc(sizeof(*node));
+ if (!node)
+ goto fail;
+ cds_lfq_node_init_rcu(&node->list);
+ rcu_read_lock();
+ cds_lfq_enqueue_rcu(&q, &node->list);
+ rcu_read_unlock();
+ URCU_TLS(nr_successful_enqueues)++;
+
+ if (caa_unlikely(wdelay))
+ loop_sleep(wdelay);
+fail:
+ URCU_TLS(nr_enqueues)++;
+ if (caa_unlikely(!test_duration_enqueue()))
+ break;
+ }
+
+ rcu_unregister_thread();
+
+ count[0] = URCU_TLS(nr_enqueues);
+ count[1] = URCU_TLS(nr_successful_enqueues);
+ printf_verbose("enqueuer thread_end, tid %lu, "
+ "enqueues %llu successful_enqueues %llu\n",
+ urcu_get_thread_id(),
+ URCU_TLS(nr_enqueues),
+ URCU_TLS(nr_successful_enqueues));
+ return ((void*)1);
+
+}
+
+static
+void free_node_cb(struct rcu_head *head)
+{
+ struct test *node =
+ caa_container_of(head, struct test, rcu);
+ free(node);
+}
+
+void *thr_dequeuer(void *_count)
+{
+ unsigned long long *count = _count;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "dequeuer", urcu_get_thread_id());
+
+ set_affinity();
+
+ rcu_register_thread();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ struct cds_lfq_node_rcu *qnode;
+
+ rcu_read_lock();
+ qnode = cds_lfq_dequeue_rcu(&q);
+ rcu_read_unlock();
+
+ if (qnode) {
+ struct test *node;
+
+ node = caa_container_of(qnode, struct test, list);
+ call_rcu(&node->rcu, free_node_cb);
+ URCU_TLS(nr_successful_dequeues)++;
+ }
+
+ URCU_TLS(nr_dequeues)++;
+ if (caa_unlikely(!test_duration_dequeue()))
+ break;
+ if (caa_unlikely(rduration))
+ loop_sleep(rduration);
+ }
+
+ rcu_unregister_thread();
+ printf_verbose("dequeuer thread_end, tid %lu, "
+ "dequeues %llu, successful_dequeues %llu\n",
+ urcu_get_thread_id(),
+ URCU_TLS(nr_dequeues),
+ URCU_TLS(nr_successful_dequeues));
+ count[0] = URCU_TLS(nr_dequeues);
+ count[1] = URCU_TLS(nr_successful_dequeues);
+ return ((void*)2);
+}
+
+void test_end(struct cds_lfq_queue_rcu *q, unsigned long long *nr_dequeues)
+{
+ struct cds_lfq_node_rcu *snode;
+
+ do {
+ snode = cds_lfq_dequeue_rcu(q);
+ if (snode) {
+ struct test *node;
+
+ node = caa_container_of(snode, struct test, list);
+ free(node); /* no more concurrent access */
+ (*nr_dequeues)++;
+ }
+ } while (snode);
+}
+
+void show_usage(int argc, char **argv)
+{
+ printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) <OPTIONS>\n",
+ argv[0]);
+ printf("OPTIONS:\n");
+ printf(" [-d delay] (enqueuer period (in loops))\n");
+ printf(" [-c duration] (dequeuer period (in loops))\n");
+ printf(" [-v] (verbose output)\n");
+ printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ int err;
+ pthread_t *tid_enqueuer, *tid_dequeuer;
+ void *tret;
+ unsigned long long *count_enqueuer, *count_dequeuer;
+ unsigned long long tot_enqueues = 0, tot_dequeues = 0;
+ unsigned long long tot_successful_enqueues = 0,
+ tot_successful_dequeues = 0;
+ unsigned long long end_dequeues = 0;
+ int i, a;
+
+ if (argc < 4) {
+ show_usage(argc, argv);
+ return -1;
+ }
+
+ err = sscanf(argv[1], "%u", &nr_dequeuers);
+ if (err != 1) {
+ show_usage(argc, argv);
+ return -1;
+ }
+
+ err = sscanf(argv[2], "%u", &nr_enqueuers);
+ 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++) {
+ if (argv[i][0] != '-')
+ continue;
+ switch (argv[i][1]) {
+ case 'a':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ a = atoi(argv[++i]);
+ cpu_affinities[next_aff++] = a;
+ use_affinity = 1;
+ printf_verbose("Adding CPU %d affinity\n", a);
+ break;
+ case 'c':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ rduration = atol(argv[++i]);
+ break;
+ case 'd':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wdelay = atol(argv[++i]);
+ break;
+ case 'v':
+ verbose_mode = 1;
+ break;
+ }
+ }
+
+ printf_verbose("running test for %lu seconds, %u enqueuers, "
+ "%u dequeuers.\n",
+ duration, nr_enqueuers, nr_dequeuers);
+ printf_verbose("Writer delay : %lu loops.\n", rduration);
+ printf_verbose("Reader duration : %lu loops.\n", wdelay);
+ printf_verbose("thread %-6s, tid %lu\n",
+ "main", urcu_get_thread_id());
+
+ tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer));
+ tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer));
+ count_enqueuer = calloc(nr_enqueuers, 2 * sizeof(*count_enqueuer));
+ count_dequeuer = calloc(nr_dequeuers, 2 * sizeof(*count_dequeuer));
+ cds_lfq_init_rcu(&q, call_rcu);
+ err = create_all_cpu_call_rcu_data(0);
+ if (err) {
+ printf("Per-CPU call_rcu() worker threads unavailable. Using default global worker thread.\n");
+ }
+
+ next_aff = 0;
+
+ for (i = 0; i < nr_enqueuers; i++) {
+ err = pthread_create(&tid_enqueuer[i], NULL, thr_enqueuer,
+ &count_enqueuer[2 * i]);
+ if (err != 0)
+ exit(1);
+ }
+ for (i = 0; i < nr_dequeuers; i++) {
+ err = pthread_create(&tid_dequeuer[i], NULL, thr_dequeuer,
+ &count_dequeuer[2 * i]);
+ if (err != 0)
+ exit(1);
+ }
+
+ cmm_smp_mb();
+
+ test_go = 1;
+
+ for (i = 0; i < duration; i++) {
+ sleep(1);
+ if (verbose_mode)
+ write (1, ".", 1);
+ }
+
+ test_stop = 1;
+
+ for (i = 0; i < nr_enqueuers; i++) {
+ err = pthread_join(tid_enqueuer[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_enqueues += count_enqueuer[2 * i];
+ tot_successful_enqueues += count_enqueuer[2 * i + 1];
+ }
+ for (i = 0; i < nr_dequeuers; i++) {
+ err = pthread_join(tid_dequeuer[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_dequeues += count_dequeuer[2 * i];
+ tot_successful_dequeues += count_dequeuer[2 * i + 1];
+ }
+
+ test_end(&q, &end_dequeues);
+ err = cds_lfq_destroy_rcu(&q);
+ assert(!err);
+
+ printf_verbose("total number of enqueues : %llu, dequeues %llu\n",
+ tot_enqueues, tot_dequeues);
+ printf_verbose("total number of successful enqueues : %llu, "
+ "successful dequeues %llu\n",
+ tot_successful_enqueues, tot_successful_dequeues);
+ printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu "
+ "nr_dequeuers %3u "
+ "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu "
+ "successful enqueues %12llu successful dequeues %12llu "
+ "end_dequeues %llu nr_ops %12llu\n",
+ argv[0], duration, nr_enqueuers, wdelay,
+ nr_dequeuers, rduration, tot_enqueues, tot_dequeues,
+ tot_successful_enqueues,
+ tot_successful_dequeues, end_dequeues,
+ tot_enqueues + tot_dequeues);
+ if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues)
+ printf("WARNING! Discrepancy between nr succ. enqueues %llu vs "
+ "succ. dequeues + end dequeues %llu.\n",
+ tot_successful_enqueues,
+ tot_successful_dequeues + end_dequeues);
+
+ free_all_cpu_call_rcu_data();
+ free(count_enqueuer);
+ free(count_dequeuer);
+ free(tid_enqueuer);
+ free(tid_dequeuer);
+ return 0;
+}
--- /dev/null
+/*
+ * test_urcu_lfs.c
+ *
+ * Userspace RCU library - example lock-free stack
+ *
+ * Copyright 2010-2012 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright February 2010 - Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "config.h"
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <urcu/arch.h>
+#include <urcu/tls-compat.h>
+#include "cpuset.h"
+#include "thread-id.h"
+
+/* hardcoded number of CPUs */
+#define NR_CPUS 16384
+
+#ifndef DYNAMIC_LINK_TEST
+#define _LGPL_SOURCE
+#endif
+#include <urcu.h>
+#include <urcu/cds.h>
+
+/*
+ * External synchronization used.
+ */
+enum test_sync {
+ TEST_SYNC_NONE = 0,
+ TEST_SYNC_RCU,
+};
+
+static enum test_sync test_sync;
+
+static volatile int test_go, test_stop;
+
+static unsigned long rduration;
+
+static unsigned long duration;
+
+/* read-side C.S. duration, in loops */
+static unsigned long wdelay;
+
+static inline void loop_sleep(unsigned long loops)
+{
+ while (loops-- != 0)
+ caa_cpu_relax();
+}
+
+static int verbose_mode;
+
+static int test_pop, test_pop_all;
+
+#define printf_verbose(fmt, args...) \
+ do { \
+ if (verbose_mode) \
+ printf(fmt, ## args); \
+ } while (0)
+
+static unsigned int cpu_affinities[NR_CPUS];
+static unsigned int next_aff = 0;
+static int use_affinity = 0;
+
+pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void set_affinity(void)
+{
+#if HAVE_SCHED_SETAFFINITY
+ cpu_set_t mask;
+ int cpu, ret;
+#endif /* HAVE_SCHED_SETAFFINITY */
+
+ if (!use_affinity)
+ return;
+
+#if HAVE_SCHED_SETAFFINITY
+ ret = pthread_mutex_lock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+ cpu = cpu_affinities[next_aff++];
+ ret = pthread_mutex_unlock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+#if SCHED_SETAFFINITY_ARGS == 2
+ sched_setaffinity(0, &mask);
+#else
+ sched_setaffinity(0, sizeof(mask), &mask);
+#endif
+#endif /* HAVE_SCHED_SETAFFINITY */
+}
+
+/*
+ * returns 0 if test should end.
+ */
+static int test_duration_dequeue(void)
+{
+ return !test_stop;
+}
+
+static int test_duration_enqueue(void)
+{
+ return !test_stop;
+}
+
+static DEFINE_URCU_TLS(unsigned long long, nr_dequeues);
+static DEFINE_URCU_TLS(unsigned long long, nr_enqueues);
+
+static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues);
+static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues);
+
+static unsigned int nr_enqueuers;
+static unsigned int nr_dequeuers;
+
+struct test {
+ struct cds_lfs_node list;
+ struct rcu_head rcu;
+};
+
+static struct cds_lfs_stack s;
+
+static void *thr_enqueuer(void *_count)
+{
+ unsigned long long *count = _count;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "enqueuer", urcu_get_thread_id());
+
+ set_affinity();
+
+ rcu_register_thread();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ struct test *node = malloc(sizeof(*node));
+ if (!node)
+ goto fail;
+ cds_lfs_node_init(&node->list);
+ cds_lfs_push(&s, &node->list);
+ URCU_TLS(nr_successful_enqueues)++;
+
+ if (caa_unlikely(wdelay))
+ loop_sleep(wdelay);
+fail:
+ URCU_TLS(nr_enqueues)++;
+ if (caa_unlikely(!test_duration_enqueue()))
+ break;
+ }
+
+ rcu_unregister_thread();
+
+ count[0] = URCU_TLS(nr_enqueues);
+ count[1] = URCU_TLS(nr_successful_enqueues);
+ printf_verbose("enqueuer thread_end, tid %lu, "
+ "enqueues %llu successful_enqueues %llu\n",
+ urcu_get_thread_id(),
+ URCU_TLS(nr_enqueues),
+ URCU_TLS(nr_successful_enqueues));
+ return ((void*)1);
+
+}
+
+static
+void free_node_cb(struct rcu_head *head)
+{
+ struct test *node =
+ caa_container_of(head, struct test, rcu);
+ free(node);
+}
+
+static
+void do_test_pop(enum test_sync sync)
+{
+ struct cds_lfs_node *snode;
+
+ if (sync == TEST_SYNC_RCU)
+ rcu_read_lock();
+ snode = __cds_lfs_pop(&s);
+ if (sync == TEST_SYNC_RCU)
+ rcu_read_unlock();
+ if (snode) {
+ struct test *node;
+
+ node = caa_container_of(snode,
+ struct test, list);
+ if (sync == TEST_SYNC_RCU)
+ call_rcu(&node->rcu, free_node_cb);
+ else
+ free(node);
+ URCU_TLS(nr_successful_dequeues)++;
+ }
+ URCU_TLS(nr_dequeues)++;
+}
+
+static
+void do_test_pop_all(enum test_sync sync)
+{
+ struct cds_lfs_node *snode;
+ struct cds_lfs_head *head;
+ struct cds_lfs_node *n;
+
+ head = __cds_lfs_pop_all(&s);
+ cds_lfs_for_each_safe(head, snode, n) {
+ struct test *node;
+
+ node = caa_container_of(snode, struct test, list);
+ if (sync == TEST_SYNC_RCU)
+ call_rcu(&node->rcu, free_node_cb);
+ else
+ free(node);
+ URCU_TLS(nr_successful_dequeues)++;
+ URCU_TLS(nr_dequeues)++;
+ }
+
+}
+
+static void *thr_dequeuer(void *_count)
+{
+ unsigned long long *count = _count;
+ unsigned int counter = 0;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "dequeuer", urcu_get_thread_id());
+
+ set_affinity();
+
+ rcu_register_thread();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ assert(test_pop || test_pop_all);
+
+ for (;;) {
+ if (test_pop && test_pop_all) {
+ /* both pop and pop all */
+ if (counter & 1)
+ do_test_pop(test_sync);
+ else
+ do_test_pop_all(test_sync);
+ counter++;
+ } else {
+ if (test_pop)
+ do_test_pop(test_sync);
+ else
+ do_test_pop_all(test_sync);
+ }
+
+ if (caa_unlikely(!test_duration_dequeue()))
+ break;
+ if (caa_unlikely(rduration))
+ loop_sleep(rduration);
+ }
+
+ rcu_unregister_thread();
+
+ printf_verbose("dequeuer thread_end, tid %lu, "
+ "dequeues %llu, successful_dequeues %llu\n",
+ urcu_get_thread_id(),
+ URCU_TLS(nr_dequeues),
+ URCU_TLS(nr_successful_dequeues));
+ count[0] = URCU_TLS(nr_dequeues);
+ count[1] = URCU_TLS(nr_successful_dequeues);
+ return ((void*)2);
+}
+
+static void test_end(struct cds_lfs_stack *s, unsigned long long *nr_dequeues)
+{
+ struct cds_lfs_node *snode;
+
+ do {
+ snode = __cds_lfs_pop(s);
+ if (snode) {
+ struct test *node;
+
+ node = caa_container_of(snode, struct test, list);
+ free(node);
+ (*nr_dequeues)++;
+ }
+ } while (snode);
+}
+
+static void show_usage(int argc, char **argv)
+{
+ printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) <OPTIONS>\n",
+ argv[0]);
+ printf("OPTIONS:\n");
+ printf(" [-d delay] (enqueuer period (in loops))\n");
+ printf(" [-c duration] (dequeuer period (in loops))\n");
+ printf(" [-v] (verbose output)\n");
+ printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
+ printf(" [-p] (test pop)\n");
+ printf(" [-P] (test pop_all, enabled by default)\n");
+ printf(" [-R] (use RCU external synchronization)\n");
+ printf(" Note: default: no external synchronization used.\n");
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ int err;
+ pthread_t *tid_enqueuer, *tid_dequeuer;
+ void *tret;
+ unsigned long long *count_enqueuer, *count_dequeuer;
+ unsigned long long tot_enqueues = 0, tot_dequeues = 0;
+ unsigned long long tot_successful_enqueues = 0,
+ tot_successful_dequeues = 0;
+ unsigned long long end_dequeues = 0;
+ int i, a;
+
+ if (argc < 4) {
+ show_usage(argc, argv);
+ return -1;
+ }
+
+ err = sscanf(argv[1], "%u", &nr_dequeuers);
+ if (err != 1) {
+ show_usage(argc, argv);
+ return -1;
+ }
+
+ err = sscanf(argv[2], "%u", &nr_enqueuers);
+ 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++) {
+ if (argv[i][0] != '-')
+ continue;
+ switch (argv[i][1]) {
+ case 'a':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ a = atoi(argv[++i]);
+ cpu_affinities[next_aff++] = a;
+ use_affinity = 1;
+ printf_verbose("Adding CPU %d affinity\n", a);
+ break;
+ case 'c':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ rduration = atol(argv[++i]);
+ break;
+ case 'd':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wdelay = atol(argv[++i]);
+ break;
+ case 'v':
+ verbose_mode = 1;
+ break;
+ case 'p':
+ test_pop = 1;
+ break;
+ case 'P':
+ test_pop_all = 1;
+ break;
+ case 'R':
+ test_sync = TEST_SYNC_RCU;
+ break;
+ }
+ }
+
+ /* activate pop_all test by default */
+ if (!test_pop && !test_pop_all)
+ test_pop_all = 1;
+
+ printf_verbose("running test for %lu seconds, %u enqueuers, "
+ "%u dequeuers.\n",
+ duration, nr_enqueuers, nr_dequeuers);
+ if (test_pop)
+ printf_verbose("pop test activated.\n");
+ if (test_pop_all)
+ printf_verbose("pop_all test activated.\n");
+ if (test_sync == TEST_SYNC_RCU)
+ printf_verbose("External sync: RCU.\n");
+ else
+ printf_verbose("External sync: none.\n");
+ printf_verbose("Writer delay : %lu loops.\n", rduration);
+ printf_verbose("Reader duration : %lu loops.\n", wdelay);
+ printf_verbose("thread %-6s, tid %lu\n",
+ "main", urcu_get_thread_id());
+
+ tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer));
+ tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer));
+ count_enqueuer = calloc(nr_enqueuers, 2 * sizeof(*count_enqueuer));
+ count_dequeuer = calloc(nr_dequeuers, 2 * sizeof(*count_dequeuer));
+ cds_lfs_init(&s);
+ err = create_all_cpu_call_rcu_data(0);
+ if (err) {
+ printf("Per-CPU call_rcu() worker threads unavailable. Using default global worker thread.\n");
+ }
+
+ next_aff = 0;
+
+ for (i = 0; i < nr_enqueuers; i++) {
+ err = pthread_create(&tid_enqueuer[i], NULL, thr_enqueuer,
+ &count_enqueuer[2 * i]);
+ if (err != 0)
+ exit(1);
+ }
+ for (i = 0; i < nr_dequeuers; i++) {
+ err = pthread_create(&tid_dequeuer[i], NULL, thr_dequeuer,
+ &count_dequeuer[2 * i]);
+ if (err != 0)
+ exit(1);
+ }
+
+ cmm_smp_mb();
+
+ test_go = 1;
+
+ for (i = 0; i < duration; i++) {
+ sleep(1);
+ if (verbose_mode)
+ write (1, ".", 1);
+ }
+
+ test_stop = 1;
+
+ for (i = 0; i < nr_enqueuers; i++) {
+ err = pthread_join(tid_enqueuer[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_enqueues += count_enqueuer[2 * i];
+ tot_successful_enqueues += count_enqueuer[2 * i + 1];
+ }
+ for (i = 0; i < nr_dequeuers; i++) {
+ err = pthread_join(tid_dequeuer[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_dequeues += count_dequeuer[2 * i];
+ tot_successful_dequeues += count_dequeuer[2 * i + 1];
+ }
+
+ test_end(&s, &end_dequeues);
+
+ printf_verbose("total number of enqueues : %llu, dequeues %llu\n",
+ tot_enqueues, tot_dequeues);
+ printf_verbose("total number of successful enqueues : %llu, "
+ "successful dequeues %llu\n",
+ tot_successful_enqueues, tot_successful_dequeues);
+ printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu "
+ "nr_dequeuers %3u "
+ "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu "
+ "successful enqueues %12llu successful dequeues %12llu "
+ "end_dequeues %llu nr_ops %12llu\n",
+ argv[0], duration, nr_enqueuers, wdelay,
+ nr_dequeuers, rduration, tot_enqueues, tot_dequeues,
+ tot_successful_enqueues,
+ tot_successful_dequeues, end_dequeues,
+ tot_enqueues + tot_dequeues);
+ if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues)
+ printf("WARNING! Discrepancy between nr succ. enqueues %llu vs "
+ "succ. dequeues + end dequeues %llu.\n",
+ tot_successful_enqueues,
+ tot_successful_dequeues + end_dequeues);
+
+ free_all_cpu_call_rcu_data();
+ free(count_enqueuer);
+ free(count_dequeuer);
+ free(tid_enqueuer);
+ free(tid_dequeuer);
+ return 0;
+}
--- /dev/null
+/*
+ * test_urcu_lfs_rcu.c
+ *
+ * Userspace RCU library - example RCU-based lock-free stack
+ *
+ * Copyright February 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright February 2010 - Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "config.h"
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <urcu/arch.h>
+#include <urcu/tls-compat.h>
+#include "cpuset.h"
+#include "thread-id.h"
+
+/* hardcoded number of CPUs */
+#define NR_CPUS 16384
+
+#ifndef DYNAMIC_LINK_TEST
+#define _LGPL_SOURCE
+#endif
+#include <urcu.h>
+
+/* Remove deprecation warnings from test build. */
+#define CDS_LFS_RCU_DEPRECATED
+
+#include <urcu/cds.h>
+
+static volatile int test_go, test_stop;
+
+static unsigned long rduration;
+
+static unsigned long duration;
+
+/* read-side C.S. duration, in loops */
+static unsigned long wdelay;
+
+static inline void loop_sleep(unsigned long loops)
+{
+ while (loops-- != 0)
+ caa_cpu_relax();
+}
+
+static int verbose_mode;
+
+#define printf_verbose(fmt, args...) \
+ do { \
+ if (verbose_mode) \
+ printf(fmt, args); \
+ } while (0)
+
+static unsigned int cpu_affinities[NR_CPUS];
+static unsigned int next_aff = 0;
+static int use_affinity = 0;
+
+pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void set_affinity(void)
+{
+ cpu_set_t mask;
+ int cpu;
+ int ret;
+
+ if (!use_affinity)
+ return;
+
+#if HAVE_SCHED_SETAFFINITY
+ ret = pthread_mutex_lock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+ cpu = cpu_affinities[next_aff++];
+ ret = pthread_mutex_unlock(&affinity_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+#if SCHED_SETAFFINITY_ARGS == 2
+ sched_setaffinity(0, &mask);
+#else
+ sched_setaffinity(0, sizeof(mask), &mask);
+#endif
+#endif /* HAVE_SCHED_SETAFFINITY */
+}
+
+/*
+ * returns 0 if test should end.
+ */
+static int test_duration_dequeue(void)
+{
+ return !test_stop;
+}
+
+static int test_duration_enqueue(void)
+{
+ return !test_stop;
+}
+
+static DEFINE_URCU_TLS(unsigned long long, nr_dequeues);
+static DEFINE_URCU_TLS(unsigned long long, nr_enqueues);
+
+static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues);
+static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues);
+
+static unsigned int nr_enqueuers;
+static unsigned int nr_dequeuers;
+
+struct test {
+ struct cds_lfs_node_rcu list;
+ struct rcu_head rcu;
+};
+
+static struct cds_lfs_stack_rcu s;
+
+void *thr_enqueuer(void *_count)
+{
+ unsigned long long *count = _count;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "enqueuer", urcu_get_thread_id());
+
+ set_affinity();
+
+ rcu_register_thread();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ struct test *node = malloc(sizeof(*node));
+ if (!node)
+ goto fail;
+ cds_lfs_node_init_rcu(&node->list);
+ /* No rcu read-side is needed for push */
+ cds_lfs_push_rcu(&s, &node->list);
+ URCU_TLS(nr_successful_enqueues)++;
+
+ if (caa_unlikely(wdelay))
+ loop_sleep(wdelay);
+fail:
+ URCU_TLS(nr_enqueues)++;
+ if (caa_unlikely(!test_duration_enqueue()))
+ break;
+ }
+
+ rcu_unregister_thread();
+
+ count[0] = URCU_TLS(nr_enqueues);
+ count[1] = URCU_TLS(nr_successful_enqueues);
+ printf_verbose("enqueuer thread_end, tid %lu, "
+ "enqueues %llu successful_enqueues %llu\n",
+ urcu_get_thread_id(),
+ URCU_TLS(nr_enqueues),
+ URCU_TLS(nr_successful_enqueues));
+ return ((void*)1);
+
+}
+
+static
+void free_node_cb(struct rcu_head *head)
+{
+ struct test *node =
+ caa_container_of(head, struct test, rcu);
+ free(node);
+}
+
+void *thr_dequeuer(void *_count)
+{
+ unsigned long long *count = _count;
+
+ printf_verbose("thread_begin %s, tid %lu\n",
+ "dequeuer", urcu_get_thread_id());
+
+ set_affinity();
+
+ rcu_register_thread();
+
+ while (!test_go)
+ {
+ }
+ cmm_smp_mb();
+
+ for (;;) {
+ struct cds_lfs_node_rcu *snode;
+
+ rcu_read_lock();
+ snode = cds_lfs_pop_rcu(&s);
+ rcu_read_unlock();
+ if (snode) {
+ struct test *node;
+
+ node = caa_container_of(snode, struct test, list);
+ call_rcu(&node->rcu, free_node_cb);
+ URCU_TLS(nr_successful_dequeues)++;
+ }
+ URCU_TLS(nr_dequeues)++;
+ if (caa_unlikely(!test_duration_dequeue()))
+ break;
+ if (caa_unlikely(rduration))
+ loop_sleep(rduration);
+ }
+
+ rcu_unregister_thread();
+
+ printf_verbose("dequeuer thread_end, tid %lu, "
+ "dequeues %llu, successful_dequeues %llu\n",
+ urcu_get_thread_id(),
+ URCU_TLS(nr_dequeues),
+ URCU_TLS(nr_successful_dequeues));
+ count[0] = URCU_TLS(nr_dequeues);
+ count[1] = URCU_TLS(nr_successful_dequeues);
+ return ((void*)2);
+}
+
+void test_end(struct cds_lfs_stack_rcu *s, unsigned long long *nr_dequeues)
+{
+ struct cds_lfs_node_rcu *snode;
+
+ do {
+ snode = cds_lfs_pop_rcu(s);
+ if (snode) {
+ struct test *node;
+
+ node = caa_container_of(snode, struct test, list);
+ free(node);
+ (*nr_dequeues)++;
+ }
+ } while (snode);
+}
+
+void show_usage(int argc, char **argv)
+{
+ printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) <OPTIONS>\n",
+ argv[0]);
+ printf("OPTIONS:\n");
+ printf(" [-d delay] (enqueuer period (in loops))\n");
+ printf(" [-c duration] (dequeuer period (in loops))\n");
+ printf(" [-v] (verbose output)\n");
+ printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ int err;
+ pthread_t *tid_enqueuer, *tid_dequeuer;
+ void *tret;
+ unsigned long long *count_enqueuer, *count_dequeuer;
+ unsigned long long tot_enqueues = 0, tot_dequeues = 0;
+ unsigned long long tot_successful_enqueues = 0,
+ tot_successful_dequeues = 0;
+ unsigned long long end_dequeues = 0;
+ int i, a;
+
+ if (argc < 4) {
+ show_usage(argc, argv);
+ return -1;
+ }
+
+ err = sscanf(argv[1], "%u", &nr_dequeuers);
+ if (err != 1) {
+ show_usage(argc, argv);
+ return -1;
+ }
+
+ err = sscanf(argv[2], "%u", &nr_enqueuers);
+ 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++) {
+ if (argv[i][0] != '-')
+ continue;
+ switch (argv[i][1]) {
+ case 'a':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ a = atoi(argv[++i]);
+ cpu_affinities[next_aff++] = a;
+ use_affinity = 1;
+ printf_verbose("Adding CPU %d affinity\n", a);
+ break;
+ case 'c':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ rduration = atol(argv[++i]);
+ break;
+ case 'd':
+ if (argc < i + 2) {
+ show_usage(argc, argv);
+ return -1;
+ }
+ wdelay = atol(argv[++i]);
+ break;
+ case 'v':
+ verbose_mode = 1;
+ break;
+ }
+ }
+
+ printf_verbose("running test for %lu seconds, %u enqueuers, "
+ "%u dequeuers.\n",
+ duration, nr_enqueuers, nr_dequeuers);
+ printf_verbose("Writer delay : %lu loops.\n", rduration);
+ printf_verbose("Reader duration : %lu loops.\n", wdelay);
+ printf_verbose("thread %-6s, tid %lu\n",
+ "main", urcu_get_thread_id());
+
+ tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer));
+ tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer));
+ count_enqueuer = calloc(nr_enqueuers, 2 * sizeof(*count_enqueuer));
+ count_dequeuer = calloc(nr_dequeuers, 2 * sizeof(*count_dequeuer));
+ cds_lfs_init_rcu(&s);
+ err = create_all_cpu_call_rcu_data(0);
+ if (err) {
+ printf("Per-CPU call_rcu() worker threads unavailable. Using default global worker thread.\n");
+ }
+
+ next_aff = 0;
+
+ for (i = 0; i < nr_enqueuers; i++) {
+ err = pthread_create(&tid_enqueuer[i], NULL, thr_enqueuer,
+ &count_enqueuer[2 * i]);
+ if (err != 0)
+ exit(1);
+ }
+ for (i = 0; i < nr_dequeuers; i++) {
+ err = pthread_create(&tid_dequeuer[i], NULL, thr_dequeuer,
+ &count_dequeuer[2 * i]);
+ if (err != 0)
+ exit(1);
+ }
+
+ cmm_smp_mb();
+
+ test_go = 1;
+
+ for (i = 0; i < duration; i++) {
+ sleep(1);
+ if (verbose_mode)
+ write (1, ".", 1);
+ }
+
+ test_stop = 1;
+
+ for (i = 0; i < nr_enqueuers; i++) {
+ err = pthread_join(tid_enqueuer[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_enqueues += count_enqueuer[2 * i];
+ tot_successful_enqueues += count_enqueuer[2 * i + 1];
+ }
+ for (i = 0; i < nr_dequeuers; i++) {
+ err = pthread_join(tid_dequeuer[i], &tret);
+ if (err != 0)
+ exit(1);
+ tot_dequeues += count_dequeuer[2 * i];
+ tot_successful_dequeues += count_dequeuer[2 * i + 1];
+ }
+
+ test_end(&s, &end_dequeues);
+
+ printf_verbose("total number of enqueues : %llu, dequeues %llu\n",
+ tot_enqueues, tot_dequeues);
+ printf_verbose("total number of successful enqueues : %llu, "
+ "successful dequeues %llu\n",
+ tot_successful_enqueues, tot_successful_dequeues);
+ printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu "
+ "nr_dequeuers %3u "
+ "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu "
+ "successful enqueues %12llu successful dequeues %12llu "
+ "end_dequeues %llu nr_ops %12llu\n",
+ argv[0], duration, nr_enqueuers, wdelay,
+ nr_dequeuers, rduration, tot_enqueues, tot_dequeues,
+ tot_successful_enqueues,
+ tot_successful_dequeues, end_dequeues,
+ tot_enqueues + tot_dequeues);
+ if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues)
+ printf("WARNING! Discrepancy between nr succ. enqueues %llu vs "
+ "succ. dequeues + end dequeues %llu.\n",
+ tot_successful_enqueues,
+ tot_successful_dequeues + end_dequeues);
+
+ free_all_cpu_call_rcu_data();
+ free(count_enqueuer);
+ free(count_dequeuer);
+ free(tid_enqueuer);
+ free(tid_dequeuer);
+ return 0;
+}
--- /dev/null
+/*
+ * test_urcu.c
+ *
+ * Userspace RCU library - test program
+ *
+ * Copyright February 2009 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include "config.h"
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <urcu/arch.h>
+#include <urcu/tls-compat.h>
+#include "cpuset.h"
+#include "thread-id.h"
+
+/* hardcoded number of CPUs */
+#define NR_CPUS 16384
+
+#ifndef DYNAMIC_LINK_TEST
+#define _LGPL_SOURCE
+#else
+#define rcu_debug_yield_read()
+#endif
+#include "urcu-qsbr.h"
+
+static volatile int test_go, test_stop;
+
+static unsigned long wdelay;
+
+static int *test_rcu_pointer;
+
+static unsigned long duration;
+
+/* read-side C.S. duration, in loops */
+static unsigned long rduration;
+
+/* write-side C.S. duration, in loops */
+static unsigned long wduration;
+
+static inline void loop_sleep(unsigned long loops)
+{
+ while (loops-- != 0)
+ caa_cpu_relax();
+}
+
+static int verbose_mode;
+
+#define printf_verbose(fmt, args...) \
+ do { \
+ if (verbose_mode) \
+ printf(fmt, args); \
+ } while (0)
+
+static unsigned int cpu_affinities[NR_CPUS];
+static&nbs