ck_barrier: First round audit, tournament barriers next.

The barriers have been restructured into individual file
per implementation. Some micro-optimizations were implemented
for some barriers (caching common computations in the barrier).
State subsription is now explicit with the TID counter allocated
on a per-barrier basis.

Tournament barriers remaining and then another round will be done
for correctness and algorithmic improvements.
ck_pring
Samy Al Bahra 14 years ago
parent b0acd3439b
commit 8b4f72057c

@ -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 */

@ -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)

@ -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);

@ -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)

@ -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

@ -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 <ck_barrier.h>
#include <ck_cc.h>
#include <ck_pr.h>
#include <ck_spinlock.h>
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;
}

@ -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 <ck_barrier.h>
#include <ck_pr.h>
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;
}

@ -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 <ck_barrier.h>
#include <ck_cc.h>
#include <ck_pr.h>
#include <ck_spinlock.h>
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;
}

@ -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 <ck_barrier.h>
#include <ck_cc.h>
#include <ck_pr.h>
#include <ck_spinlock.h>
#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;
}

@ -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 <ck_barrier.h>
#include <ck_cc.h>
#include <ck_pr.h>
#include <stdbool.h>
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;
}

@ -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 <ck_barrier.h>
#include <ck_pr.h>
#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;
}

@ -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);
}
Loading…
Cancel
Save