From: Mathieu Desnoyers Date: Sun, 25 Aug 2013 18:22:15 +0000 (-0400) Subject: Implement DQ unit test X-Git-Url: http://git.liburcu.org/?p=urcu.git;a=commitdiff_plain;h=refs%2Fheads%2Frcudq Implement DQ unit test Signed-off-by: Mathieu Desnoyers --- diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 1fa8b71..b394adf 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -3,7 +3,8 @@ AM_CFLAGS=-I$(top_srcdir) -I$(top_builddir) -I$(top_srcdir)/tests/common -g noinst_PROGRAMS = test_uatomic \ test_urcu_multiflavor \ - test_urcu_multiflavor_dynlink + test_urcu_multiflavor_dynlink \ + test_urcu_dq noinst_HEADERS = test_urcu_multiflavor.h @@ -55,7 +56,11 @@ 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) +test_urcu_dq_SOURCES = test_urcu_dq.c $(COMPAT) +test_urcu_dq_LDADD = $(URCU_LIB) + check-am: ./test_uatomic ./test_urcu_multiflavor ./test_urcu_multiflavor_dynlink + ./test_urcu_dq diff --git a/tests/unit/test_urcu_dq.c b/tests/unit/test_urcu_dq.c new file mode 100644 index 0000000..8ef6f78 --- /dev/null +++ b/tests/unit/test_urcu_dq.c @@ -0,0 +1,241 @@ +/* + * test_urcu_dq.c + * + * Userspace RCU library - double-ended queue unit test + * + * Copyright 2013 - Mathieu Desnoyers + * + * 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 +#include +#include +#include +#include +#include + +#define err_printf(fmt, args...) \ + fprintf(stderr, "[error in %s@%d:%s()] " fmt, __FILE__, __LINE__, __func__, ## args) + +struct myobj { + int a; + int b; + struct cds_rcudq_head node; + struct rcu_head rcu_head; +}; + +static CDS_RCUDQ_HEAD(dq); + +static +struct myobj *create_obj(int a, int b) +{ + struct myobj *obj; + + obj = malloc(sizeof(*obj)); + if (!obj) + return NULL; + obj->a = a; + obj->b = b; + CDS_INIT_RCUDQ_HEAD(&obj->node); + return obj; +} + +static +void poison_head(struct cds_rcudq_head *head) +{ + memset(head, 42, sizeof(*head)); +} + +static +void print_obj(struct myobj *obj) +{ + printf("(%d, %d) ", obj->a, obj->b); +} + +static +void free_obj(struct rcu_head *rcu_head) +{ + struct myobj *obj = caa_container_of(rcu_head, struct myobj, rcu_head); + + free(obj); +} + +int main(int argc, char **argv) +{ + struct myobj *obj, *tmp; + struct cds_rcudq_head *pos, *p; + unsigned int i, j; + + rcu_register_thread(); + + if (!cds_rcudq_empty(&dq)) { + err_printf("Queue is not empty as expected\n"); + goto error; + } + + poison_head(&dq); + CDS_INIT_RCUDQ_HEAD(&dq); + if (!cds_rcudq_empty(&dq)) { + err_printf("Queue is not empty as expected\n"); + goto error; + } + + /* Single updater */ + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + obj = create_obj(i, j); + if (!obj) + goto error; + /* Add to tail */ + cds_rcudq_add_tail(&obj->node, &dq); + } + } + for (i = 42; i < 46; i++) { + obj = create_obj(i, i); + if (!obj) + goto error; + /* Add to head */ + cds_rcudq_add(&obj->node, &dq); + } + + printf("cds_rcudq_first_entry()\n"); + obj = cds_rcudq_first_entry(&dq, struct myobj, node); + print_obj(obj); + printf("\n"); + + printf("cds_rcudq_for_each()\n"); + cds_rcudq_for_each(pos, &dq) { + obj = cds_rcudq_entry(pos, struct myobj, node); + print_obj(obj); + } + printf("\n"); + + printf("cds_rcudq_for_each_safe()\n"); + cds_rcudq_for_each_safe(pos, p, &dq) { + obj = cds_rcudq_entry(pos, struct myobj, node); + print_obj(obj); + if (obj->a == 42) { + printf("(removing) "); + cds_rcudq_del(&obj->node); + call_rcu(&obj->rcu_head, free_obj); + } + } + printf("\n"); + + printf("cds_rcudq_for_each_entry()\n"); + cds_rcudq_for_each_entry(obj, &dq, node) { + print_obj(obj); + } + printf("\n"); + + printf("cds_rcudq_for_each_entry_safe()\n"); + cds_rcudq_for_each_entry_safe(obj, tmp, &dq, node) { + print_obj(obj); + if (obj->a == 43) { + printf("(removing) "); + cds_rcudq_del(&obj->node); + call_rcu(&obj->rcu_head, free_obj); + } + } + printf("\n"); + + printf("cds_rcudq_for_each_reverse()\n"); + cds_rcudq_for_each_reverse(pos, &dq) { + obj = cds_rcudq_entry(pos, struct myobj, node); + print_obj(obj); + } + printf("\n"); + + printf("cds_rcudq_for_each_reverse_safe()\n"); + cds_rcudq_for_each_reverse_safe(pos, p, &dq) { + obj = cds_rcudq_entry(pos, struct myobj, node); + print_obj(obj); + if (obj->a == 44) { + printf("(removing) "); + cds_rcudq_del(&obj->node); + call_rcu(&obj->rcu_head, free_obj); + } + } + printf("\n"); + + printf("cds_rcudq_for_each_entry_reverse()\n"); + cds_rcudq_for_each_entry_reverse(obj, &dq, node) { + print_obj(obj); + } + printf("\n"); + + printf("cds_rcudq_for_each_entry_reverse_safe()\n"); + cds_rcudq_for_each_entry_reverse_safe(obj, tmp, &dq, node) { + print_obj(obj); + if (obj->a == 45) { + printf("(removing) "); + cds_rcudq_del(&obj->node); + call_rcu(&obj->rcu_head, free_obj); + } + } + printf("\n"); + + + rcu_read_lock(); + + printf("cds_rcudq_for_each_rcu()\n"); + cds_rcudq_for_each_rcu(pos, &dq) { + obj = cds_rcudq_entry(pos, struct myobj, node); + print_obj(obj); + } + printf("\n"); + + printf("cds_rcudq_for_each_entry_rcu()\n"); + cds_rcudq_for_each_entry_rcu(obj, &dq, node) { + print_obj(obj); + } + printf("\n"); + + printf("cds_rcudq_for_each_reverse_rcu()\n"); + cds_rcudq_for_each_reverse_rcu(pos, &dq) { + obj = cds_rcudq_entry(pos, struct myobj, node); + print_obj(obj); + } + printf("\n"); + + printf("cds_rcudq_for_each_entry_reverse_rcu()\n"); + cds_rcudq_for_each_entry_reverse_rcu(obj, &dq, node) { + print_obj(obj); + } + printf("\n"); + + rcu_read_unlock(); + + cds_rcudq_for_each_entry_safe(obj, tmp, &dq, node) { + cds_rcudq_del(&obj->node); + call_rcu(&obj->rcu_head, free_obj); + } + + if (!cds_rcudq_empty(&dq)) { + err_printf("Queue is not empty as expected\n"); + goto error; + } + + /* Free memory (in flight call_rcu callback execution) before exiting */ + rcu_barrier(); + + rcu_unregister_thread(); + + exit(EXIT_SUCCESS); + +error: + exit(EXIT_FAILURE); +}