From 7ac06cef7a7d9394332837edc5c0e9595286a5fc Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Tue, 15 Sep 2009 15:51:34 -0400 Subject: [PATCH] Build urcu-qsbr.so library, update README Signed-off-by: Mathieu Desnoyers --- Makefile.inc | 19 +++- README | 40 +++++-- tests/Makefile.inc | 5 +- tests/test_qsbr.c | 14 ++- urcu-qsbr-static.h | 255 +++++++++++++++++++++++++++++++++++++++++++++ urcu-qsbr.c | 23 +++- urcu-qsbr.h | 250 +++++++------------------------------------- urcu-static.h | 6 ++ 8 files changed, 379 insertions(+), 233 deletions(-) create mode 100644 urcu-qsbr-static.h diff --git a/Makefile.inc b/Makefile.inc index 870bde8..14ba4ed 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -3,7 +3,11 @@ include Makefile.build.inc DIRS=tests -all: checkarch liburcu.so urcu.o urcu-yield.o urcu-qsbr.o urcu-mb.o subdirs +all: checkarch liburcu.so urcu.o \ + liburcu-qsbr.so urcu-qsbr.o \ + liburcu-mb.so urcu-mb.o \ + urcu-yield.o \ + subdirs checkarch: ifeq (${ARCHTYPE},) @@ -33,6 +37,12 @@ urcu-qsbr.o: urcu-qsbr.c urcu-qsbr.h liburcu.so: urcu.o $(CC) -fPIC -shared -o $@ $< +liburcu-qsbr.so: urcu-qsbr.o + $(CC) -fPIC -shared -o $@ $< + +liburcu-mb.so: urcu-mb.o + $(CC) -fPIC -shared -o $@ $< + urcu-yield.o: urcu.c urcu.h $(CC) -DDEBUG_YIELD ${CFLAGS} $(LDFLAGS) -c -o $@ $(SRC_DEP) @@ -42,8 +52,11 @@ subdirs: -for d in ${DIRS}; do cd $${d}; ${MAKE} ${MFLAGS}; done install: liburcu.so - cp -f liburcu.so /usr/lib/ - cp -f arch.h arch_atomic.h compiler.h urcu.h urcu-static.h /usr/include/ + cp -f liburcu.so liburcu-mb.so liburcu-qsbr.so /usr/lib/ + cp -f arch.h arch_atomic.h compiler.h \ + urcu.h urcu-static.h \ + urcu-qsbr.h urcu-qsbr-static.h \ + /usr/include/ clean: rm -f *.o *.so arch.h arch_atomic.h diff --git a/README b/README index f9af1eb..93c0a74 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -Userspace RCU Implementatation +Userspace RCU Implementation by Mathieu Desnoyers and Paul E. McKenney BUILDING @@ -31,23 +31,41 @@ Writing Being careful with signals - The library uses signals internally. The signal handler is + The urcu library uses signals internally. The signal handler is registered with the SA_RESTART flag. However, these signals may cause some non-restartable system calls to fail with errno = EINTR. Care should be taken to restart system calls manually if they fail with this error. A list of non-restartable system calls may be found in signal(7). To ensure the Userspace RCU library does not use signals, - define CONFIG_URCU_AVOID_SIGNALS at compile-time. + see "Usage of liburcu-mb" below. - Read-side critical sections can sit in a signal handler. Be careful, - however, to disable these signals between thread creation and calls to - rcu_register_thread(), because a signal handler nesting on an - unregistered thread would not be allowed to call rcu_read_lock(). + Read-side critical sections can are allowed in a signal handler with + liburcu and liburcu-mb. Be careful, however, to disable these signals + between thread creation and calls to rcu_register_thread(), because a + signal handler nesting on an unregistered thread would not be allowed to + call rcu_read_lock(). -Usage of CONFIG_URCU_AVOID_SIGNALS +Usage of liburcu - CONFIG_URCU_AVOID_SIGNALS uses full SMP barriers for readers. This - eliminates the need for signals but results in slower reads. + This is the preferred version of the library, both in terms of speed and + flexibility. Define _LGPL_SOURCE if your code is LGPL or GPL (otherwise + function calls will be generated instead of inlines). Use the urcu.h + header. Link the application with "-lurcu". + +Usage of liburcu-mb + + Compile code with "-DCONFIG_URCU_AVOID_SIGNALS" and link with + "-lurcu-mb" to use a version of the urcu library which does not need to + reserve a signal number. CONFIG_URCU_AVOID_SIGNALS uses full SMP + barriers for readers. This eliminates the need for signals but results + in slower reads. + +Usage of liburcu-qsbr + + The QSBR flavor of RCU needs to have each reader thread executing + rcu_quiescent_state() periodically to progress. rcu_thread_online() and + rcu_thread_offline() can be used to mark long periods for which the + threads are not active. Link with "-lurcu-qsbr" and use urcu-qsbr.h. Usage of DEBUG_RCU @@ -60,3 +78,5 @@ Usage of DEBUG_YIELD DEBUG_YIELD is used to add random delays in the code for testing purposes. + + diff --git a/tests/Makefile.inc b/tests/Makefile.inc index 5fac462..e99bfee 100644 --- a/tests/Makefile.inc +++ b/tests/Makefile.inc @@ -22,7 +22,7 @@ all: test_urcu test_urcu_dynamic_link test_urcu_timing \ urcu-asm.S test_qsbr_timing test_qsbr urcu-asm.o urcutorture \ urcutorture-yield test_mutex test_looplen test_urcu_gc \ test_urcu_gc_mb test_qsbr_gc test_qsbr_lgc test_urcu_lgc \ - test_urcu_lgc_mb + test_urcu_lgc_mb test_qsbr_dynamic_link api.h: ${APIHEADER} cp -f ${APIHEADER} api.h @@ -60,6 +60,9 @@ test_qsbr_gc: test_qsbr_gc.c ${URCU_QSBR} test_qsbr_lgc: test_qsbr_gc.c ${URCU_QSBR} $(CC) -DTEST_LOCAL_GC ${CFLAGS} $(LDFLAGS) -o $@ $(SRC_DEP) +test_qsbr_dynamic_link: test_qsbr.c ${URCU_QSBR} + $(CC) ${CFLAGS} -DDYNAMIC_LINK_TEST $(LDFLAGS) -o $@ $(SRC_DEP) + test_rwlock: test_rwlock.c ${URCU_SIGNAL} $(CC) ${CFLAGS} $(LDFLAGS) -o $@ $(SRC_DEP) diff --git a/tests/test_qsbr.c b/tests/test_qsbr.c index 89a9423..4379771 100644 --- a/tests/test_qsbr.c +++ b/tests/test_qsbr.c @@ -56,7 +56,11 @@ static inline pid_t gettid(void) } #endif +#ifndef DYNAMIC_LINK_TEST #define _LGPL_SOURCE +#else +#define debug_yield_read() +#endif #include "../urcu-qsbr.h" struct test_array { @@ -215,18 +219,18 @@ void *thr_reader(void *_count) smp_mb(); for (;;) { - _rcu_read_lock(); - local_ptr = _rcu_dereference(test_rcu_pointer); + rcu_read_lock(); + local_ptr = rcu_dereference(test_rcu_pointer); debug_yield_read(); if (local_ptr) assert(local_ptr->a == 8); if (unlikely(rduration)) loop_sleep(rduration); - _rcu_read_unlock(); + rcu_read_unlock(); nr_reads++; /* QS each 1024 reads */ if (unlikely((nr_reads & ((1 << 10) - 1)) == 0)) - _rcu_quiescent_state(); + rcu_quiescent_state(); if (unlikely(!test_duration_read())) break; } @@ -258,7 +262,7 @@ void *thr_writer(void *_count) for (;;) { new = test_array_alloc(); new->a = 8; - old = _rcu_publish_content(&test_rcu_pointer, new); + old = rcu_publish_content(&test_rcu_pointer, new); /* can be done after unlock */ if (old) old->a = 0; diff --git a/urcu-qsbr-static.h b/urcu-qsbr-static.h new file mode 100644 index 0000000..752d4b9 --- /dev/null +++ b/urcu-qsbr-static.h @@ -0,0 +1,255 @@ +#ifndef _URCU_QSBR_STATIC_H +#define _URCU_QSBR_STATIC_H + +/* + * urcu-qsbr-static.h + * + * Userspace RCU QSBR header. + * + * TO BE INCLUDED ONLY IN LGPL-COMPATIBLE CODE. See urcu-qsbr.h for linking + * dynamically with the userspace rcu QSBR library. + * + * Copyright (c) 2009 Mathieu Desnoyers + * Copyright (c) 2009 Paul E. McKenney, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * IBM's contributions to this file may be relicensed under LGPLv2 or later. + */ + +#include +#include +#include + +#include +#include + +/* + * Identify a shared load. A smp_rmc() or smp_mc() should come before the load. + */ +#define _LOAD_SHARED(p) ACCESS_ONCE(p) + +/* + * Load a data from shared memory, doing a cache flush if required. + */ +#define LOAD_SHARED(p) \ + ({ \ + smp_rmc(); \ + _LOAD_SHARED(p); \ + }) + +/* + * Identify a shared store. A smp_wmc() or smp_mc() should follow the store. + */ +#define _STORE_SHARED(x, v) ({ ACCESS_ONCE(x) = (v); }) + +/* + * Store v into x, where x is located in shared memory. Performs the required + * cache flush after writing. Returns v. + */ +#define STORE_SHARED(x, v) \ + ({ \ + _STORE_SHARED(x, v); \ + smp_wmc(); \ + (v); \ + }) + +/** + * _rcu_dereference - reads (copy) a RCU-protected pointer to a local variable + * into a RCU read-side critical section. The pointer can later be safely + * dereferenced within the critical section. + * + * This ensures that the pointer copy is invariant thorough the whole critical + * section. + * + * Inserts memory barriers on architectures that require them (currently only + * Alpha) and documents which pointers are protected by RCU. + * + * Should match rcu_assign_pointer() or rcu_xchg_pointer(). + */ + +#define _rcu_dereference(p) ({ \ + typeof(p) _________p1 = LOAD_SHARED(p); \ + smp_read_barrier_depends(); \ + (_________p1); \ + }) + +/* + * This code section can only be included in LGPL 2.1 compatible source code. + * See below for the function call wrappers which can be used in code meant to + * be only linked with the Userspace RCU library. This comes with a small + * performance degradation on the read-side due to the added function calls. + * This is required to permit relinking with newer versions of the library. + */ + +/* + * If a reader is really non-cooperative and refuses to commit its + * rcu_reader_qs_gp count to memory (there is no barrier in the reader + * per-se), kick it after a few loops waiting for it. + */ +#define KICK_READER_LOOPS 10000 + +#ifdef DEBUG_RCU +#define rcu_assert(args...) assert(args) +#else +#define rcu_assert(args...) +#endif + +#ifdef DEBUG_YIELD +#include +#include +#include +#include + +#define YIELD_READ (1 << 0) +#define YIELD_WRITE (1 << 1) + +/* maximum sleep delay, in us */ +#define MAX_SLEEP 50 + +extern unsigned int yield_active; +extern unsigned int __thread rand_yield; + +static inline void debug_yield_read(void) +{ + if (yield_active & YIELD_READ) + if (rand_r(&rand_yield) & 0x1) + usleep(rand_r(&rand_yield) % MAX_SLEEP); +} + +static inline void debug_yield_write(void) +{ + if (yield_active & YIELD_WRITE) + if (rand_r(&rand_yield) & 0x1) + usleep(rand_r(&rand_yield) % MAX_SLEEP); +} + +static inline void debug_yield_init(void) +{ + rand_yield = time(NULL) ^ pthread_self(); +} +#else +static inline void debug_yield_read(void) +{ +} + +static inline void debug_yield_write(void) +{ +} + +static inline void debug_yield_init(void) +{ + +} +#endif + +static inline void reader_barrier() +{ + smp_mb(); +} + +/* + * Global quiescent period counter with low-order bits unused. + * Using a int rather than a char to eliminate false register dependencies + * causing stalls on some architectures. + */ +extern long urcu_gp_ctr; + +extern long __thread rcu_reader_qs_gp; + +static inline int rcu_gp_ongoing(long *value) +{ + if (value == NULL) + return 0; + + return LOAD_SHARED(*value) & 1; +} + +static inline void _rcu_read_lock(void) +{ + rcu_assert(rcu_reader_qs_gp & 1); +} + +static inline void _rcu_read_unlock(void) +{ +} + +static inline void _rcu_quiescent_state(void) +{ + smp_mb(); + rcu_reader_qs_gp = ACCESS_ONCE(urcu_gp_ctr) + 1; + smp_mb(); +} + +static inline void _rcu_thread_offline(void) +{ + smp_mb(); + rcu_reader_qs_gp = ACCESS_ONCE(urcu_gp_ctr); +} + +static inline void _rcu_thread_online(void) +{ + rcu_reader_qs_gp = ACCESS_ONCE(urcu_gp_ctr) + 1; + smp_mb(); +} + +/** + * _rcu_assign_pointer - assign (publicize) a pointer to a new data structure + * meant to be read by RCU read-side critical sections. Returns the assigned + * value. + * + * Documents which pointers will be dereferenced by RCU read-side critical + * sections and adds the required memory barriers on architectures requiring + * them. It also makes sure the compiler does not reorder code initializing the + * data structure before its publication. + * + * Should match rcu_dereference_pointer(). + */ + +#define _rcu_assign_pointer(p, v) \ + ({ \ + if (!__builtin_constant_p(v) || \ + ((v) != NULL)) \ + wmb(); \ + STORE_SHARED(p, v); \ + }) + +/** + * _rcu_xchg_pointer - same as rcu_assign_pointer, but returns the previous + * pointer to the data structure, which can be safely freed after waiting for a + * quiescent state using synchronize_rcu(). + */ + +#define _rcu_xchg_pointer(p, v) \ + ({ \ + if (!__builtin_constant_p(v) || \ + ((v) != NULL)) \ + wmb(); \ + xchg(p, v); \ + }) + +/* + * Exchanges the pointer and waits for quiescent state. + * The pointer returned can be freed. + */ +#define _rcu_publish_content(p, v) \ + ({ \ + void *oldptr; \ + oldptr = _rcu_xchg_pointer(p, v); \ + synchronize_rcu(); \ + oldptr; \ + }) + +#endif /* _URCU_QSBR_STATIC_H */ diff --git a/urcu-qsbr.c b/urcu-qsbr.c index 0576f41..300eb97 100644 --- a/urcu-qsbr.c +++ b/urcu-qsbr.c @@ -1,7 +1,7 @@ /* - * urcu.c + * urcu-qsbr.c * - * Userspace RCU library + * Userspace RCU QSBR library * * Copyright (c) 2009 Mathieu Desnoyers * Copyright (c) 2009 Paul E. McKenney, IBM Corporation. @@ -32,9 +32,9 @@ #include #include -#include "urcu-qsbr.h" +#include "urcu-qsbr-static.h" /* Do not #define _LGPL_SOURCE to ensure we can emit the wrapper symbols */ -//#include "urcu.h" +#include "urcu-qsbr.h" pthread_mutex_t urcu_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -200,6 +200,21 @@ void *rcu_publish_content_sym(void **p, void *v) return oldptr; } +void rcu_quiescent_state(void) +{ + _rcu_quiescent_state(); +} + +void rcu_thread_offline(void) +{ + _rcu_thread_offline(); +} + +void rcu_thread_online(void) +{ + _rcu_thread_online(); +} + static void rcu_add_reader(pthread_t id) { struct reader_registry *oldarray; diff --git a/urcu-qsbr.h b/urcu-qsbr.h index 448074d..1dc4ddd 100644 --- a/urcu-qsbr.h +++ b/urcu-qsbr.h @@ -2,15 +2,14 @@ #define _URCU_QSBR_H /* - * urcu-static.h + * urcu-qsbr.h * - * Userspace RCU header. + * Userspace RCU QSBR header. * - * TO BE INCLUDED ONLY IN LGPL-COMPATIBLE CODE. See urcu.h for linking - * dynamically with the userspace rcu library. + * LGPL-compatible code should include this header with : * - * Copyright (c) 2009 Mathieu Desnoyers - * Copyright (c) 2009 Paul E. McKenney, IBM Corporation. + * #define _LGPL_SOURCE + * #include * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -31,234 +30,65 @@ #include #include -#include - -#include -#include - -/* - * Identify a shared load. A smp_rmc() or smp_mc() should come before the load. - */ -#define _LOAD_SHARED(p) ACCESS_ONCE(p) - -/* - * Load a data from shared memory, doing a cache flush if required. - */ -#define LOAD_SHARED(p) \ - ({ \ - smp_rmc(); \ - _LOAD_SHARED(p); \ - }) - -/* - * Identify a shared store. A smp_wmc() or smp_mc() should follow the store. - */ -#define _STORE_SHARED(x, v) ({ ACCESS_ONCE(x) = (v); }) /* - * Store v into x, where x is located in shared memory. Performs the required - * cache flush after writing. Returns v. - */ -#define STORE_SHARED(x, v) \ - ({ \ - _STORE_SHARED(x, v); \ - smp_wmc(); \ - (v); \ - }) - -/** - * _rcu_dereference - reads (copy) a RCU-protected pointer to a local variable - * into a RCU read-side critical section. The pointer can later be safely - * dereferenced within the critical section. - * - * This ensures that the pointer copy is invariant thorough the whole critical - * section. + * Important ! * - * Inserts memory barriers on architectures that require them (currently only - * Alpha) and documents which pointers are protected by RCU. - * - * Should match rcu_assign_pointer() or rcu_xchg_pointer(). + * Each thread containing read-side critical sections must be registered + * with rcu_register_thread() before calling rcu_read_lock(). + * rcu_unregister_thread() should be called before the thread exits. */ -#define _rcu_dereference(p) ({ \ - typeof(p) _________p1 = LOAD_SHARED(p); \ - smp_read_barrier_depends(); \ - (_________p1); \ - }) - -/* - * This code section can only be included in LGPL 2.1 compatible source code. - * See below for the function call wrappers which can be used in code meant to - * be only linked with the Userspace RCU library. This comes with a small - * performance degradation on the read-side due to the added function calls. - * This is required to permit relinking with newer versions of the library. - */ +#ifdef _LGPL_SOURCE -/* - * The signal number used by the RCU library can be overridden with - * -DSIGURCU= when compiling the library. - */ -#ifndef SIGURCU -#define SIGURCU SIGUSR1 -#endif +#include /* - * If a reader is really non-cooperative and refuses to commit its - * rcu_reader_qs_gp count to memory (there is no barrier in the reader - * per-se), kick it after a few loops waiting for it. + * Mappings for static use of the userspace RCU library. + * Should only be used in LGPL-compatible code. */ -#define KICK_READER_LOOPS 10000 -#ifdef DEBUG_RCU -#define rcu_assert(args...) assert(args) -#else -#define rcu_assert(args...) -#endif +#define rcu_dereference _rcu_dereference +#define rcu_read_lock _rcu_read_lock +#define rcu_read_unlock _rcu_read_unlock -#ifdef DEBUG_YIELD -#include -#include -#include -#include +#define rcu_quiescent_state _rcu_quiescent_state +#define rcu_thread_offline _rcu_thread_offline +#define rcu_thread_online _rcu_thread_online -#define YIELD_READ (1 << 0) -#define YIELD_WRITE (1 << 1) +#define rcu_assign_pointer _rcu_assign_pointer +#define rcu_xchg_pointer _rcu_xchg_pointer +#define rcu_publish_content _rcu_publish_content -/* maximum sleep delay, in us */ -#define MAX_SLEEP 50 - -extern unsigned int yield_active; -extern unsigned int __thread rand_yield; - -static inline void debug_yield_read(void) -{ - if (yield_active & YIELD_READ) - if (rand_r(&rand_yield) & 0x1) - usleep(rand_r(&rand_yield) % MAX_SLEEP); -} - -static inline void debug_yield_write(void) -{ - if (yield_active & YIELD_WRITE) - if (rand_r(&rand_yield) & 0x1) - usleep(rand_r(&rand_yield) % MAX_SLEEP); -} - -static inline void debug_yield_init(void) -{ - rand_yield = time(NULL) ^ pthread_self(); -} -#else -static inline void debug_yield_read(void) -{ -} - -static inline void debug_yield_write(void) -{ -} - -static inline void debug_yield_init(void) -{ - -} -#endif - -static inline void reader_barrier() -{ - smp_mb(); -} +#else /* !_LGPL_SOURCE */ /* - * Global quiescent period counter with low-order bits unused. - * Using a int rather than a char to eliminate false register dependencies - * causing stalls on some architectures. + * library wrappers to be used by non-LGPL compatible source code. */ -extern long urcu_gp_ctr; -extern long __thread rcu_reader_qs_gp; +extern void rcu_read_lock(void); +extern void rcu_read_unlock(void); -static inline int rcu_gp_ongoing(long *value) -{ - if (value == NULL) - return 0; +extern void *rcu_dereference(void *p); - return LOAD_SHARED(*value) & 1; -} +extern void rcu_quiescent_state(void); +extern void rcu_thread_offline(void); +extern void rcu_thread_online(void); -static inline void _rcu_read_lock(void) -{ - rcu_assert(rcu_reader_qs_gp & 1); -} +extern void *rcu_assign_pointer_sym(void **p, void *v); -static inline void _rcu_read_unlock(void) -{ -} +#define rcu_assign_pointer(p, v) \ + rcu_assign_pointer_sym((void **)(p), (v)) -static inline void _rcu_quiescent_state(void) -{ - smp_mb(); - rcu_reader_qs_gp = ACCESS_ONCE(urcu_gp_ctr) + 1; - smp_mb(); -} +extern void *rcu_xchg_pointer_sym(void **p, void *v); +#define rcu_xchg_pointer(p, v) \ + rcu_xchg_pointer_sym((void **)(p), (v)) -static inline void _rcu_thread_offline(void) -{ - smp_mb(); - rcu_reader_qs_gp = ACCESS_ONCE(urcu_gp_ctr); -} +extern void *rcu_publish_content_sym(void **p, void *v); +#define rcu_publish_content(p, v) \ + rcu_publish_content_sym((void **)(p), (v)) -static inline void _rcu_thread_online(void) -{ - rcu_reader_qs_gp = ACCESS_ONCE(urcu_gp_ctr) + 1; - smp_mb(); -} - -/** - * _rcu_assign_pointer - assign (publicize) a pointer to a new data structure - * meant to be read by RCU read-side critical sections. Returns the assigned - * value. - * - * Documents which pointers will be dereferenced by RCU read-side critical - * sections and adds the required memory barriers on architectures requiring - * them. It also makes sure the compiler does not reorder code initializing the - * data structure before its publication. - * - * Should match rcu_dereference_pointer(). - */ - -#define _rcu_assign_pointer(p, v) \ - ({ \ - if (!__builtin_constant_p(v) || \ - ((v) != NULL)) \ - wmb(); \ - STORE_SHARED(p, v); \ - }) - -/** - * _rcu_xchg_pointer - same as rcu_assign_pointer, but returns the previous - * pointer to the data structure, which can be safely freed after waiting for a - * quiescent state using synchronize_rcu(). - */ - -#define _rcu_xchg_pointer(p, v) \ - ({ \ - if (!__builtin_constant_p(v) || \ - ((v) != NULL)) \ - wmb(); \ - xchg(p, v); \ - }) - -/* - * Exchanges the pointer and waits for quiescent state. - * The pointer returned can be freed. - */ -#define _rcu_publish_content(p, v) \ - ({ \ - void *oldptr; \ - oldptr = _rcu_xchg_pointer(p, v); \ - synchronize_rcu(); \ - oldptr; \ - }) +#endif /* !_LGPL_SOURCE */ extern void synchronize_rcu(void); diff --git a/urcu-static.h b/urcu-static.h index 66a06df..0f3eba1 100644 --- a/urcu-static.h +++ b/urcu-static.h @@ -117,6 +117,12 @@ */ #define KICK_READER_LOOPS 10000 +#ifdef DEBUG_RCU +#define rcu_assert(args...) assert(args) +#else +#define rcu_assert(args...) +#endif + #ifdef DEBUG_YIELD #include #include -- 2.34.1