+/**
+ * @file barrier.c
+ * @purpose Implementation of a pthread barrier
+ * @author Sam Moore (20503628) 2012
+ */
+
+#include "barrier.h"
+
+
+/**
+ * @function Barrier_Init
+ * @purpose Initialise a Barrier
+ * Should only be called once for each Barrier
+ * @param b - The Barrier
+ * @param total_threads - Number of threads that will be participating in the barrier
+ *
+ */
+void Barrier_Init(Barrier * b, unsigned total_threads)
+{
+ b->total_threads = total_threads;
+ b->threads_done = total_threads; // All threads start as done.
+ // When Barrier_Join is called, the first entering thread will set threads_done to zero.
+ // Each thread joining the barrier increments threads_done, and sleep until all threads have joined.
+ b->active = false;
+}
+
+void Barrier_Enter(Barrier * b)
+{
+ b->active = true;
+}
+
+void Barrier_Wait(Barrier * b)
+{
+
+ pthread_mutex_lock(&(b->mutex));
+ while (b->active)
+ {
+ pthread_cond_wait(&(b->threads_done_cv), &(b->mutex));
+ }
+ pthread_mutex_unlock(&(b->mutex));
+}
+
+/**
+ * @function Barrier_Join
+ * @purpose A Barrier that waits for the required number of threads to enter it, before allowing all threads to continue
+ *
+ * This function should be called for each thread participating in the barrier.
+ * Barrier_Init should first be called with the correct total number of threads participating in the barrier.
+ *
+ * @param b - The barrier
+ */
+void Barrier_Join(Barrier * b)
+{
+ pthread_mutex_lock(&(b->mutex));
+
+ if (b->threads_done >= b->total_threads) // The barrier is not active
+ {
+ b->threads_done = 0; // Activate the barrier
+ b->active = true;
+ }
+
+ b->threads_done += 1;
+ //printf("Thread joins barrier %p, %u done out of %u\n", (void*)(b), b->threads_done, b->total_threads);
+ if (b->threads_done == b->total_threads)
+ {
+ pthread_cond_broadcast(&(b->threads_done_cv));
+ b->active = false;
+ //printf("\tThread wakes up other threads\n");
+ }
+ else
+ {
+ //printf("\tThread waiting on barrier\n");
+ while (b->threads_done < b->total_threads) // Keep waiting as long as the barrier is active
+ pthread_cond_wait(&(b->threads_done_cv), &(b->mutex));
+
+ //printf("\tThread done with barrier\n");
+
+ }
+
+
+ //printf("\tThread leaves barrier; %u threads done out of %u\n", b->threads_done, b->total_threads);
+ pthread_mutex_unlock(&(b->mutex));
+
+}
+
+
+void Barrier_ForceExit(Barrier * b)
+{
+ pthread_mutex_lock(&(b->mutex));
+ pthread_cond_broadcast(&(b->threads_done_cv));
+ b->active = false;
+ pthread_mutex_unlock(&(b->mutex));
+}
+
+/**
+ * @function Barrier_JoinCall
+ * @purpose Behaves as Barrier_Join(b), except the last thread in the barrier calls the specified function
+ * (This is a hack to get code to execute in one thread without additional mutexes when the code happens just after a barrier anyway).
+ * (Could make the first thread execute the function for efficiency, but for the nbody simulator that would make the performance metric wrong)
+ * @param b - The barrier
+ * @param call - Function pointer to call (function pointers are fun!)
+ * @param args - Arguments to the function
+ */
+void * Barrier_JoinCall(Barrier * b, void*(*call)(void*), void * args)
+{
+ void * result = NULL;
+ pthread_mutex_lock(&(b->mutex));
+
+ if (b->threads_done >= b->total_threads)
+ {
+ b->threads_done = 0;
+ b->active = true;
+ }
+
+ b->threads_done += 1;
+ //printf("Thread joins barrier %p, %u done out of %u\n", (void*)(b), b->threads_done, b->total_threads);
+ if (b->threads_done == b->total_threads)
+ {
+ result = (*call)(args);
+ pthread_cond_broadcast(&(b->threads_done_cv));
+ b->active = false;
+ //printf("\tThread wakes up other threads\n");
+ }
+ else
+ {
+ //printf("\tThread waiting on barrier\n");
+ while (b->threads_done < b->total_threads)
+ pthread_cond_wait(&(b->threads_done_cv), &(b->mutex));
+ //printf("\tThread done with barrier\n");
+
+ }
+
+ pthread_mutex_unlock(&(b->mutex));
+
+ return result;
+}
+
+//EOF
+
+