barrier.h 4.88 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
/*******************************************************************************
 * This file is part of SWIFT.
 * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 ******************************************************************************/
#ifndef SWIFT_BARRIER_H
#define SWIFT_BARRIER_H

/**
 * @file barrier.h
 * @brief Define the thread barriers if the POSIX implementation on this system
 * does not.
 *
 * The pthread barriers are only an option of the POSIX norm and they are not
 * necessarily implemented. One example is OSX where all the rest of POSIX
 * exists but not the barriers.
 * We implement them here in a simple way to allow for SWIFT to run on such
 * systems but this may lead to poorer performance.
 *
 * Note that we only define the three functions we need. This is a textbook
 * implementation of a barrier that uses the common POSIX features (mutex,
 * conditions and broadcasts).
 *
 * If the pthread barriers exist (Linux systems), we default to them.
 */

/* Config parameters. */
#include "../config.h"

/* Standard headers */
#include <pthread.h>

/* Does this POSIX implementation provide barriers? */
#ifdef HAVE_PTHREAD_BARRIERS

#define swift_barrier_t pthread_barrier_t
#define swift_barrier_wait pthread_barrier_wait
#define swift_barrier_init pthread_barrier_init
#define swift_barrier_destroy pthread_barrier_destroy

#else

/* Local headers */
#include "error.h"
#include "inline.h"

/**
 * @brief An ersatz of POSIX barriers to be used on systems that don't provide
 * the good ones.
 */
typedef struct {

  /*! Barrier mutex */
  pthread_mutex_t mutex;

  /*! Condition to open the barrier */
  pthread_cond_t condition;

  /*! Total number of threads */
  int limit;

  /*! Number of threads that reached the barrier */
  int count;

} swift_barrier_t;

/**
 * @brief Initialise a barrier object.
 *
 * @param barrier The #swift_barrier_t to initialise
 * @param unused Unused parameter (NULL) as we don't support barrier attributes.
 * @param count The number of threads that will wait at the barrier.
 */
static INLINE int swift_barrier_init(swift_barrier_t *barrier, void *unused,
                                     unsigned int count) {
  /* Initialise the mutex */
  if (pthread_mutex_init(&barrier->mutex, 0) != 0)
    error("Error initializing the barrier mutex");

  /* Initialise the condition */
  if (pthread_cond_init(&barrier->condition, 0) != 0)
    error("Error initializing the barrier condition");

  barrier->limit = count;
  barrier->count = 0;

  /* All is good */
  return 0;
}

/**
 * @brief Make a set of threads wait at the barrier
 *
 * Note that once all threads have reached the barrier, we also
 * reset the barrier to state where it is ready to be re-used
 * without calling swift_barrier_init.
 *
 * @param barrier The (initialised) #swift_barrier_t to wait at.
 */
static INLINE int swift_barrier_wait(swift_barrier_t *barrier) {

  /* Start by locking the barrier */
  pthread_mutex_lock(&barrier->mutex);

  /* One more thread has gone home*/
  barrier->count++;

  /* Are threads still running? */
  if (barrier->count < barrier->limit) {

    /* We need to make the thread wait until everyone is back */
    pthread_cond_wait(&barrier->condition, &(barrier->mutex));

    /* Release the mutex */
    pthread_mutex_unlock(&barrier->mutex);

    /* Say that this was not the last thread */
    return 0;

  } else { /* Everybody is home */

    /* Open the barrier (i.e. release the threads blocked in the while loop) */
    pthread_cond_broadcast(&barrier->condition);

    /* Re-initialize the barrier */
    barrier->count = 0;

    /* Release the mutex */
    pthread_mutex_unlock(&barrier->mutex);

    /* Say that we are all done */
    return 1;
  }
}

/**
 * @brief Destroy a barrier object
 *
 * Note that if destroy is called before a barrier is open, we return
 * an error message and do not attempt to wait for the barrier to open
 * before destroying it.
 *
 * @param barrier The #swift_barrier_t object to destroy.
 */
static INLINE int swift_barrier_destroy(swift_barrier_t *barrier) {

  /* Destroy the pthread things */
  pthread_cond_destroy(&barrier->condition);
  pthread_mutex_destroy(&barrier->mutex);

  /* All is good */
  return 0;
}

#endif /* HAVE_PTHREAD_BARRIERS */

#endif /* SWIFT_BARRIER_H */