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