X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=course%2Fsemester2%2Fpprog%2Fassignment1%2Fmthread%2Fnbody.c;h=75049286f5a92f17ac1de4c2548d6501f77e750b;hb=19d5190c55a836926f2bd646e043fcd6c61ab919;hp=afd1d607dbd54b89edb9ca8166ea4fcbdebd21b1;hpb=4b52f4b6f00888b393292646bcedb4561f5de714;p=matches%2Fhonours.git diff --git a/course/semester2/pprog/assignment1/mthread/nbody.c b/course/semester2/pprog/assignment1/mthread/nbody.c index afd1d607..75049286 100644 --- a/course/semester2/pprog/assignment1/mthread/nbody.c +++ b/course/semester2/pprog/assignment1/mthread/nbody.c @@ -7,18 +7,19 @@ #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; /** @@ -82,26 +83,20 @@ void * Compute_Thread(void * arg) 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(); } @@ -115,11 +110,6 @@ void * Compute_Thread(void * arg) continue; } - - - - workers_busy = options.num_threads; //All threads working - //Compute forces for (unsigned i = 0; i < options.num_threads; ++i) { @@ -129,19 +119,19 @@ void * Compute_Thread(void * arg) 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) @@ -155,19 +145,47 @@ void * Compute_Thread(void * arg) } //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 @@ -175,16 +193,12 @@ void * Compute_Thread(void * arg) */ 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; } @@ -195,16 +209,9 @@ void * Force_Thread(void * s) */ 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; } @@ -217,7 +224,8 @@ void * Position_Thread(void * s) */ 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; @@ -247,29 +255,60 @@ void Thread_Cleanup(void) * @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)); }