/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2017 Matthieu Schaller (schaller@strw.leidenuniv.nl).
*
* 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 .
*
******************************************************************************/
#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
/* Standard headers */
#include
/* 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 */