From 674e69f2590a445ab1db7d6383adcf6c53a07861 Mon Sep 17 00:00:00 2001 From: Samy Al Bahra Date: Mon, 12 Mar 2012 12:27:11 -0400 Subject: [PATCH] ck_queue: Add BSD-derived queue.h facility. Writer-side synchronization is still necessary. My current use-cases call for SLIST and LIST implementations, and as such, I've only implemented support for these. TAILQ facilities will be developed when the time comes that I require them or if there is sufficient user-demand. --- .gitignore | 2 + include/ck_queue.h | 281 +++++++++++++++++++++++ regressions/Makefile | 2 + regressions/ck_queue/validate/Makefile | 18 ++ regressions/ck_queue/validate/ck_list.c | 219 ++++++++++++++++++ regressions/ck_queue/validate/ck_slist.c | 222 ++++++++++++++++++ 6 files changed, 744 insertions(+) create mode 100644 include/ck_queue.h create mode 100644 regressions/ck_queue/validate/Makefile create mode 100644 regressions/ck_queue/validate/ck_list.c create mode 100644 regressions/ck_queue/validate/ck_slist.c diff --git a/.gitignore b/.gitignore index 18ad3ab..8cfc166 100644 --- a/.gitignore +++ b/.gitignore @@ -100,3 +100,5 @@ regressions/ck_stack/validate/upmc_pop regressions/ck_stack/validate/upmc_push regressions/ck_brlock/benchmark/throughput regressions/ck_rwlock/benchmark/throughput +regressions/ck_queue/validate/ck_list +regressions/ck_queue/validate/ck_slist diff --git a/include/ck_queue.h b/include/ck_queue.h new file mode 100644 index 0000000..004461d --- /dev/null +++ b/include/ck_queue.h @@ -0,0 +1,281 @@ +/* + * Copyright 2012 Samy Al Bahra. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD: release/9.0.0/sys/sys/queue.h 221843 2011-05-13 15:49:23Z mdf $ + */ + +#ifndef _CK_QUEUE_H_ +#define _CK_QUEUE_H_ + +#include + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * It is safe to use _FOREACH/_FOREACH_SAFE in the presence of concurrent + * modifications to the list. Writers to these lists must, on the other hand, + * implement writer-side synchronization. The _SWAP operations are not atomic. + * This facility is currently unsupported on architectures such as the Alpha + * which require load-depend memory fences. + * + * CK_SLIST CK_LIST + * _HEAD + + + * _HEAD_INITIALIZER + + + * _ENTRY + + + * _INIT + + + * _EMPTY + + + * _FIRST + + + * _NEXT + + + * _FOREACH + + + * _FOREACH_SAFE + + + * _INSERT_HEAD + + + * _INSERT_BEFORE - + + * _INSERT_AFTER + + + * _REMOVE_AFTER + - + * _REMOVE_HEAD + - + * _REMOVE + + + * _SWAP + + + */ + +/* + * Singly-linked List declarations. + */ +#define CK_SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define CK_SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define CK_SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define CK_SLIST_EMPTY(head) \ + (ck_pr_load_ptr(&(head)->slh_first) == NULL) + +#define CK_SLIST_FIRST(head) \ + (ck_pr_load_ptr(&(head)->slh_first)) + +#define CK_SLIST_NEXT(elm, field) \ + ck_pr_load_ptr(&((elm)->field.sle_next)) + +#define CK_SLIST_FOREACH(var, head, field) \ + for ((var) = CK_SLIST_FIRST((head)); \ + (var); \ + (var) = CK_SLIST_NEXT((var), field)) + +#define CK_SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = (head)->slh_first; \ + (var) && ((tvar) = (var)->field.sle_next, 1); \ + (var) = (tvar)) + +#define CK_SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &(head)->slh_first; \ + ((var) = ck_pr_load_ptr(varp)) != NULL; \ + (varp) = &(var)->field.sle_next) + +#define CK_SLIST_INIT(head) do { \ + ck_pr_store_ptr(&(head)->slh_first, NULL); \ + ck_pr_fence_store(); \ +} while (0) + +#define CK_SLIST_INSERT_AFTER(a, b, field) do { \ + (b)->field.sle_next = (a)->field.sle_next; \ + ck_pr_fence_store(); \ + ck_pr_store_ptr(&(a)->field.sle_next, b); \ +} while (0) + +#define CK_SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + ck_pr_fence_store(); \ + ck_pr_store_ptr(&(head)->slh_first, elm); \ +} while (0) + +#define CK_SLIST_REMOVE_AFTER(elm, field) do { \ + ck_pr_store_ptr(&(elm)->field.sle_next, \ + (elm)->field.sle_next->field.sle_next); \ +} while (0) + +#define CK_SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + CK_SLIST_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->slh_first; \ + while (curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + CK_SLIST_REMOVE_AFTER(curelm, field); \ + } \ +} while (0) + +#define CK_SLIST_REMOVE_HEAD(head, field) do { \ + ck_pr_store_ptr(&(head)->slh_first, \ + (head)->slh_first->field.sle_next); \ +} while (0) + +/* + * This operation is not applied atomically. + */ +#define CK_SLIST_SWAP(a, b, type) do { \ + struct type *swap_first = (a)->slh_first; \ + (a)->slh_first = (b)->slh_first; \ + (b)->slh_first = swap_first; \ +} while (0) + +/* + * List declarations. + */ +#define CK_LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define CK_LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define CK_LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +#define CK_LIST_FIRST(head) ck_pr_load_ptr(&(head)->lh_first) +#define CK_LIST_EMPTY(head) (CK_LIST_FIRST(head) == NULL) +#define CK_LIST_NEXT(elm, field) ck_pr_load_ptr(&(elm)->field.le_next) + +#define CK_LIST_FOREACH(var, head, field) \ + for ((var) = CK_LIST_FIRST((head)); \ + (var); \ + (var) = CK_LIST_NEXT((var), field)) + +#define CK_LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = CK_LIST_FIRST((head)); \ + (var) && ((tvar) = CK_LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define CK_LIST_INIT(head) do { \ + ck_pr_store_ptr(&(head)->lh_first, NULL); \ + ck_pr_fence_store(); \ +} while (0) + +#define CK_LIST_INSERT_AFTER(listelm, elm, field) do { \ + (elm)->field.le_next = (listelm)->field.le_next; \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ + ck_pr_fence_store(); \ + if ((listelm)->field.le_next != NULL) \ + (listelm)->field.le_next->field.le_prev = &(elm)->field.le_next;\ + ck_pr_store_ptr(&(listelm)->field.le_next, elm); \ +} while (0) + +#define CK_LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + ck_pr_fence_store(); \ + ck_pr_store_ptr((listelm)->field.le_prev, (elm)); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define CK_LIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.le_next = (head)->lh_first; \ + ck_pr_fence_store(); \ + ck_pr_store_ptr(&(head)->lh_first, elm); \ + if ((elm)->field.le_next != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next; \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define CK_LIST_REMOVE(elm, field) do { \ + ck_pr_store_ptr((elm)->field.le_prev, (elm)->field.le_next); \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = (elm)->field.le_prev; \ +} while (0) + +/* + * This operation is not applied atomically. + */ +#define CK_LIST_SWAP(head1, head2, type, field) do { \ + struct type *swap_tmp = (head1)->lh_first; \ + (head1)->lh_first = (head2)->lh_first; \ + (head2)->lh_first = swap_tmp; \ + if ((swap_tmp = (head1)->lh_first) != NULL) \ + swap_tmp->field.le_prev = &(head1)->lh_first; \ + if ((swap_tmp = (head2)->lh_first) != NULL) \ + swap_tmp->field.le_prev = &(head2)->lh_first; \ +} while (0) + +#endif /* _CK_QUEUE_H */ diff --git a/regressions/Makefile b/regressions/Makefile index 30eb984..bfbd89a 100644 --- a/regressions/Makefile +++ b/regressions/Makefile @@ -1,6 +1,7 @@ .PHONY: all clean all: + $(MAKE) -C ./ck_queue/validate all $(MAKE) -C ./ck_brlock/validate all $(MAKE) -C ./ck_brlock/benchmark all $(MAKE) -C ./ck_spinlock/validate all @@ -25,6 +26,7 @@ all: $(MAKE) -C ./ck_hp/benchmark all clean: + $(MAKE) -C ./ck_queue/validate clean $(MAKE) -C ./ck_brlock/validate clean $(MAKE) -C ./ck_brlock/benchmark clean $(MAKE) -C ./ck_spinlock/validate clean diff --git a/regressions/ck_queue/validate/Makefile b/regressions/ck_queue/validate/Makefile new file mode 100644 index 0000000..51989a7 --- /dev/null +++ b/regressions/ck_queue/validate/Makefile @@ -0,0 +1,18 @@ +.PHONY: clean distribution + +HEADER=../../../include/ck_queue.h +OBJECTS=ck_slist ck_list + +all: $(OBJECTS) + +ck_slist: $(HEADER) ck_slist.c + $(CC) $(CFLAGS) -o ck_slist ck_slist.c + +ck_list: $(HEADER) ck_list.c + $(CC) $(CFLAGS) -o ck_list ck_list.c + +clean: + rm -rf *~ *.o $(OBJECTS) *.dSYM + +include ../../../build/regressions.build +CFLAGS+=-O0 -g3 diff --git a/regressions/ck_queue/validate/ck_list.c b/regressions/ck_queue/validate/ck_list.c new file mode 100644 index 0000000..21b9dae --- /dev/null +++ b/regressions/ck_queue/validate/ck_list.c @@ -0,0 +1,219 @@ +/* + * Copyright 2012 Samy Al Bahra. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include "../../common.h" + +struct test { + int value; + CK_LIST_ENTRY(test) list_entry; +}; +static CK_LIST_HEAD(test_list, test) head = CK_LIST_HEAD_INITIALIZER(head); + +static int goal; + +static void +test_foreach(void) +{ + struct test *n, *next, *safe; + int i, s = 0, j = 0, k = 0; + + for (i = goal; i != 0; i = goal) { + s = 0; + + CK_LIST_FOREACH(n, &head, list_entry) { + j++; + if (s == 0) + s = n->value; + else + s = s - 1; + + if (n->value != s) { + fprintf(stderr, "\nExpected %d, but got %d.\n", + s, n->value); + exit(EXIT_FAILURE); + } + + next = CK_LIST_NEXT(n, list_entry); + if (next != NULL && next->value != s - 1) { + fprintf(stderr, "\nExpected %d, but got %d.\n", + s, next->value); + exit(EXIT_FAILURE); + } + + i--; + } + + if (i == 0) + break; + + s = 0; + CK_LIST_FOREACH_SAFE(n, &head, list_entry, safe) { + k++; + + if (s == 0) + s = n->value; + else + s = s - 1; + + if (n->value != s) { + fprintf(stderr, "\nExpected %d, but got %d.\n", + s, n->value); + exit(EXIT_FAILURE); + } + + next = CK_LIST_NEXT(n, list_entry); + if (next != NULL && next->value != s - 1) { + fprintf(stderr, "\nExpected %d, but got %d.\n", + s, next->value); + exit(EXIT_FAILURE); + } + + i--; + } + + if (i == 0 || CK_LIST_EMPTY(&head) == true) + break; + } + + fprintf(stderr, "(%d, %d) ", j, k); + return; +} + +static void * +execute(void *c) +{ + + (void)c; + test_foreach(); + return NULL; +} + +int +main(int argc, char *argv[]) +{ + pthread_t *thread; + struct test *n; + int n_threads, i; + + if (argc != 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(EXIT_FAILURE); + } + + n_threads = atoi(argv[1]); + if (n_threads < 1) { + fprintf(stderr, "ERROR: Number of threads must be >= 1.\n"); + exit(EXIT_FAILURE); + } + + thread = malloc(sizeof(pthread_t) * n_threads); + assert(thread != NULL); + + goal = atoi(argv[2]); + if (goal < 4) { + fprintf(stderr, "ERROR: Number of entires must be >= 4.\n"); + exit(EXIT_FAILURE); + } + + fprintf(stderr, "Beginning serial test..."); + CK_LIST_INIT(&head); + + for (i = 1; i <= goal; i++) { + n = malloc(sizeof *n); + assert(n != NULL); + n->value = i; + CK_LIST_INSERT_HEAD(&head, n, list_entry); + } + + test_foreach(); + + for (i = 1; i <= goal; i++) { + n = CK_LIST_FIRST(&head); + CK_LIST_REMOVE(n, list_entry); + free(n); + } + + if (CK_LIST_EMPTY(&head) == false) { + fprintf(stderr, "List is not empty after bulk removal.\n"); + exit(EXIT_FAILURE); + } + + fprintf(stderr, "done (success)\n"); + + fprintf(stderr, "Beginning parallel traversal..."); + + n = malloc(sizeof *n); + assert(n != NULL); + n->value = 1; + CK_LIST_INSERT_HEAD(&head, n, list_entry); + + for (i = 0; i < n_threads; i++) { + int r = pthread_create(&thread[i], NULL, execute, NULL); + assert(r == 0); + } + + for (i = 2; i <= goal; i++) { + volatile int j; + + n = malloc(sizeof *n); + assert(n != NULL); + n->value = i; + CK_LIST_INSERT_HEAD(&head, n, list_entry); + for (j = 0; j <= 1000; j++); + } + + for (i = 0; i < n_threads; i++) + pthread_join(thread[i], NULL); + + for (i = 0; i < n_threads; i++) { + int r = pthread_create(&thread[i], NULL, execute, NULL); + assert(r == 0); + } + + for (i = 1; i <= goal; i++) { + volatile int j; + + if (CK_LIST_EMPTY(&head) == false) { + struct test *r = CK_LIST_FIRST(&head); + CK_LIST_REMOVE(r, list_entry); + } + + for (j = 0; j <= 1000; j++); + } + + for (i = 0; i < n_threads; i++) + pthread_join(thread[i], NULL); + + fprintf(stderr, "done (success)\n"); + return (0); +} + diff --git a/regressions/ck_queue/validate/ck_slist.c b/regressions/ck_queue/validate/ck_slist.c new file mode 100644 index 0000000..63d2355 --- /dev/null +++ b/regressions/ck_queue/validate/ck_slist.c @@ -0,0 +1,222 @@ +/* + * Copyright 2012 Samy Al Bahra. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include "../../common.h" + +struct test { + int value; + CK_SLIST_ENTRY(test) list_entry; +}; +static CK_SLIST_HEAD(test_list, test) head = CK_SLIST_HEAD_INITIALIZER(head); + +static int goal; + +static void +test_foreach(void) +{ + struct test *n, *next, *safe; + int i, s = 0, j = 0, k = 0; + + for (i = goal; i != 0; i = goal) { + s = 0; + + CK_SLIST_FOREACH(n, &head, list_entry) { + j++; + if (s == 0) + s = n->value; + else + s = s - 1; + + if (n->value != s) { + fprintf(stderr, "\nExpected %d, but got %d.\n", + s, n->value); + exit(EXIT_FAILURE); + } + + next = CK_SLIST_NEXT(n, list_entry); + if (next != NULL && next->value != s - 1) { + fprintf(stderr, "\nExpected %d, but got %d.\n", + s, next->value); + exit(EXIT_FAILURE); + } + + i--; + } + + if (i == 0) + break; + + s = 0; + CK_SLIST_FOREACH_SAFE(n, &head, list_entry, safe) { + k++; + + if (s == 0) + s = n->value; + else + s = s - 1; + + if (n->value != s) { + fprintf(stderr, "\nExpected %d, but got %d.\n", + s, n->value); + exit(EXIT_FAILURE); + } + + next = CK_SLIST_NEXT(n, list_entry); + if (next != NULL && next->value != s - 1) { + fprintf(stderr, "\nExpected %d, but got %d.\n", + s, next->value); + exit(EXIT_FAILURE); + } + + i--; + } + + if (i == 0 || CK_SLIST_EMPTY(&head) == true) + break; + } + + fprintf(stderr, "(%d, %d) ", j, k); + return; +} + +static void * +execute(void *c) +{ + + (void)c; + test_foreach(); + return NULL; +} + +int +main(int argc, char *argv[]) +{ + pthread_t *thread; + struct test *n; + int n_threads, i; + + if (argc != 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(EXIT_FAILURE); + } + + n_threads = atoi(argv[1]); + if (n_threads < 1) { + fprintf(stderr, "ERROR: Number of threads must be >= 1.\n"); + exit(EXIT_FAILURE); + } + + thread = malloc(sizeof(pthread_t) * n_threads); + assert(thread != NULL); + + goal = atoi(argv[2]); + if (goal < 4) { + fprintf(stderr, "ERROR: Number of entires must be >= 4.\n"); + exit(EXIT_FAILURE); + } + + fprintf(stderr, "Beginning serial test..."); + CK_SLIST_INIT(&head); + + for (i = 1; i <= goal; i++) { + n = malloc(sizeof *n); + assert(n != NULL); + n->value = i; + CK_SLIST_INSERT_HEAD(&head, n, list_entry); + } + + test_foreach(); + + for (i = 1; i <= goal; i++) { + n = CK_SLIST_FIRST(&head); + CK_SLIST_REMOVE_HEAD(&head, list_entry); + free(n); + } + + if (CK_SLIST_EMPTY(&head) == false) { + fprintf(stderr, "List is not empty after bulk removal.\n"); + exit(EXIT_FAILURE); + } + + fprintf(stderr, "done (success)\n"); + + fprintf(stderr, "Beginning parallel traversal..."); + + n = malloc(sizeof *n); + assert(n != NULL); + n->value = 1; + CK_SLIST_INSERT_HEAD(&head, n, list_entry); + + for (i = 0; i < n_threads; i++) { + int r = pthread_create(&thread[i], NULL, execute, NULL); + assert(r == 0); + } + + for (i = 2; i <= goal; i++) { + volatile int j; + + n = malloc(sizeof *n); + assert(n != NULL); + n->value = i; + CK_SLIST_INSERT_HEAD(&head, n, list_entry); + for (j = 0; j <= 1000; j++); + } + + for (i = 0; i < n_threads; i++) + pthread_join(thread[i], NULL); + + for (i = 0; i < n_threads; i++) { + int r = pthread_create(&thread[i], NULL, execute, NULL); + assert(r == 0); + } + + for (i = 1; i <= goal; i++) { + volatile int j; + + if (CK_SLIST_EMPTY(&head) == false) + CK_SLIST_REMOVE_HEAD(&head, list_entry); + + for (j = 0; j <= 1000; j++); + + if (CK_SLIST_EMPTY(&head) == false) { + struct test *r = CK_SLIST_FIRST(&head); + CK_SLIST_REMOVE(&head, r, test, list_entry); + } + } + + for (i = 0; i < n_threads; i++) + pthread_join(thread[i], NULL); + + fprintf(stderr, "done (success)\n"); + return (0); +} +