Parallel Programming - pthreads version
[matches/honours.git] / course / semester2 / pprog / assignment1 / mthread / nbody.c
index afd1d60..7504928 100644 (file)
@@ -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));
 }

UCC git Repository :: git.ucc.asn.au