diff --git a/include/ck_barrier.h b/include/ck_barrier.h index 74937da..fec90eb 100644 --- a/include/ck_barrier.h +++ b/include/ck_barrier.h @@ -49,13 +49,13 @@ void ck_barrier_centralized(ck_barrier_centralized_t *, unsigned int); struct ck_barrier_combining_group { - struct ck_barrier_combining_group *parent; - struct ck_barrier_combining_group *lchild; - struct ck_barrier_combining_group *rchild; - struct ck_barrier_combining_group *next; unsigned int k; unsigned int count; unsigned int sense; + struct ck_barrier_combining_group *parent; + struct ck_barrier_combining_group *left; + struct ck_barrier_combining_group *right; + struct ck_barrier_combining_group *next; } CK_CC_CACHELINE; typedef struct ck_barrier_combining_group ck_barrier_combining_group_t; @@ -82,14 +82,17 @@ void ck_barrier_combining(ck_barrier_combining_t *, ck_barrier_combining_group_t *, ck_barrier_combining_state_t *); -struct ck_barrier_dissemination_internal { +struct ck_barrier_dissemination_flag { unsigned int tflag; unsigned int *pflag; }; -typedef struct ck_barrier_dissemination_internal ck_barrier_dissemination_internal_t; +typedef struct ck_barrier_dissemination_flag ck_barrier_dissemination_flag_t; struct ck_barrier_dissemination { - struct ck_barrier_dissemination_internal *flags[2]; + unsigned int nthr; + unsigned int size; + unsigned int tid; + struct ck_barrier_dissemination_flag *flags[2]; }; typedef struct ck_barrier_dissemination ck_barrier_dissemination_t; struct ck_barrier_dissemination_state { @@ -100,10 +103,11 @@ struct ck_barrier_dissemination_state { typedef struct ck_barrier_dissemination_state ck_barrier_dissemination_state_t; void ck_barrier_dissemination_init(ck_barrier_dissemination_t *, - ck_barrier_dissemination_internal_t **, + ck_barrier_dissemination_flag_t **, unsigned int); -void ck_barrier_dissemination_state_init(ck_barrier_dissemination_state_t *); +void ck_barrier_dissemination_subscribe(ck_barrier_dissemination_t *, + ck_barrier_dissemination_state_t *); unsigned int ck_barrier_dissemination_size(unsigned int); @@ -123,20 +127,13 @@ struct ck_barrier_tournament_state { }; typedef struct ck_barrier_tournament_state ck_barrier_tournament_state_t; -void -ck_barrier_tournament_state_init(ck_barrier_tournament_state_t *); - -void -ck_barrier_tournament_round_init(ck_barrier_tournament_round_t **, - unsigned int); - +void ck_barrier_tournament_state_init(ck_barrier_tournament_state_t *); +void ck_barrier_tournament_round_init(ck_barrier_tournament_round_t **, unsigned int); unsigned int ck_barrier_tournament_size(unsigned int); - -void -ck_barrier_tournament(ck_barrier_tournament_round_t **, - ck_barrier_tournament_state_t *); +void ck_barrier_tournament(ck_barrier_tournament_round_t **, ck_barrier_tournament_state_t *); struct ck_barrier_mcs { + unsigned int tid; unsigned int *children[2]; unsigned int childnotready[4]; unsigned int dummy; @@ -152,16 +149,9 @@ struct ck_barrier_mcs_state { }; typedef struct ck_barrier_mcs_state ck_barrier_mcs_state_t; -void -ck_barrier_mcs_init(ck_barrier_mcs_t *, - unsigned int); - -void -ck_barrier_mcs_state_init(ck_barrier_mcs_state_t *); - -void -ck_barrier_mcs(ck_barrier_mcs_t *, - ck_barrier_mcs_state_t *); +void ck_barrier_mcs_init(ck_barrier_mcs_t *, unsigned int); +void ck_barrier_mcs_subscribe(ck_barrier_mcs_t *, ck_barrier_mcs_state_t *); +void ck_barrier_mcs(ck_barrier_mcs_t *, ck_barrier_mcs_state_t *); #endif /* _CK_BARRIER_H */ diff --git a/regressions/ck_barrier/validate/Makefile b/regressions/ck_barrier/validate/Makefile index 7a49792..e0611da 100644 --- a/regressions/ck_barrier/validate/Makefile +++ b/regressions/ck_barrier/validate/Makefile @@ -4,20 +4,20 @@ OBJECTS=ck_barrier_centralized ck_barrier_combining ck_barrier_dissemination ck_ all: $(OBJECTS) -ck_barrier_centralized: ck_barrier_centralized.c ../../../include/ck_barrier.h ../../../src/ck_barrier.c - $(CC) $(CFLAGS) -o ck_barrier_centralized ck_barrier_centralized.c ../../../src/ck_barrier.c +ck_barrier_centralized: ck_barrier_centralized.c ../../../include/ck_barrier.h ../../../src/ck_barrier_centralized.c + $(CC) $(CFLAGS) -o ck_barrier_centralized ck_barrier_centralized.c ../../../src/ck_barrier_centralized.c -ck_barrier_combining: ck_barrier_combining.c ../../../include/ck_barrier.h ../../../src/ck_barrier.c - $(CC) $(CFLAGS) -o ck_barrier_combining ck_barrier_combining.c ../../../src/ck_barrier.c +ck_barrier_combining: ck_barrier_combining.c ../../../include/ck_barrier.h ../../../src/ck_barrier_combining.c + $(CC) $(CFLAGS) -o ck_barrier_combining ck_barrier_combining.c ../../../src/ck_barrier_combining.c -ck_barrier_dissemination: ck_barrier_dissemination.c ../../../include/ck_barrier.h ../../../src/ck_barrier.c - $(CC) $(CFLAGS) -o ck_barrier_dissemination ck_barrier_dissemination.c ../../../src/ck_barrier.c +ck_barrier_dissemination: ck_barrier_dissemination.c ../../../include/ck_barrier.h ../../../src/ck_barrier_dissemination.c + $(CC) $(CFLAGS) -o ck_barrier_dissemination ck_barrier_dissemination.c ../../../src/ck_barrier_dissemination.c -ck_barrier_tournament: ck_barrier_tournament.c ../../../include/ck_barrier.h ../../../src/ck_barrier.c - $(CC) $(CFLAGS) -o ck_barrier_tournament ck_barrier_tournament.c ../../../src/ck_barrier.c +ck_barrier_tournament: ck_barrier_tournament.c ../../../include/ck_barrier.h ../../../src/ck_barrier_tournament.c + $(CC) $(CFLAGS) -o ck_barrier_tournament ck_barrier_tournament.c ../../../src/ck_barrier_tournament.c -ck_barrier_mcs: ck_barrier_mcs.c ../../../include/ck_barrier.h ../../../src/ck_barrier.c - $(CC) $(CFLAGS) -o ck_barrier_mcs ck_barrier_mcs.c ../../../src/ck_barrier.c +ck_barrier_mcs: ck_barrier_mcs.c ../../../include/ck_barrier.h ../../../src/ck_barrier_mcs.c + $(CC) $(CFLAGS) -o ck_barrier_mcs ck_barrier_mcs.c ../../../src/ck_barrier_mcs.c clean: rm -rf *.dSYM *~ *.o $(OBJECTS) diff --git a/regressions/ck_barrier/validate/ck_barrier_dissemination.c b/regressions/ck_barrier/validate/ck_barrier_dissemination.c index 40bd908..8b37c0d 100644 --- a/regressions/ck_barrier/validate/ck_barrier_dissemination.c +++ b/regressions/ck_barrier/validate/ck_barrier_dissemination.c @@ -55,14 +55,15 @@ static int counters[ENTRIES]; static int barrier_wait; static void * -thread(void *barrier) +thread(void *b) { + ck_barrier_dissemination_t *barrier = b; ck_barrier_dissemination_state_t state; int j, k, counter; int i = 0; aff_iterate(&a); - ck_barrier_dissemination_state_init(&state); + ck_barrier_dissemination_subscribe(barrier, &state); ck_pr_inc_int(&barrier_wait); while (ck_pr_load_int(&barrier_wait) != nthr) @@ -86,7 +87,7 @@ int main(int argc, char *argv[]) { ck_barrier_dissemination_t *barrier; - ck_barrier_dissemination_internal_t **barrier_internal; + ck_barrier_dissemination_flag_t **barrier_internal; pthread_t *threads; int i, size; @@ -115,7 +116,7 @@ main(int argc, char *argv[]) exit(EXIT_FAILURE); } - barrier_internal = malloc(sizeof(ck_barrier_dissemination_internal_t *) * nthr); + barrier_internal = malloc(sizeof(ck_barrier_dissemination_flag_t *) * nthr); if (barrier_internal == NULL) { fprintf(stderr, "ERROR: Could not allocate barrier structures\n"); exit(EXIT_FAILURE); @@ -123,7 +124,7 @@ main(int argc, char *argv[]) size = ck_barrier_dissemination_size(nthr); for (i = 0; i < nthr; ++i) { - barrier_internal[i] = malloc(sizeof(ck_barrier_dissemination_internal_t) * size); + barrier_internal[i] = malloc(sizeof(ck_barrier_dissemination_flag_t) * size); if (barrier_internal[i] == NULL) { fprintf(stderr, "ERROR: Could not allocate barrier structures\n"); exit(EXIT_FAILURE); diff --git a/regressions/ck_barrier/validate/ck_barrier_mcs.c b/regressions/ck_barrier/validate/ck_barrier_mcs.c index 980de56..88a13ce 100644 --- a/regressions/ck_barrier/validate/ck_barrier_mcs.c +++ b/regressions/ck_barrier/validate/ck_barrier_mcs.c @@ -55,15 +55,16 @@ static int counters[ENTRIES]; static int barrier_wait; static void * -thread(void *barrier) +thread(void *b) { + ck_barrier_mcs_t *barrier = b; ck_barrier_mcs_state_t state; int j, counter; int i = 0; aff_iterate(&a); - ck_barrier_mcs_state_init(&state); + ck_barrier_mcs_subscribe(barrier, &state); ck_pr_inc_int(&barrier_wait); while (ck_pr_load_int(&barrier_wait) != nthr) diff --git a/src/Makefile b/src/Makefile index f8f8509..8836ca0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,11 @@ .PHONY: clean distribution -OBJECTS=ck_hp.o ck_barrier.o +OBJECTS=ck_hp.o \ + ck_barrier_centralized.o \ + ck_barrier_combining.o \ + ck_barrier_dissemination.o \ + ck_barrier_tournament.o \ + ck_barrier_mcs.o all: libck.so libck.a @@ -13,8 +18,20 @@ libck.a: $(OBJECTS) ck_hp.o: ck_hp.c $(CC) $(CFLAGS) -c -o ck_hp.o ck_hp.c -ck_barrier.o: ck_barrier.c - $(CC) $(CFLAGS) -c -o ck_barrier.o ck_barrier.c +ck_barrier_centralized.o: ck_barrier_centralized.c + $(CC) $(CFLAGS) -c -o ck_barrier_centralized.o ck_barrier_centralized.c + +ck_barrier_combining.o: ck_barrier_combining.c + $(CC) $(CFLAGS) -c -o ck_barrier_combining.o ck_barrier_combining.c + +ck_barrier_dissemination.o: ck_barrier_dissemination.c + $(CC) $(CFLAGS) -c -o ck_barrier_dissemination.o ck_barrier_dissemination.c + +ck_barrier_tournament.o: ck_barrier_tournament.c + $(CC) $(CFLAGS) -c -o ck_barrier_tournament.o ck_barrier_tournament.c + +ck_barrier_mcs.o: ck_barrier_mcs.c + $(CC) $(CFLAGS) -c -o ck_barrier_mcs.o ck_barrier_mcs.c clean: rm -rf *.dSYM *~ *.o $(OBJECTS) libck.a libck.so diff --git a/src/ck_barrier.c b/src/ck_barrier.c deleted file mode 100644 index 9550b0f..0000000 --- a/src/ck_barrier.c +++ /dev/null @@ -1,605 +0,0 @@ -/* - * 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 - -struct ck_barrier_combining_queue { - struct ck_barrier_combining_group *head; - struct ck_barrier_combining_group *tail; -}; - -/* - * Log and power_2 algorithms from: http://graphics.stanford.edu/~seander/bithacks.html - */ -CK_CC_INLINE static unsigned int -ck_barrier_internal_log(unsigned int v) -{ - static const unsigned int b[] = {0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, - 0xFF00FF00, 0xFFFF0000}; - - register unsigned int r = (v & b[0]) != 0; - int i; - - for (i = 4; i > 0; i--) { - r |= ((v & b[i]) != 0) << i; - } - - return (r); -} - -CK_CC_INLINE static unsigned int -ck_barrier_internal_power_2(unsigned int v) -{ - - --v; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - ++v; - - return (v); -} - -void -ck_barrier_centralized(struct ck_barrier_centralized *barrier, - struct ck_barrier_centralized_state *state, - unsigned int n_threads) -{ - unsigned int sense, value; - - sense = state->sense = ~state->sense; - value = ck_pr_faa_uint(&barrier->value, 1); - if (value == n_threads - 1) { - ck_pr_store_uint(&barrier->value, 0); - ck_pr_store_uint(&barrier->sense, sense); - return; - } - - while (sense != ck_pr_load_uint(&barrier->sense)) - ck_pr_stall(); - - return; -} - -/* - * This implementation of software combining tree barriers - * uses level order traversal to insert new thread groups - * into the barrier's tree. We use a queue to implement this - * traversal. - */ -CK_CC_INLINE static void -ck_barrier_combining_queue_enqueue(struct ck_barrier_combining_queue *queue, - struct ck_barrier_combining_group *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_group * -ck_barrier_combining_queue_dequeue(struct ck_barrier_combining_queue *queue) -{ - struct ck_barrier_combining_group *front = NULL; - - if (queue->head != NULL) { - front = queue->head; - queue->head = queue->head->next; - } - - return (front); -} - -CK_CC_INLINE static void -ck_barrier_combining_insert(struct ck_barrier_combining_group *parent, - struct ck_barrier_combining_group *tnode, - struct ck_barrier_combining_group **child) -{ - - *child = tnode; - tnode->parent = parent; - /* - * After inserting, we must increment the parent group's count for - * number of threads expected to reach it; otherwise, the - * barrier may end prematurely. - */ - ++parent->k; - - return; -} - -/* - * This tries to insert a new thread group as a child of the given - * parent group. - */ -CK_CC_INLINE static bool -ck_barrier_combining_try_insert(struct ck_barrier_combining_group *parent, - struct ck_barrier_combining_group *tnode) -{ - - if (parent->lchild == NULL) { - ck_barrier_combining_insert(parent, tnode, &parent->lchild); - - return (true); - } - - if (parent->rchild == NULL) { - ck_barrier_combining_insert(parent, tnode, &parent->rchild); - - return (true); - } - - return (false); -} - -void -ck_barrier_combining_group_init(struct ck_barrier_combining *root, - struct ck_barrier_combining_group *tnode, - unsigned int nthr) -{ - struct ck_barrier_combining_group *node; - struct ck_barrier_combining_queue queue; - - queue.head = queue.tail = NULL; - - tnode->k = nthr; - tnode->count = 0; - tnode->sense = 0; - tnode->lchild = tnode->rchild = NULL; - - /* - * The lock here simplifies insertion logic (no CAS required in this case). - * It prevents concurrent threads from overwriting insertions. - */ - ck_spinlock_fas_lock(&root->mutex); - ck_barrier_combining_queue_enqueue(&queue, root->root); - while (queue.head != NULL) { - node = ck_barrier_combining_queue_dequeue(&queue); - - /* Attempt to insert the new group as a child of the current node. */ - if (ck_barrier_combining_try_insert(node, tnode) == true) - goto leave; - - /* - * If unsuccessful, try inserting as a child of the children of the - * current node. - */ - ck_barrier_combining_queue_enqueue(&queue, node->lchild); - ck_barrier_combining_queue_enqueue(&queue, node->rchild); - } - -leave: - ck_spinlock_fas_unlock(&root->mutex); - return; -} - -void -ck_barrier_combining_init(struct ck_barrier_combining *root, - struct ck_barrier_combining_group *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_fas_init(&root->mutex); - root->root = init_root; - return; -} - -static void -ck_barrier_combining_aux(struct ck_barrier_combining *barrier, - struct ck_barrier_combining_group *tnode, - unsigned int sense) -{ - - /* - * If this is the last thread in the group, - * it moves on to the parent group. Otherwise, it - * spins on this group's sense. - */ - if (ck_pr_faa_uint(&tnode->count, 1) == tnode->k - 1) { - if (tnode->parent != NULL) - ck_barrier_combining_aux(barrier, tnode->parent, sense); - - /* - * Once the thread returns from its parent(s), it reinitializes - * the group's arrival count and frees the threads waiting at - * this group. - */ - 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; -} - -void -ck_barrier_combining(struct ck_barrier_combining *barrier, - struct ck_barrier_combining_group *tnode, - struct ck_barrier_combining_state *state) -{ - - ck_barrier_combining_aux(barrier, tnode, state->sense); - - /* Set the thread's private sense for the next barrier. */ - state->sense = ~state->sense; - return; -} - -static unsigned int ck_barrier_dissemination_nthr; -static unsigned int ck_barrier_dissemination_tid; - -void -ck_barrier_dissemination_init(struct ck_barrier_dissemination *barrier, - struct ck_barrier_dissemination_internal **barrier_internal, - unsigned int nthr) -{ - unsigned int i, j, k, size, offset; - - ck_barrier_dissemination_nthr = nthr; - size = (ck_barrier_internal_log(ck_barrier_internal_power_2(nthr))); - - for (i = 0; i < nthr; ++i) { - barrier[i].flags[0] = barrier_internal[i]; - barrier[i].flags[1] = barrier_internal[i] + size; - } - - for (i = 0; i < nthr; ++i) { - for (k = 0, offset = 1; k < size; ++k, offset <<= 1) { - /* - * Determine the thread's partner, j, for the current round, k. - * Partners are chosen such that by the completion of the barrier, - * every thread has been directly (having one of its flag set) or - * indirectly (having one of its partners's flags set) signaled - * by every other thread in the barrier. - */ - if ((nthr & (nthr - 1)) == 0) - j = (i + offset) & (nthr - 1); - else - j = (i + offset) % nthr; - - /* Set the thread's partner for round k. */ - barrier[i].flags[0][k].pflag = &barrier[j].flags[0][k].tflag; - barrier[i].flags[1][k].pflag = &barrier[j].flags[1][k].tflag; - - /* Set the thread's flags to false. */ - barrier[i].flags[0][k].tflag = barrier[i].flags[1][k].tflag = 0; - } - } - - return; -} - -void -ck_barrier_dissemination_state_init(struct ck_barrier_dissemination_state *state) -{ - - state->parity = 0; - state->sense = ~0; - state->tid = ck_pr_faa_uint(&ck_barrier_dissemination_tid, 1); - return; -} - -unsigned int -ck_barrier_dissemination_size(unsigned int nthr) -{ - - return (ck_barrier_internal_log(ck_barrier_internal_power_2(nthr)) * 2); -} - -void -ck_barrier_dissemination(struct ck_barrier_dissemination *barrier, - struct ck_barrier_dissemination_state *state) -{ - unsigned int i, size; - - size = (ck_barrier_internal_log(ck_barrier_internal_power_2(ck_barrier_dissemination_nthr))); - for (i = 0; i < size; ++i) { - /* Unblock current partner. */ - ck_pr_store_uint(barrier[state->tid].flags[state->parity][i].pflag, state->sense); - - /* Wait until some other thread unblocks this one. */ - while (ck_pr_load_uint(&barrier[state->tid].flags[state->parity][i].tflag) != state->sense) - ck_pr_stall(); - } - - /* - * Dissemination barriers use two sets of flags to prevent race conditions - * between successive calls to the barrier. Parity indicates which set will - * be used for the next barrier. They also use a sense reversal technique - * to avoid re-initialization of the flags for every two calls to the barrier. - */ - if (state->parity == 1) - state->sense = ~state->sense; - state->parity = 1 - state->parity; - - return; -} - -/* - * This is a tournament barrier implementation. Threads are statically - * assigned roles to perform for each round of the barrier. Winners - * move on to the next round, while losers spin in their current rounds - * on their own flags. During the last round, the champion of the tournament - * sets the last flag that begins the wakeup process. - */ -static unsigned int ck_barrier_tournament_tid; - -void -ck_barrier_tournament_state_init(ck_barrier_tournament_state_t *state) -{ - - state->sense = ~0; - state->vpid = ck_pr_faa_uint(&ck_barrier_tournament_tid, 1); - return; -} - -void -ck_barrier_tournament_round_init(struct ck_barrier_tournament_round **rounds, - unsigned int nthr) -{ - unsigned int i, k, size, twok, twokm1, imod2k; - - size = ck_barrier_tournament_size(nthr); - for (i = 0; i < nthr; ++i) { - /* The first role is always DROPOUT. */ - rounds[i][0].flag = 0; - rounds[i][0].role = DROPOUT; - for (k = 1, twok = 2, twokm1 = 1; k < size; ++k, twokm1 = twok, twok <<= 1) { - rounds[i][k].flag = 0; - - imod2k = i & (twok - 1); - if (imod2k == 0) { - if ((i + twokm1 < nthr) && (twok < nthr)) - rounds[i][k].role = WINNER; - else if (i + twokm1 >= nthr) - rounds[i][k].role = BYE; - } - if (imod2k == twokm1) - rounds[i][k].role = LOSER; - - /* There is exactly one cHAMPION in a tournament barrier. */ - else if ((i == 0) && (twok >= nthr)) - rounds[i][k].role = CHAMPION; - - if (rounds[i][k].role == LOSER) - rounds[i][k].opponent = &rounds[i - twokm1][k].flag; - else if (rounds[i][k].role == WINNER || rounds[i][k].role == CHAMPION) - rounds[i][k].opponent = &rounds[i + twokm1][k].flag; - } - } - - return; -} - -unsigned int -ck_barrier_tournament_size(unsigned int nthr) -{ - - return (ck_barrier_internal_log(ck_barrier_internal_power_2(nthr)) + 1); -} - -void -ck_barrier_tournament(struct ck_barrier_tournament_round **rounds, - struct ck_barrier_tournament_state *state) -{ - int round = 1; - - for (;; ++round) { - switch (rounds[state->vpid][round].role) { // MIGHT NEED TO USE CK_PR_LOAD*** - case BYE: - break; - case CHAMPION: - /* - * The CHAMPION waits until it wins the tournament; it then - * sets the final flag before the wakeup phase of the barrier. - */ - while (ck_pr_load_uint(&rounds[state->vpid][round].flag) != state->sense) - ck_pr_stall(); - ck_pr_store_uint(rounds[state->vpid][round].opponent, state->sense); - goto wakeup; - break; - case DROPOUT: - /* NOTREACHED */ - break; - case LOSER: - /* - * LOSERs set the flags of their opponents and wait until - * their opponents release them after the tournament is over. - */ - ck_pr_store_uint(rounds[state->vpid][round].opponent, state->sense); - while (ck_pr_load_uint(&rounds[state->vpid][round].flag) != state->sense) - ck_pr_stall(); - goto wakeup; - break; - case WINNER: - /* - * WINNERs wait until their current opponent sets their flag; they then - * continue to the next round of the tournament. - */ - while (ck_pr_load_uint(&rounds[state->vpid][round].flag) != state->sense) - ck_pr_stall(); - break; - } - } -wakeup: - for (round -= 1;; --round) { - switch (rounds[state->vpid][round].role) { // MIGHT NEED TO USE CK_PR_LOAD*** - case BYE: - break; - case CHAMPION: - /* NOTREACHED */ - break; - case DROPOUT: - goto leave; - break; - case LOSER: - /* NOTREACHED */ - break; - case WINNER: - /* - * Winners inform their old opponents the tournament is over - * by setting their flags. - */ - ck_pr_store_uint(rounds[state->vpid][round].opponent, state->sense); - break; - } - } - -leave: - state->sense = ~state->sense; - return; -} - -static unsigned int ck_barrier_mcs_tid; - -void -ck_barrier_mcs_init(struct ck_barrier_mcs *barrier, - unsigned int nthr) -{ - unsigned int i, j; - - for (i = 0; i < nthr; ++i) { - for (j = 0; j < 4; ++j) { - /* - If there are still threads that don't have parents, - * add it as a child. - */ - barrier[i].havechild[j] = ((i << 2) + j < nthr - 1) ? - ~0 : - 0; - - /* - * Childnotready is initialized to havechild to ensure - * a thread does not wait for a child that does not exist. - */ - barrier[i].childnotready[j] = barrier[i].havechild[j]; - } - - /* The root thread does not have a parent. */ - barrier[i].parent = (i == 0) ? - &barrier[i].dummy : - &barrier[(i - 1) >> 2].childnotready[(i - 1) & 3]; - - /* Leaf threads do not have any children. */ - barrier[i].children[0] = ((i << 1) + 1 >= nthr) ? - &barrier[i].dummy : - &barrier[(i << 1) + 1].parentsense; - - barrier[i].children[1] = ((i << 1) + 2 >= nthr) ? - &barrier[i].dummy : - &barrier[(i << 1) + 2].parentsense; - - barrier[i].parentsense = 0; - } - - return; -} - -void -ck_barrier_mcs_state_init(struct ck_barrier_mcs_state *state) -{ - - state->sense = ~0; - state->vpid = ck_pr_faa_uint(&ck_barrier_mcs_tid, 1); - return; -} - -CK_CC_INLINE static bool -ck_barrier_mcs_check_children(unsigned int *childnotready) -{ - int i; - - for (i = 0; i < 4; ++i) { - if (ck_pr_load_uint(&childnotready[i]) != 0) - return (false); - } - - return (true); -} - -CK_CC_INLINE static void -ck_barrier_mcs_reinitialize_children(struct ck_barrier_mcs *node) -{ - int i; - - for (i = 0; i < 4; ++i) - ck_pr_store_uint(&node->childnotready[i], node->havechild[i]); - - return; -} - -void -ck_barrier_mcs(struct ck_barrier_mcs *barrier, - struct ck_barrier_mcs_state *state) -{ - - /* - * Wait until all children have reached the barrier and are done waiting - * for their children. - */ - while (ck_barrier_mcs_check_children(barrier[state->vpid].childnotready) == false) - ck_pr_stall(); - - /* Reinitialize for next barrier. */ - ck_barrier_mcs_reinitialize_children(&barrier[state->vpid]); - - /* Inform parent thread and its children have arrived at the barrier. */ - ck_pr_store_uint(barrier[state->vpid].parent, 0); - - /* Wait until parent indicates all threads have arrived at the barrier. */ - if (state->vpid != 0) { - while (ck_pr_load_uint(&barrier[state->vpid].parentsense) != state->sense) - ck_pr_stall(); - } - - /* Inform children of successful barrier. */ - ck_pr_store_uint(barrier[state->vpid].children[0], state->sense); - ck_pr_store_uint(barrier[state->vpid].children[1], state->sense); - - state->sense = ~state->sense; - - return; -} - diff --git a/src/ck_barrier_centralized.c b/src/ck_barrier_centralized.c new file mode 100644 index 0000000..c380b36 --- /dev/null +++ b/src/ck_barrier_centralized.c @@ -0,0 +1,56 @@ +/* + * 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 + +void +ck_barrier_centralized(struct ck_barrier_centralized *barrier, + struct ck_barrier_centralized_state *state, + unsigned int n_threads) +{ + unsigned int sense, value; + + /* + * Every execution context has a sense associated with it. + * This sense is reversed when the barrier is entered. Every + * thread will spin on the global sense until the last thread + * reverses it. + */ + sense = state->sense = ~state->sense; + value = ck_pr_faa_uint(&barrier->value, 1); + if (value == n_threads - 1) { + ck_pr_store_uint(&barrier->value, 0); + ck_pr_store_uint(&barrier->sense, sense); + return; + } + + while (sense != ck_pr_load_uint(&barrier->sense)) + ck_pr_stall(); + + return; +} diff --git a/src/ck_barrier_combining.c b/src/ck_barrier_combining.c new file mode 100644 index 0000000..a86fca8 --- /dev/null +++ b/src/ck_barrier_combining.c @@ -0,0 +1,206 @@ +/* + * 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 + +struct ck_barrier_combining_queue { + struct ck_barrier_combining_group *head; + struct ck_barrier_combining_group *tail; +}; + +CK_CC_INLINE static struct ck_barrier_combining_group * +ck_barrier_combining_queue_dequeue(struct ck_barrier_combining_queue *queue) +{ + struct ck_barrier_combining_group *front = NULL; + + if (queue->head != NULL) { + front = queue->head; + queue->head = queue->head->next; + } + + return (front); +} + +CK_CC_INLINE static void +ck_barrier_combining_insert(struct ck_barrier_combining_group *parent, + struct ck_barrier_combining_group *tnode, + struct ck_barrier_combining_group **child) +{ + + *child = tnode; + tnode->parent = parent; + + /* + * After inserting, we must increment the parent group's count for + * number of threads expected to reach it; otherwise, the + * barrier may end prematurely. + */ + parent->k++; + return; +} + +/* + * This implementation of software combining tree barriers + * uses level order traversal to insert new thread groups + * into the barrier's tree. We use a queue to implement this + * traversal. + */ +CK_CC_INLINE static void +ck_barrier_combining_queue_enqueue(struct ck_barrier_combining_queue *queue, + struct ck_barrier_combining_group *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; +} + + +void +ck_barrier_combining_group_init(struct ck_barrier_combining *root, + struct ck_barrier_combining_group *tnode, + unsigned int nthr) +{ + struct ck_barrier_combining_group *node; + struct ck_barrier_combining_queue queue; + + queue.head = queue.tail = NULL; + + tnode->k = nthr; + tnode->count = 0; + tnode->sense = 0; + tnode->left = tnode->right = NULL; + + /* + * Finds the first available node for linkage into the combining + * tree. The use of a spinlock is excusable as this is a one-time + * initialization cost. + */ + ck_spinlock_fas_lock(&root->mutex); + ck_barrier_combining_queue_enqueue(&queue, root->root); + while (queue.head != NULL) { + node = ck_barrier_combining_queue_dequeue(&queue); + + /* If the left child is free, link the group there. */ + if (node->left == NULL) { + ck_barrier_combining_insert(node, tnode, &node->left); + goto leave; + } + + /* If the right child is free, link the group there. */ + if (node->right == NULL) { + ck_barrier_combining_insert(node, tnode, &node->right); + goto leave; + } + + /* + * If unsuccessful, try inserting as a child of the children of the + * current node. + */ + ck_barrier_combining_queue_enqueue(&queue, node->left); + ck_barrier_combining_queue_enqueue(&queue, node->right); + } + +leave: + ck_spinlock_fas_unlock(&root->mutex); + return; +} + +void +ck_barrier_combining_init(struct ck_barrier_combining *root, + struct ck_barrier_combining_group *init_root) +{ + + init_root->k = 0; + init_root->count = 0; + init_root->sense = 0; + init_root->parent = init_root->left = init_root->right = NULL; + ck_spinlock_fas_init(&root->mutex); + root->root = init_root; + return; +} + +static void +ck_barrier_combining_aux(struct ck_barrier_combining *barrier, + struct ck_barrier_combining_group *tnode, + unsigned int sense) +{ + + /* + * If this is the last thread in the group, it moves on to the parent group. + * Otherwise, it spins on this group's sense. + */ + if (ck_pr_faa_uint(&tnode->count, 1) == tnode->k - 1) { + /* + * If we are and will be the last thread entering the barrier for the + * current group then signal the parent group if one exists. + */ + if (tnode->parent != NULL) + ck_barrier_combining_aux(barrier, tnode->parent, sense); + + /* + * Once the thread returns from its parent(s), it reinitializes the group's + * arrival count and signals other threads to continue by flipping the group + * sense. Order of these operations is not important since we assume a static + * number of threads are members of a barrier for the lifetime of the barrier. + * Since count is explicitly reinitialized, it is guaranteed that at any point + * tnode->count is equivalent to tnode->k if and only if that many threads + * are at the barrier. + */ + 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; +} + +void +ck_barrier_combining(struct ck_barrier_combining *barrier, + struct ck_barrier_combining_group *tnode, + struct ck_barrier_combining_state *state) +{ + + ck_barrier_combining_aux(barrier, tnode, state->sense); + + /* Reverse the execution context's sense for the next barrier. */ + state->sense = ~state->sense; + return; +} + diff --git a/src/ck_barrier_dissemination.c b/src/ck_barrier_dissemination.c new file mode 100644 index 0000000..7d54386 --- /dev/null +++ b/src/ck_barrier_dissemination.c @@ -0,0 +1,124 @@ +/* + * 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 "ck_internal.h" + +void +ck_barrier_dissemination_init(struct ck_barrier_dissemination *barrier, + struct ck_barrier_dissemination_flag **barrier_internal, + unsigned int nthr) +{ + unsigned int i, j, k, size, offset; + bool p = nthr & (nthr - 1); + + barrier->nthr = nthr; + barrier->size = size = ck_internal_log(ck_internal_power_2(nthr)); + ck_pr_store_uint(&barrier->tid, 0); + + for (i = 0; i < nthr; ++i) { + barrier[i].flags[0] = barrier_internal[i]; + barrier[i].flags[1] = barrier_internal[i] + size; + } + + for (i = 0; i < nthr; ++i) { + for (k = 0, offset = 1; k < size; ++k, offset <<= 1) { + /* + * Determine the thread's partner, j, for the current round, k. + * Partners are chosen such that by the completion of the barrier, + * every thread has been directly (having one of its flag set) or + * indirectly (having one of its partners's flags set) signaled + * by every other thread in the barrier. + */ + if (p == false) + j = (i + offset) & (nthr - 1); + else + j = (i + offset) % nthr; + + /* Set the thread's partner for round k. */ + barrier[i].flags[0][k].pflag = &barrier[j].flags[0][k].tflag; + barrier[i].flags[1][k].pflag = &barrier[j].flags[1][k].tflag; + + /* Set the thread's flags to false. */ + barrier[i].flags[0][k].tflag = barrier[i].flags[1][k].tflag = 0; + } + } + + return; +} + +void +ck_barrier_dissemination_subscribe(struct ck_barrier_dissemination *barrier, + struct ck_barrier_dissemination_state *state) +{ + + state->parity = 0; + state->sense = ~0; + state->tid = ck_pr_faa_uint(&barrier->tid, 1); + return; +} + +unsigned int +ck_barrier_dissemination_size(unsigned int nthr) +{ + + return (ck_internal_log(ck_internal_power_2(nthr)) << 1); +} + +void +ck_barrier_dissemination(struct ck_barrier_dissemination *barrier, + struct ck_barrier_dissemination_state *state) +{ + unsigned int i; + unsigned int size = barrier->size; + + for (i = 0; i < size; ++i) { + /* Unblock current partner. */ + ck_pr_store_uint(barrier[state->tid].flags[state->parity][i].pflag, state->sense); + + /* Wait until some other thread unblocks this one. */ + while (ck_pr_load_uint(&barrier[state->tid].flags[state->parity][i].tflag) != state->sense) + ck_pr_stall(); + } + + /* + * Dissemination barriers use two sets of flags to prevent race conditions + * between successive calls to the barrier. Parity indicates which set will + * be used for the next barrier. They also use a sense reversal technique + * to avoid re-initialization of the flags for every two calls to the barrier. + */ + if (state->parity == 1) + state->sense = ~state->sense; + + state->parity = 1 - state->parity; + return; +} + diff --git a/src/ck_barrier_mcs.c b/src/ck_barrier_mcs.c new file mode 100644 index 0000000..e497714 --- /dev/null +++ b/src/ck_barrier_mcs.c @@ -0,0 +1,142 @@ +/* + * 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 + +void +ck_barrier_mcs_init(struct ck_barrier_mcs *barrier, + unsigned int nthr) +{ + unsigned int i, j; + + ck_pr_store_uint(&barrier->tid, 0); + + for (i = 0; i < nthr; ++i) { + for (j = 0; j < 4; ++j) { + /* + * If there are still threads that don't have parents, + * add it as a child. + */ + barrier[i].havechild[j] = ((i << 2) + j < nthr - 1) ? ~0 : 0; + + /* + * childnotready is initialized to havechild to ensure + * a thread does not wait for a child that does not exist. + */ + barrier[i].childnotready[j] = barrier[i].havechild[j]; + } + + /* The root thread does not have a parent. */ + barrier[i].parent = (i == 0) ? + &barrier[i].dummy : + &barrier[(i - 1) >> 2].childnotready[(i - 1) & 3]; + + /* Leaf threads do not have any children. */ + barrier[i].children[0] = ((i << 1) + 1 >= nthr) ? + &barrier[i].dummy : + &barrier[(i << 1) + 1].parentsense; + + barrier[i].children[1] = ((i << 1) + 2 >= nthr) ? + &barrier[i].dummy : + &barrier[(i << 1) + 2].parentsense; + + barrier[i].parentsense = 0; + } + + return; +} + +void +ck_barrier_mcs_subscribe(struct ck_barrier_mcs *barrier, struct ck_barrier_mcs_state *state) +{ + + state->sense = ~0; + state->vpid = ck_pr_faa_uint(&barrier->tid, 1); + return; +} + +CK_CC_INLINE static bool +ck_barrier_mcs_check_children(unsigned int *childnotready) +{ + + if (ck_pr_load_uint(&childnotready[0]) != 0) + return (false); + if (ck_pr_load_uint(&childnotready[1]) != 0) + return (false); + if (ck_pr_load_uint(&childnotready[2]) != 0) + return (false); + if (ck_pr_load_uint(&childnotready[3]) != 0) + return (false); + + return (true); +} + +CK_CC_INLINE static void +ck_barrier_mcs_reinitialize_children(struct ck_barrier_mcs *node) +{ + + ck_pr_store_uint(&node->childnotready[0], node->havechild[0]); + ck_pr_store_uint(&node->childnotready[1], node->havechild[1]); + ck_pr_store_uint(&node->childnotready[2], node->havechild[2]); + ck_pr_store_uint(&node->childnotready[3], node->havechild[3]); + return; +} + +void +ck_barrier_mcs(struct ck_barrier_mcs *barrier, + struct ck_barrier_mcs_state *state) +{ + + /* + * Wait until all children have reached the barrier and are done waiting + * for their children. + */ + while (ck_barrier_mcs_check_children(barrier[state->vpid].childnotready) == false) + ck_pr_stall(); + + /* Reinitialize for next barrier. */ + ck_barrier_mcs_reinitialize_children(&barrier[state->vpid]); + + /* Inform parent thread and its children have arrived at the barrier. */ + ck_pr_store_uint(barrier[state->vpid].parent, 0); + + /* Wait until parent indicates all threads have arrived at the barrier. */ + if (state->vpid != 0) { + while (ck_pr_load_uint(&barrier[state->vpid].parentsense) != state->sense) + ck_pr_stall(); + } + + /* Inform children of successful barrier. */ + ck_pr_store_uint(barrier[state->vpid].children[0], state->sense); + ck_pr_store_uint(barrier[state->vpid].children[1], state->sense); + state->sense = ~state->sense; + return; +} + diff --git a/src/ck_barrier_tournament.c b/src/ck_barrier_tournament.c new file mode 100644 index 0000000..f699ee4 --- /dev/null +++ b/src/ck_barrier_tournament.c @@ -0,0 +1,167 @@ +/* + * 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 "ck_internal.h" + +/* + * This is a tournament barrier implementation. Threads are statically + * assigned roles to perform for each round of the barrier. Winners + * move on to the next round, while losers spin in their current rounds + * on their own flags. During the last round, the champion of the tournament + * sets the last flag that begins the wakeup process. + */ +static unsigned int ck_barrier_tournament_tid; + +void +ck_barrier_tournament_state_init(ck_barrier_tournament_state_t *state) +{ + + state->sense = ~0; + state->vpid = ck_pr_faa_uint(&ck_barrier_tournament_tid, 1); + return; +} + +void +ck_barrier_tournament_round_init(struct ck_barrier_tournament_round **rounds, + unsigned int nthr) +{ + unsigned int i, k, size, twok, twokm1, imod2k; + + size = ck_barrier_tournament_size(nthr); + for (i = 0; i < nthr; ++i) { + /* The first role is always DROPOUT. */ + rounds[i][0].flag = 0; + rounds[i][0].role = DROPOUT; + for (k = 1, twok = 2, twokm1 = 1; k < size; ++k, twokm1 = twok, twok <<= 1) { + rounds[i][k].flag = 0; + + imod2k = i & (twok - 1); + if (imod2k == 0) { + if ((i + twokm1 < nthr) && (twok < nthr)) + rounds[i][k].role = WINNER; + else if (i + twokm1 >= nthr) + rounds[i][k].role = BYE; + } + if (imod2k == twokm1) + rounds[i][k].role = LOSER; + + /* There is exactly one champion in a tournament barrier. */ + else if ((i == 0) && (twok >= nthr)) + rounds[i][k].role = CHAMPION; + + if (rounds[i][k].role == LOSER) + rounds[i][k].opponent = &rounds[i - twokm1][k].flag; + else if (rounds[i][k].role == WINNER || rounds[i][k].role == CHAMPION) + rounds[i][k].opponent = &rounds[i + twokm1][k].flag; + } + } + + return; +} + +unsigned int +ck_barrier_tournament_size(unsigned int nthr) +{ + + return (ck_internal_log(ck_internal_power_2(nthr)) + 1); +} + +void +ck_barrier_tournament(struct ck_barrier_tournament_round **rounds, + struct ck_barrier_tournament_state *state) +{ + int round = 1; + + for (;; ++round) { + switch (rounds[state->vpid][round].role) { // MIGHT NEED TO USE CK_PR_LOAD*** + case BYE: + break; + case CHAMPION: + /* + * The CHAMPION waits until it wins the tournament; it then + * sets the final flag before the wakeup phase of the barrier. + */ + while (ck_pr_load_uint(&rounds[state->vpid][round].flag) != state->sense) + ck_pr_stall(); + ck_pr_store_uint(rounds[state->vpid][round].opponent, state->sense); + goto wakeup; + break; + case DROPOUT: + /* NOTREACHED */ + break; + case LOSER: + /* + * LOSERs set the flags of their opponents and wait until + * their opponents release them after the tournament is over. + */ + ck_pr_store_uint(rounds[state->vpid][round].opponent, state->sense); + while (ck_pr_load_uint(&rounds[state->vpid][round].flag) != state->sense) + ck_pr_stall(); + goto wakeup; + break; + case WINNER: + /* + * WINNERs wait until their current opponent sets their flag; they then + * continue to the next round of the tournament. + */ + while (ck_pr_load_uint(&rounds[state->vpid][round].flag) != state->sense) + ck_pr_stall(); + break; + } + } +wakeup: + for (round -= 1;; --round) { + switch (rounds[state->vpid][round].role) { // MIGHT NEED TO USE CK_PR_LOAD*** + case BYE: + break; + case CHAMPION: + /* NOTREACHED */ + break; + case DROPOUT: + goto leave; + break; + case LOSER: + /* NOTREACHED */ + break; + case WINNER: + /* + * Winners inform their old opponents the tournament is over + * by setting their flags. + */ + ck_pr_store_uint(rounds[state->vpid][round].opponent, state->sense); + break; + } + } + +leave: + state->sense = ~state->sense; + return; +} + diff --git a/src/ck_internal.h b/src/ck_internal.h new file mode 100644 index 0000000..5e7a4f1 --- /dev/null +++ b/src/ck_internal.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +/* + * Log and power_2 algorithms from: http://graphics.stanford.edu/~seander/bithacks.html + */ + +#define CK_INTERNAL_LOG_0 (0xAAAAAAAA) +#define CK_INTERNAL_LOG_1 (0xCCCCCCCC) +#define CK_INTERNAL_LOG_2 (0xF0F0F0F0) +#define CK_INTERNAL_LOG_3 (0xFF00FF00) +#define CK_INTERNAL_LOG_4 (0xFFFF0000) + +CK_CC_INLINE static uint32_t +ck_internal_log(uint32_t v) +{ + uint32_t r = (v & CK_INTERNAL_LOG_0) != 0; + + r |= ((v & CK_INTERNAL_LOG_4) != 0) << 4; + r |= ((v & CK_INTERNAL_LOG_3) != 0) << 3; + r |= ((v & CK_INTERNAL_LOG_2) != 0) << 2; + r |= ((v & CK_INTERNAL_LOG_1) != 0) << 1; + return (r); +} + +CK_CC_INLINE static uint32_t +ck_internal_power_2(uint32_t v) +{ + + --v; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return (++v); +} +