diff --git a/include/ck_barrier.h b/include/ck_barrier.h index ac4dc27..dd673db 100644 --- a/include/ck_barrier.h +++ b/include/ck_barrier.h @@ -1,5 +1,6 @@ /* * Copyright 2011 Samy Al Bahra. + * Copyright 2011 David Joseph. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,6 +30,10 @@ #include #include +#include + +#ifndef CK_BARRIER_CENTRALIZED +#define CK_BARRIER_CENTRALIZED struct ck_barrier_centralized { unsigned int value; @@ -65,4 +70,185 @@ ck_barrier_centralized(struct ck_barrier_centralized *barrier, return; } +#endif /* CK_BARRIER_CENTRALIZED */ + +#ifndef CK_BARRIER_COMBINING +#define CK_BARRIER_COMBINING + +struct ck_barrier_combining_entry { + unsigned int k; + unsigned int count; + unsigned int sense; + struct ck_barrier_combining_entry *parent; + struct ck_barrier_combining_entry *lchild; + struct ck_barrier_combining_entry *rchild; + struct ck_barrier_combining_entry *next; +}; + +typedef struct ck_barrier_combining_entry ck_barrier_combining_entry_t; + +struct ck_barrier_combining_state { + unsigned int sense; +}; + +typedef struct ck_barrier_combining_state ck_barrier_combining_state_t; + +#define CK_BARRIER_COMBINING_STATE_INITIALIZER {~0} + +struct ck_barrier_combining { + struct ck_barrier_combining_entry *root; + ck_spinlock_cas_t mutex; +}; + +typedef struct ck_barrier_combining ck_barrier_combining_t; + +struct ck_barrier_ct_queue { + struct ck_barrier_combining_entry *head; + struct ck_barrier_combining_entry *tail; +}; + +CK_CC_INLINE static void +ck_barrier_ct_queue_init(struct ck_barrier_ct_queue *queue) +{ + queue->head = queue->tail = NULL; + return; +} + +CK_CC_INLINE static void +ck_barrier_ct_queue_enqueue(struct ck_barrier_ct_queue *queue, + struct ck_barrier_combining_entry *node_value) +{ + node_value->next = NULL; + + if (queue->head == NULL) { + queue->head = queue->tail = node_value; + + return; + } + + queue->tail->next = node_value; + queue->tail = node_value; + + return; +} + +CK_CC_INLINE static struct ck_barrier_combining_entry * +ck_barrier_ct_queue_dequeue(struct ck_barrier_ct_queue *queue) +{ + struct ck_barrier_combining_entry *front = NULL; + + if (queue->head != NULL) { + front = queue->head; + queue->head = queue->head->next; + } + + return (front); +} + +CK_CC_INLINE static bool +ck_barrier_ct_queue_is_empty(struct ck_barrier_ct_queue *queue) +{ + return (queue->head == NULL); +} + +CK_CC_INLINE static void +ck_barrier_combining_init(struct ck_barrier_combining *root, + struct ck_barrier_combining_entry *init_root) +{ + init_root->k = 0; + init_root->count = 0; + init_root->sense = 0; + init_root->parent = init_root->lchild = init_root->rchild = NULL; + + ck_spinlock_cas_init(&root->mutex); + root->root = init_root; + + return; +} + +CK_CC_INLINE static bool +ck_barrier_combining_try_insert(struct ck_barrier_combining_entry *parent, + struct ck_barrier_combining_entry *tnode, + struct ck_barrier_combining_entry **child) +{ + if (*child == NULL) { + *child = tnode; + tnode->parent = parent; + parent->k++; + + return (true); + } + + return (false); +} + +CK_CC_INLINE static void +ck_barrier_combining_entry_init(struct ck_barrier_combining *root, + struct ck_barrier_combining_entry *tnode) +{ + struct ck_barrier_combining_entry *node; + struct ck_barrier_ct_queue queue; + + ck_barrier_ct_queue_init(&queue); + + tnode->k = 1; + tnode->count = 0; + tnode->sense = 0; + tnode->lchild = tnode->rchild = NULL; + + ck_spinlock_cas_lock(&root->mutex); + + ck_barrier_ct_queue_enqueue(&queue, root->root); + while (!ck_barrier_ct_queue_is_empty(&queue)) { + node = ck_barrier_ct_queue_dequeue(&queue); + if (ck_barrier_combining_try_insert(node, tnode, &node->lchild) == true) { + ck_spinlock_cas_unlock(&root->mutex); + return; + } + if (ck_barrier_combining_try_insert(node, tnode, &node->rchild) == true) { + ck_spinlock_cas_unlock(&root->mutex); + return; + } + ck_barrier_ct_queue_enqueue(&queue, node->lchild); + ck_barrier_ct_queue_enqueue(&queue, node->rchild); + } +} + +CK_CC_INLINE static void +ck_barrier_combining_aux(struct ck_barrier_combining *barrier, + struct ck_barrier_combining_entry *tnode, + unsigned int sense) +{ + /* Incrementing a leaf's count is unnecessary. */ + if (tnode->lchild == NULL) { + ck_barrier_combining_aux(barrier, tnode->parent, sense); + ck_pr_store_uint(&tnode->sense, ~tnode->sense); + return; + } + + if (ck_pr_faa_uint(&tnode->count, 1) == tnode->k - 1) { + if (tnode->parent != NULL) + ck_barrier_combining_aux(barrier, tnode->parent, sense); + ck_pr_store_uint(&tnode->count, 0); + ck_pr_store_uint(&tnode->sense, ~tnode->sense); + } else + while (sense != ck_pr_load_uint(&tnode->sense)) { + ck_pr_stall(); + } + + return; +} + +CK_CC_INLINE static void +ck_barrier_combining(struct ck_barrier_combining *barrier, + struct ck_barrier_combining_entry *tnode, + struct ck_barrier_combining_state *state) +{ + ck_barrier_combining_aux(barrier, tnode, state->sense); + state->sense = ~state->sense; +} + +#endif /* CK_BARRIER_COMBINING */ + #endif /* _CK_BARRIER_H */ + diff --git a/regressions/ck_barrier/validate/Makefile b/regressions/ck_barrier/validate/Makefile index 28cc979..c6f54de 100644 --- a/regressions/ck_barrier/validate/Makefile +++ b/regressions/ck_barrier/validate/Makefile @@ -1,12 +1,15 @@ .PHONY: clean distribution -OBJECTS=validate +OBJECTS=validate combining_validate all: $(OBJECTS) validate: validate.c ../../../include/ck_barrier.h $(CC) $(CFLAGS) -o validate validate.c -lpthread +combining_validate: combining_validate.c ../../../include/ck_barrier.h + $(CC) $(CFLAGS) -o combining_validate combining_validate.c -lpthread + clean: rm -rf *.dSYM *~ *.o $(OBJECTS) diff --git a/regressions/ck_barrier/validate/combining_validate.c b/regressions/ck_barrier/validate/combining_validate.c new file mode 100644 index 0000000..634f466 --- /dev/null +++ b/regressions/ck_barrier/validate/combining_validate.c @@ -0,0 +1,146 @@ +/* + * Copyright 2011 Samy Al Bahra. + * Copyright 2011 David Joseph. + * 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 +#include +#include +#include +#include + +#include +#include + +#include "../../common.h" + +#ifndef ITERATE +#define ITERATE 5000000 +#endif + +#ifndef ENTRIES +#define ENTRIES 512 +#endif + +static struct affinity a; +static int nthr; +static int counters[ENTRIES]; +static ck_barrier_combining_t barrier; +static int barrier_wait; + +static void * +thread(void *null CK_CC_UNUSED) +{ + ck_barrier_combining_entry_t *tnode; + ck_barrier_combining_state_t state = CK_BARRIER_COMBINING_STATE_INITIALIZER; + int j; + int i = 0; + int counter; + + tnode = malloc(sizeof(ck_barrier_combining_entry_t)); + if (tnode == NULL) { + fprintf(stderr, "Could not allocate thread barrier entry\n"); + exit(EXIT_FAILURE); + } + + ck_barrier_combining_entry_init(&barrier, tnode); + + aff_iterate(&a); + + ck_pr_inc_int(&barrier_wait); + while (ck_pr_load_int(&barrier_wait) != nthr) + ck_pr_stall(); + + for (j = 0; j < ITERATE; j++) { + i = j++ & (ENTRIES - 1); + ck_pr_inc_int(&counters[i]); + ck_barrier_combining(&barrier, tnode, &state); + counter = ck_pr_load_int(&counters[i]); + if (counter != nthr * (j / ENTRIES + 1)) { + fprintf(stderr, "FAILED [%d:%d]: %d != %d\n", i, j - 1, counter, nthr); + exit(EXIT_FAILURE); + } + } + + free(tnode); + return (NULL); +} + +int +main(int argc, char *argv[]) +{ + pthread_t *threads; + int i; + ck_barrier_combining_entry_t *init_root; + + init_root = malloc(sizeof(ck_barrier_combining_entry_t)); + if (init_root == NULL) { + fprintf(stderr, "ERROR: Could not allocate initial barrier structure\n"); + exit(EXIT_FAILURE); + } + ck_barrier_combining_init(&barrier, init_root); + + if (argc != 3) { + fprintf(stderr, "Usage: correct \n"); + exit(EXIT_FAILURE); + } + + nthr = atoi(argv[1]); + if (nthr <= 0) { + fprintf(stderr, "ERROR: Number of threads must be greater than 0\n"); + exit(EXIT_FAILURE); + } + + threads = malloc(sizeof(pthread_t) * nthr); + if (threads == NULL) { + fprintf(stderr, "ERROR: Could not allocate thread structures\n"); + exit(EXIT_FAILURE); + } + + a.delta = atoi(argv[2]); + + fprintf(stderr, "Creating threads (barrier)..."); + for (i = 0; i < nthr; i++) { + if (pthread_create(&threads[i], NULL, thread, NULL)) { + fprintf(stderr, "ERROR: Could not create thread %d\n", i); + exit(EXIT_FAILURE); + } + } + fprintf(stderr, "done\n"); + + fprintf(stderr, "Waiting for threads to finish correctness regression..."); + for (i = 0; i < nthr; i++) + pthread_join(threads[i], NULL); + fprintf(stderr, "done (passed)\n"); + + free(init_root); + return (0); +} +