| /* |
| * Copyright 2011-2015 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. |
| */ |
| |
| enum { |
| CK_BARRIER_TOURNAMENT_BYE, |
| CK_BARRIER_TOURNAMENT_CHAMPION, |
| CK_BARRIER_TOURNAMENT_DROPOUT, |
| CK_BARRIER_TOURNAMENT_LOSER, |
| CK_BARRIER_TOURNAMENT_WINNER |
| }; |
| |
| void |
| ck_barrier_tournament_subscribe(struct ck_barrier_tournament *barrier, |
| struct ck_barrier_tournament_state *state) |
| { |
| |
| state->sense = ~0; |
| state->vpid = ck_pr_faa_uint(&barrier->tid, 1); |
| return; |
| } |
| |
| void |
| ck_barrier_tournament_init(struct ck_barrier_tournament *barrier, |
| struct ck_barrier_tournament_round **rounds, |
| unsigned int nthr) |
| { |
| unsigned int i, k, size, twok, twokm1, imod2k; |
| |
| ck_pr_store_uint(&barrier->tid, 0); |
| barrier->size = size = ck_barrier_tournament_size(nthr); |
| |
| for (i = 0; i < nthr; ++i) { |
| /* The first role is always CK_BARRIER_TOURNAMENT_DROPOUT. */ |
| rounds[i][0].flag = 0; |
| rounds[i][0].role = CK_BARRIER_TOURNAMENT_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 = CK_BARRIER_TOURNAMENT_WINNER; |
| else if (i + twokm1 >= nthr) |
| rounds[i][k].role = CK_BARRIER_TOURNAMENT_BYE; |
| } |
| |
| if (imod2k == twokm1) |
| rounds[i][k].role = CK_BARRIER_TOURNAMENT_LOSER; |
| else if ((i == 0) && (twok >= nthr)) |
| rounds[i][k].role = CK_BARRIER_TOURNAMENT_CHAMPION; |
| |
| if (rounds[i][k].role == CK_BARRIER_TOURNAMENT_LOSER) |
| rounds[i][k].opponent = &rounds[i - twokm1][k].flag; |
| else if (rounds[i][k].role == CK_BARRIER_TOURNAMENT_WINNER || |
| rounds[i][k].role == CK_BARRIER_TOURNAMENT_CHAMPION) |
| rounds[i][k].opponent = &rounds[i + twokm1][k].flag; |
| } |
| } |
| |
| ck_pr_store_ptr(&barrier->rounds, rounds); |
| 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 *barrier, |
| struct ck_barrier_tournament_state *state) |
| { |
| struct ck_barrier_tournament_round **rounds = ck_pr_load_ptr(&barrier->rounds); |
| int round = 1; |
| |
| if (barrier->size == 1) |
| return; |
| |
| for (;; ++round) { |
| switch (rounds[state->vpid][round].role) { |
| case CK_BARRIER_TOURNAMENT_BYE: |
| break; |
| case CK_BARRIER_TOURNAMENT_CHAMPION: |
| /* |
| * The CK_BARRIER_TOURNAMENT_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; |
| case CK_BARRIER_TOURNAMENT_DROPOUT: |
| /* NOTREACHED */ |
| break; |
| case CK_BARRIER_TOURNAMENT_LOSER: |
| /* |
| * CK_BARRIER_TOURNAMENT_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; |
| case CK_BARRIER_TOURNAMENT_WINNER: |
| /* |
| * CK_BARRIER_TOURNAMENT_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) { |
| case CK_BARRIER_TOURNAMENT_BYE: |
| break; |
| case CK_BARRIER_TOURNAMENT_CHAMPION: |
| /* NOTREACHED */ |
| break; |
| case CK_BARRIER_TOURNAMENT_DROPOUT: |
| goto leave; |
| break; |
| case CK_BARRIER_TOURNAMENT_LOSER: |
| /* NOTREACHED */ |
| break; |
| case CK_BARRIER_TOURNAMENT_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: |
| ck_pr_fence_memory(); |
| state->sense = ~state->sense; |
| return; |
| } |