#include "nbody.h" // Declarations
#include "../single-thread/nbody.c" // Include all functions from the single threaded version
+#include "graphics.h" // For declaration of Graphics_Run only
+
// --- Variable declarations --- //
pthread_t compute_thread; // The thread responsible for computations; it spawns worker threads
pthread_t * worker_thread = NULL; //Array of worker threads responsible for Force and Position updates
System * sub_system = NULL; //Array of Systems used to divide up the main "universe" System for worker threads
-pthread_mutex_t mutex_workers; // Mutex used for the barrier between Force and Position updates
-pthread_cond_t workers_done_cv; // Conditional used for the barrier between Force and Position updates
-unsigned workers_busy; // Number of workers currently doing something
pthread_mutex_t mutex_runstate; // Mutex around the runstate
+Barrier worker_barrier;
+Barrier graphics_barrier;
/**
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //Needs to be detached, so that memory can be reused.
+
+
+
// The main computation loop
while (true)
{
-
- if (runstate != RUN) pthread_exit(NULL); //Check whether the main thread wants to exit
-
-
-
//Check whether the program should quit due to steps being computed, or a timeout
- if (options.timeout > 0.0)
+ if (ExitCondition())
{
- if ((unsigned)(time(NULL) - options.start_time.tv_sec) >= options.timeout)
- QuitProgram(false);
- }
-
- if (options.num_steps > 0 && universe.steps > options.num_steps)
QuitProgram(false);
+ pthread_exit(NULL);
+ }
- if (options.draw_graphics == false && options.verbosity != 0
- && universe.steps % options.verbosity == 1)
+ if (options.verbosity != 0 && universe.steps % options.verbosity == 1)
{
DisplayStatistics();
}
continue;
}
-
-
-
- workers_busy = options.num_threads; //All threads working
-
//Compute forces
for (unsigned i = 0; i < options.num_threads; ++i)
{
QuitProgram(true);
pthread_exit(NULL);
}
+
}
-
- //Barrier - Wait for forces to be computed
- pthread_mutex_lock(&mutex_workers);
- while (workers_busy > 0)
- pthread_cond_wait(&workers_done_cv, &mutex_workers);
- pthread_mutex_unlock(&mutex_workers);
+ Barrier_Wait(&worker_barrier);
//All the forces are now computed
-
- workers_busy = options.num_threads; //All threads working
+
+ if (options.draw_graphics && options.pedantic_graphics)
+ {
+ Barrier_Wait(&graphics_barrier);
+ Barrier_Enter(&graphics_barrier);
+ }
//Compute positions
for (unsigned i = 0; i < options.num_threads; ++i)
}
//Wait for positions to be computed
- pthread_mutex_lock(&mutex_workers);
- while (workers_busy > 0)
- pthread_cond_wait(&workers_done_cv, &mutex_workers);
- pthread_mutex_unlock(&mutex_workers);
+ Barrier_Wait(&worker_barrier);
+
//Update number of steps computed
universe.steps += 1;
+ if (options.draw_graphics && options.pedantic_graphics)
+ Barrier_Leave(&graphics_barrier);
}
}
+/**
+ * @function BeforeDraw
+ * @purpose Called in graphics thread before the draw loop
+ * When --pedantic-graphics enabled, will wait for position computations to finish before drawing
+ * Otherwise does nothing
+ */
+void BeforeDraw()
+{
+ if (!options.pedantic_graphics)
+ return;
+ Barrier_Wait(&graphics_barrier);
+ Barrier_Enter(&graphics_barrier);
+}
+
+/**
+ * @function AfterDraw
+ * @purpose Called in graphics thread after the draw loop
+ * When --pedantic-graphics is supplied, will signal computation thread that drawing is finished
+ * So that positions can be safely altered
+ * Otherwise does nothing
+ */
+void AfterDraw()
+{
+ if (!options.pedantic_graphics)
+ return;
+ Barrier_Leave(&graphics_barrier);
+}
+
/**
* @function Force_Thread
* @purpose Thread - Calculates the forces on objects in a System
*/
void * Force_Thread(void * s)
{
-
+ Barrier_Enter(&worker_barrier);
+
System_Forces((System*)s, &universe); //Simple wrapper
- pthread_mutex_lock(&mutex_workers);
- workers_busy -= 1; // Count this thread as done
- if (workers_busy == 0)
- {
- pthread_cond_signal(&workers_done_cv); // All threads done; wake up the compute_thread
- }
- pthread_mutex_unlock(&mutex_workers);
+ Barrier_Leave(&worker_barrier);
+
return NULL;
}
*/
void * Position_Thread(void * s)
{
-
+ Barrier_Enter(&worker_barrier);
System_Positions((System*)s); // Simple wrapper
-
- pthread_mutex_lock(&mutex_workers);
- workers_busy -= 1; // Count this thread as done
- if (workers_busy == 0)
- {
- pthread_cond_signal(&workers_done_cv); //All threads done; wake up the compute_thread
- }
- pthread_mutex_unlock(&mutex_workers);
+ Barrier_Leave(&worker_barrier);
return NULL;
}
*/
void QuitProgram(bool error)
{
-
+ if (runstate == QUIT || runstate == QUIT_ERROR)
+ return; //Don't do anything if already quitting
pthread_mutex_lock(&mutex_runstate); // aquire mutex
if (error) // set the runstate
runstate = QUIT_ERROR;
* @function Simulation_Run
* @purpose Initialise and start the simulation. Will be called in the main thread.
* Replaces the single-threaded macro that does nothing, and sets up the compute thread
+ * @param argc - Number of arguments - Passed to Graphics_Run if needed
+ * @param argv - Argument strings - Passed to Graphics_Run if needed
*/
-void Simulation_Run()
+void Simulation_Run(int argc, char ** argv)
{
atexit(Thread_Cleanup);
- if (options.draw_graphics == false)
+ Barrier_Init(&worker_barrier);
+ Barrier_Init(&graphics_barrier);
+
+ if (options.draw_graphics)
{
- Compute_Thread((void*)(&universe));
+ // The graphics are enabled, so create a thread to do computations
+ // Graphics are done in the main loop
+ if (pthread_create(&compute_thread, NULL, Compute_Thread, (void*)&universe) != 0)
+ {
+ perror("Error creating compute thread");
+ exit(EXIT_FAILURE);
+ }
+ Graphics_Run(argc, argv);
+ }
+ else
+ Compute_Thread((void*)(&universe)); // Graphics are disabled, so do computations in the main thread
+}
- // If there are no graphics, run the computation loop in the main thread
- // The call to pthread_exit(NULL) in the computation loop ends the main thread
- // This means the main function never calls the Graphics_Run function
- // Without this condition here, Graphics_Display would essentially be running a busy loop in the main thread
-
- fprintf(stderr,"Should not see this\n");
- exit(EXIT_FAILURE);
- }
-
- // Create a thread to handle computation loop
- if (pthread_create(&compute_thread, NULL, Compute_Thread, (void*)&universe) != 0)
+
+void Barrier_Init(Barrier * b)
+{
+ b->threads_busy = 0;
+}
+
+void Barrier_Enter(Barrier * b)
+{
+ pthread_mutex_lock(&(b->mutex));
+ b->threads_busy += 1;
+ pthread_mutex_unlock(&(b->mutex));
+}
+
+void Barrier_Leave(Barrier * b)
+{
+ pthread_mutex_lock(&(b->mutex));
+ b->threads_busy -= 1;
+ if (b->threads_busy == 0)
{
- perror("Error creating compute thread");
- exit(EXIT_FAILURE);
+ pthread_cond_signal(&(b->threads_done_cv));
}
+ pthread_mutex_unlock(&(b->mutex));
+}
+
+void Barrier_Wait(Barrier * b)
+{
+ pthread_mutex_lock(&(b->mutex));
+ while (b->threads_busy > 0)
+ pthread_cond_wait(&(b->threads_done_cv), &(b->mutex));
+ pthread_mutex_unlock(&(b->mutex));
}