Parallel Programming - Finished pthreads
authorSam Moore <sam@daedalus.(none)>
Sun, 30 Sep 2012 11:54:54 +0000 (19:54 +0800)
committerSam Moore <sam@daedalus.(none)>
Sun, 30 Sep 2012 11:54:54 +0000 (19:54 +0800)
I finally fixed all the stupid bugs in pthreads.
Committing before messing with the openmp version (which surprisingly compiles)

I basically just need to get rid of the busy loop in the openmp version.
I will probably have to completely change the structure or something. Grrr.

12 files changed:
course/semester2/pprog/assignment1/get_data.py
course/semester2/pprog/assignment1/mthread/barrier.c [new file with mode: 0644]
course/semester2/pprog/assignment1/mthread/barrier.h [new file with mode: 0644]
course/semester2/pprog/assignment1/mthread/nbody.c
course/semester2/pprog/assignment1/mthread/nbody.h
course/semester2/pprog/assignment1/mthread/test_barrier.c [new file with mode: 0644]
course/semester2/pprog/assignment1/openmp/nbody.h
course/semester2/pprog/assignment1/single-thread/graphics.c
course/semester2/pprog/assignment1/single-thread/graphics.h
course/semester2/pprog/assignment1/single-thread/main.c
course/semester2/pprog/assignment1/single-thread/nbody.c
course/semester2/pprog/assignment1/single-thread/nbody.h

index 04ad2be..359c418 100755 (executable)
@@ -14,11 +14,10 @@ plot = Gnuplot.Gnuplot(persist=0)
 programs = {
        "single-thread" : "./single-thread/nbody -v 5"
 }
-for n in range(2, 6):
-       programs.update({"mthread"+str(n) : "./mthread/nbody -v 5 --pedantic-graphics -n "+str(n)})
+for n in range(2, 20, 4):
        programs.update({"mthread"+str(n) : "./mthread/nbody -v 5 -n "+str(n)})
-       #programs.update({"slow-mthread"+str(n) : "./mthread/nbody-slow --pedantic-graphics -v 5 -n "+str(n)})
-       #programs.update({"openmp"+str(n) : "./openmp/nbody -v 5 --pedantic-graphics -n " +str(n)})
+       #programs.update({"slow-mthread"+str(n) : "./mthread/nbody-slow -v 5 -n "+str(n)})
+       programs.update({"openmp"+str(n) : "./openmp/nbody -v 5 -n " +str(n)})
 
 
 
diff --git a/course/semester2/pprog/assignment1/mthread/barrier.c b/course/semester2/pprog/assignment1/mthread/barrier.c
new file mode 100644 (file)
index 0000000..9b1a3a0
--- /dev/null
@@ -0,0 +1,140 @@
+/**
+ * @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
+
+
diff --git a/course/semester2/pprog/assignment1/mthread/barrier.h b/course/semester2/pprog/assignment1/mthread/barrier.h
new file mode 100644 (file)
index 0000000..e89630a
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * @file barrier.h
+ * @purpose Declaration for a pthread barrier
+ * @author Sam Moore (20503628) 2012
+ */
+
+// The barrier can be used as follows:
+
+// Think of the barrier as relating to a section of work that must be completed by a team of threads
+// The barrier is initialised with the number of threads in the team
+// Each thread in the team calls Barrier_Join when finished; this ensures all threads reach Barrier_Join before continuing.
+
+// To make it conceptually easier to synchronise with threads that are not part of the "team":
+// I have introduced Barrier_Wait, and Barrier_Enter
+// Barrier_Wait is used to either:
+//     1. Immediately continue if threads have already completed a task and called Barrier_Join
+//     2. Sleep until all threads call Barrier_Join if they have not completed their task
+// Barrier_Enter should be called in threads that belong to the "team", to indicate that they have started their task. 
+//     Otherwise, Barrier_Wait will generally fail.
+// This requires a single boolean, and makes things easier (for me) to understand, so I think it is worth it.
+// Also, *any* number of threads may call Barrier_Wait; it is not restricted by the number of threads actually doing the "work".
+// If there are no threads calling Barrier_Wait, then Barrier_Enter is not needed; the first thread to call Barrier_Join always activates the barrier. It is always deactivated when threads leave.
+
+
+#ifndef _BARRIER_H
+#define _BARRIER_H
+
+#include <pthread.h>
+#include <stdbool.h>
+
+/**
+ * Structure to represent a barrier for multiple threads
+ * @param mutex - Mutex around the counter
+ * @param busy - Counter of threads within the barrier
+ * @param threads_done_cv - Condition to wake up threads waiting on barrier once all working threads have left it
+ */
+typedef struct
+{
+       pthread_mutex_t mutex;
+       unsigned threads_done; // Counter of threads which are finished
+       unsigned total_threads; // Total number of threads participating in the barrier
+       bool active; // Indicates whether barrier is active
+       pthread_cond_t threads_done_cv;
+} Barrier;
+
+
+
+void Barrier_Init(Barrier * b, unsigned total_threads);
+void Barrier_Enter(Barrier * b);
+void Barrier_Join(Barrier * b);
+void Barrier_Wait(Barrier * b);
+
+void * Barrier_JoinCall(Barrier * b, void*(*call)(void*), void * args);
+
+void Barrier_ForceExit(Barrier * b); // Force all threads to exit barrier
+
+
+#endif //_BARRIER_H
+
index de9b8aa..13831cd 100644 (file)
@@ -6,12 +6,13 @@
 
 #include "nbody.h" // Declarations
 #include "../single-thread/nbody.c" // Include all functions from the single threaded version
-
+#include <assert.h>
 #include "graphics.h" // For declaration of Graphics_Run only
+#include "barrier.c"
 
 // --- Variable declarations --- //
 
-pthread_t compute_thread; // The thread responsible for computations; it spawns worker threads
+pthread_t compute_thread; // The thread responsible for computations; it spawns worker threads (* terms and conditions apply)
        
 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
@@ -21,7 +22,6 @@ pthread_mutex_t mutex_runstate; // Mutex around the runstate
 
 
 
-pthread_attr_t attr; //thread attribute for the workers. 
 
 Barrier force_barrier; // I laughed at this variable name. A bit sad really.
 Barrier position_barrier;
@@ -29,22 +29,33 @@ Barrier position_barrier;
 
 Barrier graphics_barrier;
 
+pthread_mutex_t mutex_threads_running;
+int threads_running = 0;
 
 /**
  * @function Compute_Thread
  * @purpose Thread - Continuously computes steps for a system of bodies. Seperate from graphics functions.
  *     Spawns worker threads to divide up computation.
- * @param arg - Can be cast to the System for which computations are performed (ie: &universe)
+ * @param arg - Can be cast to the System for which computations are performed 
+ *
+ *     NOTE:
+ *     This will always be (void*)(&universe) where universe is the global System variable that has every Body in it.
+ *     But I don't like global variables. And since the argument had to be passed, I thought I might as well use it.
+ *     That way, when I change "universe" to "solar_system", I only have to change the argument where this is called, not all through it.
+ *     Find and replace? Who uses that!?
  */
+
+
 void * Compute_Thread(void * arg)
 {
 
        System * s = (System*)(arg); //cast argument to a System*
 
 
-       // If no number of threads provided, use the default value, unless someone changed that to a stupid value
+
+       // If no number of threads provided, use the default value, unless someone (me) changed that to a stupid value
        if (options.num_threads <= 0)
-               options.num_threads = (DEFAULT_WORKING_THREADS > 1) ? DEFAULT_WORKING_THREADS : 1;
+               options.num_threads = (DEFAULT_WORKING_THREADS > 1) ? DEFAULT_WORKING_THREADS : 1; // Fear the ternary operator!
 
        if (options.nested_threads <= 0)
                options.nested_threads = 1;
@@ -57,91 +68,119 @@ void * Compute_Thread(void * arg)
                        s->N, options.num_threads, s->N);       
                options.num_threads = s->N;
        }
+
+       // Initialise the barriers ("shields up!")
+       Barrier_Init(&force_barrier, options.num_threads);
+       Barrier_Init(&position_barrier, options.num_threads);
+       Barrier_Init(&graphics_barrier, 1);
+
        
-       pthread_attr_init(&attr);
-       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //Needs to be detached, so that memory can be reused.
        
-       if (options.num_threads > 1) // Allocate worker threads and sub systems, as long as there would be more than 1
+       if (options.num_threads > 1) // If we require additional worker threads...
        {
-               worker_thread = Allocate_Threads(options.num_threads);
+               // Allocate worker threads and sub systems
+               
                sub_system = Split_System(&universe, options.num_threads);
 
                if (options.nested_threads > 1)
                        nested_sub_system = Split_System(&universe, options.nested_threads);
 
-               #ifdef PERSISTENT_THREADS
-               for (unsigned i = 0; i < options.num_threads; ++i)
+               #ifdef PERSISTENT_THREADS // Code for the smart way of doing it (spawn threads once, keep them running)
+               worker_thread = Allocate_Threads(options.num_threads-1);
+               // Spawn a bunch of worker threads, and let them all do their thing.
+               // Note the "-1". Because this thread can do work too!
+               for (unsigned i = 0; i < options.num_threads-1; ++i)
                {
-                       if (pthread_create(worker_thread+i, & attr, Worker_Thread, (void*)(sub_system+i)) != 0)
+                       if (pthread_create(worker_thread+i, NULL, Worker_Thread, (void*)(sub_system+i)) != 0)
                        {
                                perror("In compute thread, couldn't create worker thread");
                                QuitProgram(true);
                                pthread_exit(NULL);
                        }
                }
+               Worker_Thread((void*)(sub_system+options.num_threads-1)); // This thread becomes a worker thread
+               #else
+               worker_thread = Allocate_Threads(options.num_threads);
                #endif //PERSISTENT_THREADS
-
                
        }
        #ifdef PERSISTENT_THREADS
-       else
+       else // We only require one worker thread...
        {
+               // So just do all computations in this thread
                while (!ExitCondition())
                {
-                       if (options.verbosity != 0 && universe.steps % options.verbosity == 1)
-                               DisplayStatistics();
-
-                       // Just do everything in this thread 
+                       
                        System_Forces(s, s);
+                       // If required, wait for graphics to finish drawing
+                       if (options.draw_graphics && options.pedantic_graphics)
+                               Barrier_Wait(&graphics_barrier);
                        System_Positions(s);
+                       StepFunction(s);
+
+                       if (options.draw_graphics && options.pedantic_graphics)
+                               Barrier_ForceExit(&position_barrier); //Make the graphics continue
                }
                QuitProgram(false);
                pthread_exit(NULL);
        }
 
-       #else
-       // The main computation loop
+       #else // Code for the stupid way of doing it (respawn threads each step)
+               // (ie: The way I immediately implemented and didn't realise was stupid until someone told me)
+
+
+       
+
+       // Run until we can't run anymore
        while (!ExitCondition())
        {
-               if (options.verbosity != 0 && universe.steps % options.verbosity == 1)
-                       DisplayStatistics();
+               
 
-               if (options.num_threads <= 1)
+               if (options.num_threads <= 1) // If there is only 1 worker thread...
                {
                        // Just do everything in this thread 
                        System_Forces(s, s);
+                       // If required, wait for graphics to finish drawing
+                       if (options.draw_graphics && options.pedantic_graphics)
+                               Barrier_Join(&graphics_barrier);
+
                        System_Positions(s);
+                       StepFunction(s);
+
+                       if (options.draw_graphics && options.pedantic_graphics)
+                               Barrier_Join(&position_barrier); //Make the graphics continue
                        continue;
                }
 
                
 
-               //Compute forces
+               //Compute forces by spawning threads, each thread gets a sub system
                for (unsigned i = 0; i < options.num_threads; ++i)
                {
-                       if (pthread_create(worker_thread+i, &attr, Force_Thread, (void*)(sub_system+i)) != 0)
+                       if (pthread_create(worker_thread+i, NULL, Force_Thread, (void*)(sub_system+i)) != 0)
                        {
                                perror("In compute thread, couldn't create worker thread (force)");
                                QuitProgram(true);
                                pthread_exit(NULL);
                        }       
-                       
+                               
                }
 
+               for (unsigned i = 0; i < options.num_threads; ++i)
+                       pthread_join(worker_thread[i], NULL);
 
-               Barrier_Wait(&force_barrier);
-
-               //All the forces are now computed
-
+               
+               // If required, wait for graphics to finish drawing
                if (options.draw_graphics && options.pedantic_graphics)
-               {
                        Barrier_Wait(&graphics_barrier);
-               }
 
-               //Compute positions
+
+               Barrier_Enter(&position_barrier);
+
+               //Compute positions by spawning a bunch of threads to do it
                for (unsigned i = 0; i < options.num_threads; ++i)
                {
-                       if (pthread_create(worker_thread+i, &attr, Position_Thread, (void*)(sub_system+i)) != 0)
+                       if (pthread_create(worker_thread+i, NULL, Position_Thread, (void*)(sub_system+i)) != 0)
                        {
                                perror("In compute thread, couldn't create worker thread (position)");
                                QuitProgram(true);
@@ -149,17 +188,13 @@ void * Compute_Thread(void * arg)
                        }       
                }
 
-               //Wait for positions to be computed
-               Barrier_Wait(&position_barrier);
-
-
-               //Update number of steps computed
-               universe.steps += 1;
-
                
+               for (unsigned i = 0; i < options.num_threads; ++i)
+                       pthread_join(worker_thread[i], NULL);
 
-               
 
+               StepFunction(s); // Execute single threaded stuff
+               
 
        }
        QuitProgram(false);
@@ -172,34 +207,39 @@ void * Compute_Thread(void * arg)
  * @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
+ *
+ *     This originally seemed like a good place to put the code now in StepFunction(), since only one thread runs this
+ *     But then I realised that the graphics might be disabled, 
+ *             and there was no point having a thread that only existed to call that code.
+ *
+ *     So I changed it to the horrible solution that I currently have.
  */
 void BeforeDraw()
 {
-       if (options.verbosity != 0 && universe.steps % options.verbosity == 0)
-               DisplayStatistics();
+       
        //printf("BEFORE DRAW\n");
        if (!options.pedantic_graphics)
                return;
        
-       Barrier_Wait(&position_barrier);
-
-
-       Barrier_Enter(&graphics_barrier);       
+       //printf("Graphics thread waits on position barrier\n");
+       Barrier_Wait(&position_barrier);        
+       //printf("\tGraphics thread wakes up\n");
+       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
+ *     When --pedantic-graphics is supplied, will signal computation threads that drawing is finished
  *             So that positions can be safely altered
  *     Otherwise does nothing
  */
 void AfterDraw()
 {
-       universe.steps += 1;
+       //universe.steps += 1;
        if (!options.pedantic_graphics)
                return;
-       Barrier_Leave(&graphics_barrier);
+       Barrier_Join(&graphics_barrier);
        
 }
 
@@ -207,83 +247,67 @@ void AfterDraw()
 
 /**
  * @function Worker_Thread
- * @purpose Thread - Calculate stuff
+ * @purpose Thread - A self contained worker thread to compute a particular sub system of bodies
+ *
+ * This is the "smart" way to do it, because threads are only created once, and compute both force and position.
+ * The greatest difficulty with pthreads is getting a *single* thread from the team to execute certain code
+ *     (ie: The stuff in StepFunction()).
+ * With the "continuously respawning threads of stupidity" approach, 
+ * because there is one "master" thread (not necessarilly the main thread... don't get confused now)
+ *     to keep respawning the workers, the single threaded code can just be executed in the master thread.
+ *
+ * With this approach, I have created a hacky solution so that the *last* thread to leave the position barrier gets to call StepFunction.
+ *     
  */
 void * Worker_Thread(void * arg)
 {
-       System * s = (System*)(arg);
-
-
-       pthread_t * nested_workers = NULL;
-       System_ForcePair * system_pairs = NULL;
-       System * nested_position = NULL;
-       
-       Barrier nested_barrier; 
-       Barrier_Init(&nested_barrier);
-
-       printf("options.nested_threads == %d\n", (int)(options.nested_threads));
+       System * s = (System*)(arg); // This is mainly to save typing the RHS a lot of times
 
-       if (options.nested_threads != 1)        
+       // Each thread runs until the whole program is supposed to end
+       while (!ExitCondition()) 
        {
+               
+       
+               System_Forces(s, &universe); // Each thread computes the forces for its share of bodies
 
-               system_pairs = (System_ForcePair*)(calloc(options.nested_threads, sizeof(System_ForcePair)));
-               if (system_pairs == NULL) // Handle tedious error cases
-               {
-                       perror("Couldn't allocate array of system pairs");
-                       QuitProgram(true);
-                       pthread_exit(NULL);
-               }
-               nested_workers = Allocate_Threads(options.nested_threads);
-               nested_position = 
+               // Do not confuse with "Barrier_Wait".
+               // Barrier_Wait does not affect the barrier; it just waits for it
+               // Barrier_Join actively updates the state of the barrier, and wakes up sleeping threads if required.
 
-               for (unsigned i = 0; i < options.nested_threads; ++i)
-               {
-                       system_pairs[i].A = s;
-                       system_pairs[i].B = nested_sub_system+i;
-               }
-       }
+               Barrier_Join(&force_barrier); // All threads must reach here before moving on.
+               if (ExitCondition()) return NULL;
 
-       while (!ExitCondition())
-       {
                
-               if (options.nested_threads == 1)
-               {
-                       Barrier_Enter(&force_barrier);
-                       System_Forces(s, &universe);
-                       Barrier_Leave(&force_barrier);
-               }
-               else
-               {
-                       for (unsigned i = 0; i < options.nested_threads; ++i)
-                       {
-                               if (pthread_create(nested_workers+i, &attr, Force_Thread, (void*)(system_pairs+i)) != 0)
-                               {
-                                       perror("In worker thread, couldn't create nested worker thread (force)");
-                                       QuitProgram(true);
-                                       free(nested_workers);
-                                       pthread_exit(NULL);                                     
-                               }       
-                       }
-               }
-               //printf("Computed forces for %p\n", arg);
-               Barrier_Wait(&force_barrier);
+               //fprintf(stderr,"Thread %p - force barrier finished\n", arg);
                //printf("Computed ALL forces\n");
 
+       
+               // If required, wait for the graphics to finish drawing stuff
                if (options.draw_graphics && options.pedantic_graphics)
+               {
+                       //printf("Worker %p waits on graphics barrier\n", arg);
                        Barrier_Wait(&graphics_barrier);
+                       //printf("\tWorker %p wakes up after graphics barrier\n", arg);
+                       if (ExitCondition()) return NULL;
+               }
+               
 
-               Barrier_Enter(&position_barrier);
                
-               System_Positions(s);
+               Barrier_Enter(&position_barrier);
+               System_Positions(s); // Each thread updates the positions for its share of bodies
+
 
-               Barrier_Leave(&position_barrier);
-               //printf("Computed positions for %p\n", arg);
-               Barrier_Wait(&position_barrier);
-               //printf("Computed ALL positions\n");
+               // Barrier_JoinCall behaves in the same way as Barrier_Join, except the *last* thread 
+               //      (ie: the one that wakes up the others) also calls the function with arguments given.
+               Barrier_JoinCall(&position_barrier, StepFunction, (void*)(&universe));
+               if (ExitCondition()) return NULL;
+               //Barrier_Join(&position_barrier);
+
+               // All threads have computed positions, and *one* thread calls StepFunction()
+               
        }
-       printf("Worker thread exits\n");
-       QuitProgram(false);
-       pthread_exit(NULL);
+       QuitProgram(false); // Set the run state of the program
+       return NULL;
 }
 
 #endif //PERSISTENT_THREADS
@@ -295,12 +319,11 @@ void * Worker_Thread(void * arg)
  */
 void * Force_Thread(void * s)
 {
-       System_ForcePair * pair = (System_ForcePair*)s;
-       Barrier_Enter(&force_barrier);
+       //System_ForcePair * pair = (System_ForcePair*)s;
 
-       System_Forces(pair->A, pair->B); //Simple wrapper
-
-       Barrier_Leave(&force_barrier);
+       
+       System_Forces(s, &universe); //Simple wrapper
+       //printf("Force_Thread waits\n");
 
        return NULL;
 }
@@ -313,118 +336,129 @@ void * Force_Thread(void * s)
  */
 void * Position_Thread(void * s)
 {
-       Barrier_Enter(&position_barrier);
+
        System_Positions((System*)s); // Simple wrapper
-       Barrier_Leave(&position_barrier);
+       Barrier_Join(&position_barrier); // This needed so that graphics will wait
+
        return NULL;
 }      
 
 /**
  * @function QuitProgram
- * @purpose This function can either be called by the main thread in order to signal other threads
- *             that it wants to exit. The main thread then calls pthread_join before exiting.
- *     It can also be called by a child thread to request the main thread to exit.
- *     It is only used this way if there is an unrecovarable error (ie: Can't allocate memory in a child thread)
+ * @purpose This function can be called in any thread to signal all threads to exit
+ *     Repeated calls to this function have no effect
+ *
+ *     All threads periodically call ExitCondition(), which will return true if the program should exit.
+ *             One (not the only way) to return true is if this function has been called.
+ *     Threads will call this function if they detect ExitCondition() is true. Only the first call has any effect.
  */
-void QuitProgram(bool error)
+inline void QuitProgram(bool error)
 {
-       if (runstate == QUIT || runstate == QUIT_ERROR)
-               return; //Don't do anything if already quitting
+       //If already quitting, don't do anything
+       if (runstate == QUIT || runstate == QUIT_ERROR) 
+               return; 
+
+       
+
+       // set the runstate (checked in ExitCondition())
+
        pthread_mutex_lock(&mutex_runstate); // aquire mutex
-       if (error) // set the runstate
-               runstate = QUIT_ERROR;
+       if (error) 
+               runstate = QUIT_ERROR; // Program is exiting due to an error
        else
-               runstate = QUIT;
+               runstate = QUIT; // Program is exiting naturally
        pthread_mutex_unlock(&mutex_runstate); //release mutex
+
+       
 }
 
 /**
  * @function Thread_Cleanup
- * @purpose Will be called in the main thread when exit() is called
- *     Automatically tells all other threads to quit (if they haven't already been told) 
+ * @purpose Will be called in the *main* thread when exit() is called
+ *     Ensures working threads will exit, and waits for them to finish.
  *     Then waits for them to finish.
  *     Also frees memory associated with the worker threads.   
  */
 void Thread_Cleanup(void)
 {
-       if (runstate == RUN) // If this is true, as far as child threads are concerned, the simulation is still running
-               QuitProgram(false); // So call QuitProgram which will set runstate, and cause child threads to exit
-       pthread_join(compute_thread, NULL);
-       free(worker_thread);
-       free(sub_system);
+       
+
+       // Threads recheck the exit condition whenever they leave a barrier.
+       // These calls will stop any threads waiting forever in a barrier for threads that exited before getting to the barrier.
+       Barrier_ForceExit(&force_barrier); 
+       Barrier_ForceExit(&position_barrier);
+
+
+       if (options.draw_graphics) // If the graphics are enabled...
+       {
+               // Then there is a computation thread, since graphics are done in the main thread
+               pthread_join(compute_thread, NULL); 
+       }
+
+       #ifdef PERSISTENT_THREADS
+               for (unsigned i = 0; i < options.num_threads-1; ++i)
+               {
+                       pthread_join(worker_thread[i], NULL);
+               }
+       #else 
+               // All other worker threads (if they were spawned) are terminated in Compute_Thread
+       #endif //PERSISTENT_THREADS
+
+       // Scary memory management here.
+       if (worker_thread != NULL)
+               free(worker_thread);
+       if (sub_system != NULL)
+               free(sub_system);
+       worker_thread = NULL;
+       sub_system = NULL;
+
 }
 
 
 /**
  * @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
+ *     Replaces the single-threaded macro that does nothing, and sets up the graphics and computation threads
  * @param argc - Number of arguments - Passed to Graphics_Run if needed
  * @param argv - Argument strings - Passed to Graphics_Run if needed
  */
 void Simulation_Run(int argc, char ** argv)
 {
        atexit(Thread_Cleanup);
-
-       Barrier_Init(&force_barrier);
-       Barrier_Init(&position_barrier);
-       Barrier_Init(&graphics_barrier);
-
-       
-       if (options.draw_graphics)
+       if (options.draw_graphics) // The graphics are enabled
        {
-               // The graphics are enabled, so create a thread to do computations
-               // Graphics are done in the main loop
-               //printf("Graphics are enabled\n");
-               #ifdef PERSISTENT_THREADS
-               Compute_Thread((void*)(&universe));
-               #else
+               // I have chosen to do graphics in the main thread in this case.
+               // A *single* seperate thread is spawned here to do computations.
+               // This computation thread will spawn any additional worker threads required.
                if (pthread_create(&compute_thread, NULL, Compute_Thread, (void*)&universe) != 0)
                {
                        perror("Error creating compute thread");
                        exit(EXIT_FAILURE);
                }
-               #endif //PERSISTENT_THREADS
-               //printf("Run compute thread\n");
-               Graphics_Run(argc, argv);
-       }
-       else
-       
-               Compute_Thread((void*)(&universe)); // Graphics are disabled, so do computations in the main thread
-}
 
+               // This is run in the main thread
+               // It is effectively the graphics initialisation, followed by the glut loop
+               Graphics_Run(argc, argv);
 
+               // The main thread reaches here after leaving the glut loop when ExitCondition() returns true.
 
-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));
-}
+               QuitProgram(false);
 
-void Barrier_Leave(Barrier * b)
-{
-       pthread_mutex_lock(&(b->mutex));
-       b->threads_busy -= 1;
-       if (b->threads_busy == 0)
+               exit(EXIT_SUCCESS); // This is the main thread; use exit()
+               
+       }
+       else //The graphics are disabled
        {
-               pthread_cond_signal(&(b->threads_done_cv));
+               // If graphics are disabled, there is no point spawning an extra thread.
+               // In this case, the *main* thread starts computations.
+               // Note that it will probably spawn additional worker threads (unless options.num_threads <= 1)
+               Compute_Thread((void*)(&universe));
+               QuitProgram(false);
+               exit(EXIT_SUCCESS);
        }
-       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));
-}
+
 
 /**
  * @function Split_System
@@ -478,3 +512,25 @@ pthread_t * Allocate_Threads(unsigned n)
        }
        return result;
 }
+
+/**
+ * @function StepFunction
+ * @purpose Helper to perform stuff in a single thread every step, after position computations are done
+ *     The reason this has void* all over the place is so that I can pass the function pointer (to horrible dragons and fiendish demons).
+ * @param arg - Can be cast to System* for which steps are to be updated
+ *     Will always be (void*)(&universe). But I have been brainwashed into the "global variables are baaaaad" philosophy.
+ * @returns arg
+ */
+void * StepFunction(void * arg)
+{
+       //fprintf(stderr, "StepFunction called\n");
+       System * s = (System*)(arg);
+       s->steps += 1; //Increment number of steps computed
+
+       if (options.verbosity != 0 && s->steps % options.verbosity == 1)
+               DisplayStatistics();
+
+
+       return arg;
+}
+
index f2de022..9948e52 100644 (file)
@@ -4,13 +4,14 @@
 
 #include "../single-thread/nbody.h" //Use original simulation code
 #include <pthread.h>
+#include "barrier.h" // Barriers
 
 #undef SINGLE_THREADED
 #define PTHREADED
 
 #define DEFAULT_WORKING_THREADS 2 
 
-#define PERSISTENT_THREADS //If defined, threads will not be continually destroyed and then respawned
+//#define PERSISTENT_THREADS //If defined, threads will not be continually destroyed and then respawned
 
 
 
@@ -29,6 +30,8 @@ void AfterDraw();
 void * Compute_Thread(void * system); //Thread - Continuously perform computations for a System of bodies. May spawn additional worker threads.
 
 
+
+
 System * Split_System(System * s, unsigned n); // Splits one system into a number of other systems, returns an array of size n
 pthread_t * Allocate_Threads(unsigned n); // Allocates space for threads - handles errors
 
@@ -40,33 +43,8 @@ void * Position_Thread(void * system); //Thread - Compute positions for all obje
 
 void Thread_Cleanup(void); //Called at program exit to safely join computation thread
 
-/**
- * Structure to represent a barrier for multiple threads
- * @param mutex - Mutex around the counter
- * @param busy - Counter of threads within the barrier
- * @param threads_done_cv - Condition to wake up threads waiting on barrier once all working threads have left it
- */
-typedef struct
-{
-       pthread_mutex_t mutex;
-       unsigned threads_busy; // Counter of threads which are busy
-       pthread_cond_t threads_done_cv;
-} Barrier;
-
-/**
- * Structure to represent a pair of Systems; passed to Force_Thread
- * @param A - System to calculate forces for
- * @param B - System causing forces on System A
- */
-typedef struct
-{
-       System * A;
-       System * B;
-} System_ForcePair;
-
-void Barrier_Init(Barrier * b);
-void Barrier_Enter(Barrier * b);
-void Barrier_Leave(Barrier * b);
-void Barrier_Wait(Barrier * b);
+
+
+void * StepFunction(void * s); //Function called at the end of every step
 
 #endif //_NBODY_MTHREAD_H
diff --git a/course/semester2/pprog/assignment1/mthread/test_barrier.c b/course/semester2/pprog/assignment1/mthread/test_barrier.c
new file mode 100644 (file)
index 0000000..d2efebb
--- /dev/null
@@ -0,0 +1,90 @@
+#include "barrier.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#include "barrier.c"
+
+/**
+ * @file test_barrier.c
+ * @purpose Test the barrier!
+ * @author Sam Moore (20503628) 2012
+ */
+
+// Compile with gcc --std=c99 -o test_barrier test_barrier.c -lpthread
+// Run with ./test_barrier
+// Expected output:
+//     All worker threads print "Hello" before all worker threads print "Goodbye"
+//     At any point, the "Boss" reports that it is waiting for "Hello" to be said, and goes to sleep.
+//     At some point whilst worker threads say "Goodbye", the "Boss" wakes up and reports that "Hello" has been said.
+//     The Boss should *never* report that "Hello" has been said until all workers say "Hello". 
+//     But it may report that it is waiting for "Hello" to be said at any time.
+//     Confused yet? I am, and I wrote this!
+//     NOTE: Two barriers are required to prevent deadlocking!
+
+
+pthread_mutex_t mutex_printf; // Printing is not thread safe! Mutex needed!
+Barrier worker_barrier[2];
+
+void * Worker(void * args)
+{
+       
+       //while (true) // Comment out to only test one step
+       {
+               Barrier_Enter(&worker_barrier[0]); //  Workers are now printing "Hello"
+
+               pthread_mutex_lock(&mutex_printf);
+               printf("Worker %p: Hello\n", (args)); // Only one thread can print at a time
+               pthread_mutex_unlock(&mutex_printf);
+
+               sleep(1);
+               Barrier_Join(&worker_barrier[0]);
+
+               pthread_mutex_lock(&mutex_printf);
+               printf("\tWorker %p: Goodbye\n", (args)); // Only one thread can print at a time
+               pthread_mutex_unlock(&mutex_printf);
+               
+
+               Barrier_Join(&worker_barrier[1]); // If we replace this with:
+               //Barrier_Join(&worker_barrier[0]); 
+               // Then there is (usually) a deadlock, because the first thread to leave the barrier 
+               // immediately reactivates it, and other threads are unable to leave.
+       
+       }
+}
+
+void * Boss(void * args)
+{
+       //while (true)
+       {
+               pthread_mutex_lock(&mutex_printf);
+               printf("Boss is waiting for \"Hello\" to be said.\n", (args)); // Only one thread can print at a time
+               pthread_mutex_unlock(&mutex_printf);            
+               Barrier_Wait(&worker_barrier[0]);
+               pthread_mutex_lock(&mutex_printf);
+               printf("Boss continues now that workers have said \"Hello\".\n", (args)); // Only one thread can print at a time
+               pthread_mutex_unlock(&mutex_printf);
+               sleep(2);
+               
+       }
+}
+
+int main(int argc, char ** argv)
+{
+       #define NUM_THREADS 4
+       pthread_t worker[NUM_THREADS];
+       pthread_t boss;
+       
+       Barrier_Init(&worker_barrier[0], NUM_THREADS); // Initialise barriers with number of contributing threads
+       Barrier_Init(&worker_barrier[1], NUM_THREADS);
+
+       pthread_create(&boss, NULL, Boss, NULL); //Start boss
+
+       for (long i = 0; i < NUM_THREADS; ++i)
+               pthread_create(worker+i, NULL, Worker, (void*)(i)); // Start all threads
+
+       for (int i = 0; i < NUM_THREADS; ++i) // Join threads
+               pthread_join(worker[i], NULL);
+       return 0;
+}
index 91c4bf4..3e3d271 100644 (file)
@@ -14,6 +14,9 @@
 #undef SINGLE_THREADED
 #define OMP_THREADED
 
+
+#define CRAPPY_VERSION
+
 // Replace default macros with thread-safe functions
 #undef Simulation_Run
 void Simulation_Run(int argc, char ** argv);
index 32ca080..c3d55f5 100644 (file)
@@ -14,7 +14,7 @@
 
 #include "graphics.h" //Function declarations
 #include "nbody.h" //The simulation
-#include <GL/freeglut.h>
+
 
 // --- Variable definitions --- //
 double previousTime, eyeTheta, eyePhi, eyeRho;
@@ -99,12 +99,18 @@ void Graphics_Run(int argc, char ** argv)
        gluPerspective(VIEW_ANGLE, (double)WIDTH/(double)HEIGHT, WORLD_NEAR, WORLD_FAR);  
 
        #ifdef FLYING_CAMERA
-       eye.x[0] = 1; eye.x[1] = 0; eye.x[2] = 0;
+       eye.x[0] = 0; eye.x[1] = 0; eye.x[2] = 1;
        eye.y[0] = 0; eye.y[1] = 1; eye.y[2] = 0;
-       eye.z[0] = 0; eye.z[1] = 0; eye.z[2] = 1;
+       eye.z[0] = 1; eye.z[1] = 0; eye.z[2] = 0;
+       eye.p[0] = 0; eye.p[1] = 0; eye.p[2] = -50;
        #endif //FLYING_CAMERA
 
-       glutMainLoop();   
+
+       //This option must be set, or when glut leaves the main loop. the exit(3) function is called... annoying
+       glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
+
+       glutMainLoop();
+       
 }
 
 /**
@@ -135,6 +141,7 @@ void Graphics_Display()
        glLoadIdentity();
 
        #ifdef FLYING_CAMERA
+
        gluLookAt(eye.p[0], eye.p[1], eye.p[2], 
                eye.p[0] + eye.x[0], eye.p[1] + eye.x[1], eye.p[2] + eye.x[2],
                eye.z[0], eye.z[1], eye.z[2]);
@@ -150,6 +157,7 @@ void Graphics_Display()
 
        for (int i = 0; i < universe.N; ++i) 
        {
+       
                Body * b = universe.body+i;
                glColor3f(0.0f, b->mass/1e11*100, 0.0f);
                //glColor3f(1.0f, 0.0f, 0.0f);
@@ -183,37 +191,45 @@ void Graphics_Keyboard(unsigned char theKey, int mouseX, int mouseY)
        #ifdef FLYING_CAMERA
        switch (theKey)
        {
+               // Translate in direction of camera
                case 'W':
                case 'w':
                        for (unsigned i = 0; i < DIMENSIONS; ++i)
                                eye.p[i] += eye.x[i];
                        break;
+               // Translate backwards from camera direction
                case 'S':
                case 's':
                        for (unsigned i = 0; i < DIMENSIONS; ++i)
                                eye.p[i] -= eye.x[i];
                        break;
+               // Translate left from camera direction
                case 'A':
                case 'a':
                        for (unsigned i = 0; i < DIMENSIONS; ++i)
-                               eye.p[i] += eye.y[i];
+                               eye.p[i] -= eye.y[i];
                        break;
+               // Translate right from camera direction
                case 'D':
                case 'd':
                        for (unsigned i = 0; i < DIMENSIONS; ++i)
-                               eye.p[i] -= eye.y[i];
+                               eye.p[i] += eye.y[i];
                        break;
-               case 'Z':
-               case 'z':
+               // Translate up from camera direction
+               case 'Q':
+               case 'q':
                        for (unsigned i = 0; i < DIMENSIONS; ++i)
                                eye.p[i] += eye.z[i];
                        break;
-               case 'C':
-               case 'c':
+               // Translate down from camera direction
+               case 'E':
+               case 'e':
                        for (unsigned i = 0; i < DIMENSIONS; ++i)
                                eye.p[i] -= eye.z[i];
                        break;
 
+               // Rotate camera direction "down"
+               // (pitch control)
                case 'I':
                case 'i':
                {
@@ -227,6 +243,8 @@ void Graphics_Keyboard(unsigned char theKey, int mouseX, int mouseY)
                        }
                        break;
                }
+               // Rotate camera direction "up"
+               // (pitch control)
                case 'K':
                case 'k':
                {
@@ -240,11 +258,12 @@ void Graphics_Keyboard(unsigned char theKey, int mouseX, int mouseY)
                        }
                        break;
                }
-               
+               // Rotate camera direction "left"
+               // (yaw control)
                case 'J':
                case 'j':
                {
-                       float theta = -M_PI/80.0;
+                       float theta = +M_PI/80.0;
                        Camera old = eye;
 
                        for (unsigned i = 0; i < DIMENSIONS; ++i)
@@ -254,11 +273,12 @@ void Graphics_Keyboard(unsigned char theKey, int mouseX, int mouseY)
                        }
                        break;
                }
-
+               // Rotate camera direction "right"
+               // (yaw control)
                case 'L':
                case 'l':
                {
-                       float theta = M_PI/80.0;
+                       float theta = -M_PI/80.0;
                        Camera old = eye;
 
                        for (unsigned i = 0; i < DIMENSIONS; ++i)
@@ -268,11 +288,12 @@ void Graphics_Keyboard(unsigned char theKey, int mouseX, int mouseY)
                        }
                        break;
                }
-
-               case 'Q':
-               case 'q':
+               // Rotate camera direction CCW about its axis
+               // (roll control)
+               case 'U':
+               case 'u':
                {
-                       float theta = M_PI/80.0;
+                       float theta = -M_PI/80.0;
                        Camera old = eye;
 
                        for (unsigned i = 0; i < DIMENSIONS; ++i)
@@ -282,10 +303,12 @@ void Graphics_Keyboard(unsigned char theKey, int mouseX, int mouseY)
                        }
                        break;
                }
-               case 'E':
-               case 'e':
+               // Rotate camera direction CW about its axis
+               // (roll control)
+               case 'O':
+               case 'o':
                {
-                       float theta = -M_PI/80.0;
+                       float theta = +M_PI/80.0;
                        Camera old = eye;
 
                        for (unsigned i = 0; i < DIMENSIONS; ++i)
@@ -297,31 +320,17 @@ void Graphics_Keyboard(unsigned char theKey, int mouseX, int mouseY)
                }       
        }
 
-       /*
-       float x = 0; float y = 0; float z = 0;
-       for (unsigned i = 0; i < DIMENSIONS; ++i)
-       {
-               x += eye.x[i] * eye.x[i];
-               y += eye.y[i] * eye.y[i];
-               z += eye.z[i] * eye.z[i];
-       }
-       for (unsigned i = 0; i < DIMENSIONS; ++i)
-       {
-               eye.x[i] /= sqrt(x);
-               eye.y[i] /= sqrt(y);
-               eye.z[i] /= sqrt(z);
-       }
-       */
+       /*  Code used for debugging the camera
        system("clear");
        printf("Camera status:\n");
        printf("Position: %f %f %f\n", eye.p[0], eye.p[1], eye.p[2]);
        printf("x: %f %f %f (%f)\n", eye.x[0], eye.x[1], eye.x[2], sqrt(eye.x[0]*eye.x[0] + eye.x[1]*eye.x[1] + eye.x[2]*eye.x[2]));
        printf("y: %f %f %f (%f)\n", eye.y[0], eye.y[1], eye.y[2], sqrt(eye.y[0]*eye.y[0] + eye.y[1]*eye.y[1] + eye.y[2]*eye.y[2]));
        printf("z: %f %f %f (%f)\n", eye.z[0], eye.z[1], eye.z[2], sqrt(eye.z[0]*eye.z[0] + eye.z[1]*eye.z[1] + eye.z[2]*eye.z[2]));
-
-       #else
-       
+       */
+       #else // The original view code
        
+               // I like how 'I' is used three times.
 
                if (theKey == 'i' || theKey == 'I') {
                        eyePhi -= M_PI / 20;
index 97ccf47..24a7a33 100644 (file)
@@ -9,7 +9,14 @@
  */
 
 #include <GL/gl.h>
+
+#define GLUT
+//#define SDL
+
+#ifdef GLUT
 #include <GL/glut.h>
+#include <GL/freeglut.h>
+#endif //GLUT
 
 #include "nbody.h"
 
index e9ccb41..e55c75f 100644 (file)
@@ -21,11 +21,12 @@ Options options; // global variable declared in "nbody.h" - Options passed to pr
 
 // --- Function forward declarations --- //
 void HandleArguments(int argc, char ** argv); //Interprets program arguments and sets up the "options" variable
-unsigned UnsignedArgument(unsigned * i, int argc, char ** argv, unsigned * store, unsigned * store2); //Helper function to get switch value for unsigned
+unsigned IntegerArgument(unsigned * i, int argc, char ** argv, int * store, int * store2); //Helper function to get integer switch
 void FloatArgument(unsigned * i, int argc, char ** argv, float * store); //Helper function to get switch value for float
 void DisplayStatistics(); //Called on exit of program, displays information about computation time, steps computed, etc
 void Interrupt(int dummy); // Interrupt handler function, called when SIGINT (Ctrl-C) is sent to program
 
+
 // --- Function implementations --- //
 
 /**
@@ -43,8 +44,8 @@ int main(int argc, char** argv)
        options.output = NULL;
        options.num_threads = 0;
        options.nested_threads = 0;
-       options.num_steps = 0;
-       options.timeout = 0;
+       options.num_steps = -1; // Negative values => simulation runs forever unless otherwise specified
+       options.timeout = -1;
        options.draw_graphics = true;
        options.pedantic_graphics = false;
        options.print_positions = false;
@@ -70,6 +71,8 @@ int main(int argc, char** argv)
                exit(EXIT_FAILURE);
        }
 
+
+
        signal(SIGINT, Interrupt); //Handle SIGINT signals
        atexit(Universe_Cleanup); //On exit, cleanup universe (and write positions of bodies to file if supplied).
        atexit(DisplayStatistics); //On exit, print information about the computations done
@@ -85,8 +88,10 @@ int main(int argc, char** argv)
        options.start_clock = clock(); //Get CPU cycles executed before simulation starts
        Simulation_Run(argc, argv); // Start the simulation
        
-       //printf("Got here!\n");
-       exit(EXIT_FAILURE); //Should never get to this line
+       //printf("Main thread done!\n");
+       
+       //pthread_exit(NULL);
+       exit(EXIT_SUCCESS); //Should never get to this line
 }
 
 
@@ -119,34 +124,50 @@ void HandleArguments(int argc, char ** argv)
                switch (argv[i][1]) //The argument is a switch if we get here
                {
                        case 'n': //Number of threads switch
-                               UnsignedArgument(&i, argc, argv, &(options.num_threads), &(options.nested_threads));
+                               IntegerArgument(&i, argc, argv, &(options.num_threads), &(options.nested_threads));
                                #ifdef SINGLE_THREADED
-                               fprintf(stderr, "Warning: -%c switch has no effect in the single-threaded program.\n", argv[i][1]);
+                               fprintf(stderr, "Warning: -%c switch has no effect in the single-threaded program.\n", 'n');
                                
+                               #else
+                               if (options.num_threads <= 0)
+                               {
+                                       fprintf(stderr, "Require at least one thread (-%c %s is invalid).\n",'n', argv[i]);
+                                       exit(EXIT_FAILURE);
+                               }
                                #endif //SINGLE_THREADED
                                break;
                        case 's': //Number of steps switch
-                               UnsignedArgument(&i, argc, argv, &(options.num_steps), NULL);
+                               IntegerArgument(&i, argc, argv, &(options.num_steps), NULL);
+                               if (options.num_steps < 0)
+                               {
+                                       fprintf(stderr, "Require zero or more steps to run (-%c %s is invalid).\n", 's', argv[i]);
+                                       exit(EXIT_FAILURE);
+                               }
                                break;
                        case 't': //Timeout switch (in seconds)
-                               UnsignedArgument(&i, argc, argv, &(options.timeout), NULL);
+                               IntegerArgument(&i, argc, argv, &(options.timeout), NULL);
+                               if (options.timeout < 0)
+                               {
+                                       fprintf(stderr, "Require a timeout greater or equal to zero (-%c %s is invalid).", 't', argv[i]);
+                                       exit(EXIT_FAILURE);
+                               }                               
                                break;
                        case 'g': //Graphics switch
                                options.draw_graphics = !options.draw_graphics;
                                break;
                        case 'v': //Verbosity switch
-                               UnsignedArgument(&i, argc, argv, &(options.verbosity), NULL);
+                               IntegerArgument(&i, argc, argv, &(options.verbosity), NULL);
                                break;
 
                        case '-':
-                               if (strcmp(argv[i]+1, "pedantic-graphics"))
+                               if (strcmp(argv[i]+2, "pedantic-graphics") == 0)
                                {
                                        options.pedantic_graphics = true;
                                        #ifdef SINGLE_THREADED
                                        fprintf(stderr, "Warning: %s switch has no effect in the single threaded program.\n", argv[i]);
                                        #endif //SINGLE_THREADED
                                }
-                               else if (strcmp(argv[i]+1, "fast-graphics"))
+                               else if (strcmp(argv[i]+2, "fast-graphics") == 0)
                                {
                                        options.pedantic_graphics = false;
                                        #ifdef SINGLE_THREADED
@@ -156,7 +177,7 @@ void HandleArguments(int argc, char ** argv)
                                }
                                else
                                {
-                                       fprintf(stderr, "Unrecognised switch -%s\n", argv[i]);
+                                       fprintf(stderr, "Unrecognised switch %s\n", argv[i]);
                                        exit(EXIT_FAILURE);
                                }
                                break;
@@ -172,16 +193,16 @@ void HandleArguments(int argc, char ** argv)
 }
 
 /**
- * @function UnsignedArgument
- * @purpose Helper function to get an unsigned integer following a argument switch
+ * @function IntegerArgument
+ * @purpose Helper function to get up to two integers following a argument switch, seperated by ':'
  * @param i - position in the argument array, will be updated after this function
  * @param argc - number of arguments
  * @param argv - argument strings
  * @param store - pointer to unsigned to store result in
- * @param store2 - pointer to second unsigned
+ * @param store2 - pointer to second integer (set to NULL if there is only one integer)
  * @returns 1 if store was filled, 2 if both store1 and store2 were filled
  */
-unsigned UnsignedArgument(unsigned * i, int argc, char ** argv, unsigned * store, unsigned * store2)
+unsigned IntegerArgument(unsigned * i, int argc, char ** argv, int * store, int * store2)
 {
        if (*i >= argc-1)
        {
@@ -209,12 +230,12 @@ unsigned UnsignedArgument(unsigned * i, int argc, char ** argv, unsigned * store
        }
        
        int val = atoi(argv[*i+1]);
-       if (val <= 0)
+       if (val <= 0 && strcmp("0", argv[*i+1]) != 0)
        {
                fprintf(stderr,"Supply a positive integer for the -%c switch. %s is invalid.\n", argv[*i][1], argv[*i+1]);
                exit(EXIT_FAILURE);
        }
-       *store = (unsigned)val;
+       *store = val;
        *i += 1;
 
        return (seperator == NULL) ? 1 : 2;
@@ -256,3 +277,4 @@ void Interrupt(int dummy)
        QuitProgram(false);
 }
 
+
index e4b4bc7..ee510ed 100644 (file)
@@ -23,10 +23,12 @@ RUNSTATE runstate = RUN;
  * @param a - Body to print'
  * @param out - FILE* to print to
  */
-void Body_Print(Body * a, FILE * out)
+inline void Body_Print(Body * a, FILE * out)
 {
-       fprintf(out,"Body %p M=%f X=%f Y=%f Z=%f Fx=%f Fy=%f Fz=%f Vx=%f Vy=%f Vz=%f\n", 
-           (void*)a, a->mass, a->x[0], a->x[1], a->x[2], a->F[0], a->F[1], a->F[2], a->v[0], a->v[1], a->v[2]);
+       //fprintf(out,"Body %p M=%f X=%f Y=%f Z=%f Fx=%f Fy=%f Fz=%f Vx=%f Vy=%f Vz=%f\n", 
+           //(void*)a, a->mass, a->x[0], a->x[1], a->x[2], a->F[0], a->F[1], a->F[2], a->v[0], a->v[1], a->v[2]);
+       fprintf(out, "%f %f %f %f %f %f %f %f %f %f\n", 
+               a->mass, a->x[0], a->x[1], a->x[2], a->v[0], a->v[1], a->v[2], a->F[0], a->F[1], a->F[2]);
 }
 
 /**
@@ -35,7 +37,7 @@ void Body_Print(Body * a, FILE * out)
  * @param a - The Body
  * @param b - The System
  */
-void Body_Force(Body * a, System * s) 
+inline void Body_Force(Body * a, System * s) 
 {
        double distance;
        double con;
@@ -67,7 +69,7 @@ void Body_Force(Body * a, System * s)
  * @purpose Compute velocity of a body
  * @param a - The Body
  */
-void Body_Velocity(Body * a) 
+inline void Body_Velocity(Body * a) 
 {
        for (unsigned i = 0; i < DIMENSIONS; ++i)
                a->v[i] += a->F[i] / a->mass * DELTA_T;
@@ -78,7 +80,7 @@ void Body_Velocity(Body * a)
  * @purpose Compute position of a body
  * @param a - The Body
  */
-void Body_Position(Body * a) 
+inline void Body_Position(Body * a) 
 {
        for (unsigned i = 0; i < DIMENSIONS; ++i)
                a->x[i] += a->v[i] * DELTA_T;
@@ -89,7 +91,7 @@ void Body_Position(Body * a)
  * @purpose Compute forces and then positions for bodies in System
  * NOTE: Only used in the single threaded version of the program
  */
-void System_Compute(System * s)
+inline void System_Compute(System * s)
 {
        System_Forces(s, s);
        System_Positions(s);
@@ -103,7 +105,7 @@ void System_Compute(System * s)
  *      In multi threaded version, called with s2 == &universe, but s1 is constructed for each thread
  *             In nested multi-threaded version, s2 is constructed for the nested threads as well
  */
-void System_Forces(System * s1, System * s2) 
+inline void System_Forces(System * s1, System * s2) 
 {
        //printf("Compute forces for %p - %d bodies...\n", (void*)s1, s1->N);
        for (unsigned i = 0; i < s1->N; ++i)
@@ -119,13 +121,13 @@ void System_Forces(System * s1, System * s2)
  * @purpose Compute positions of all objects in System
  * @param s - The system
  */
-void System_Positions(System * s)
+inline void System_Positions(System * s)
 {
        //printf("Compute positions for %p - %d bodies...\n", (void*)s, s->N);
        for (unsigned i = 0; i < s->N; ++i)
                Body_Position(s->body+i);
        //printf("Compute positions for %p - Done!\n", (void*)s);
-       s->steps += 1;
+       
 }
 
 
@@ -137,7 +139,7 @@ void System_Positions(System * s)
  * @param s - The System
  * @param fileName - The input file
  */
-void System_Init(System * s, const char *fileName) 
+inline void System_Init(System * s, const char *fileName) 
 {
        char line[LINE_SIZE];
        char * token;
@@ -155,19 +157,23 @@ void System_Init(System * s, const char *fileName)
                if (fgets(line, LINE_SIZE, file) != NULL)
                {
                        Body * a = s->body+i;
-                       token = strtok(line, ",; ");
+                       token = strtok(line, ",; \t");
                        a->mass = atof(token);
                        
                        for (unsigned j = 0; j < DIMENSIONS; ++j)
                        {
-                               token = strtok(NULL, ",; ");
+                               token = strtok(NULL, ",; \t");
                                a->x[j] = atof(token);
                        }
                        for (unsigned j = 0; j < DIMENSIONS; ++j)
                        {
-                               token = strtok(NULL, ",; ");
+                               token = strtok(NULL, ",; \t");
                                a->v[j] = atof(token);
                        }
+
+                       // Ignore any additional tokens
+                       while (token != NULL)
+                               token = strtok(NULL, ",; \t");
                        //Body_Print(a);
                }
        }
@@ -182,19 +188,30 @@ void System_Init(System * s, const char *fileName)
  * @purpose Cleans up the universe by freeing all memory
  *      Also prints the bodies in the universe to a file specified in "options" if required.
  */
-void Universe_Cleanup()
+inline void Universe_Cleanup()
 {
+       //fprintf(stderr, "Called Universe_Cleanup()\n");
        if (options.output != NULL) // An output file was specified...
        {
-               FILE * save = fopen(options.output, "w");
+               FILE * save = NULL;
+               if (strcmp("stdout", options.output) == 0)
+                       save = stdout;
+               else if (strcmp("stderr", options.output) == 0)
+                       save = stderr;
+               else
+                       save = fopen(options.output, "w");
                if (save == NULL)
                        perror("Couldn't save universe to file.");
                else
                {
-                       fprintf(save, "# Final field:\n");
+                       // Print the output in the same format as the initial field file
+                       fprintf(save, "%u\n", universe.N);
+                               
                        for (unsigned i = 0; i < universe.N; ++i) // Print all bodies to the file
                        {
-                               Body_Print(universe.body+i, save);
+                               Body_Print(universe.body+i, save);                      
+                               
+                                                               
                        }
                        fclose(save);
                }
@@ -212,7 +229,7 @@ void Universe_Cleanup()
  *     - Clock cycles executed 
  *     - Equivelant time for a single thread to execute same number of clock cycles
  */
-void DisplayStatistics()
+inline void DisplayStatistics()
 {
        clock_t end = clock();
        struct timeval end_time;
@@ -232,11 +249,16 @@ void DisplayStatistics()
  * @purpose Helper to check whether the program is supposed to exit
  *     Does not check for user triggered quit
  *     Checks for either a timeout, or computation of the required number of steps
+ * 
+ * The reason timeouts are integers is because this function is called a lot in lots of threads
+ *     So using floats (and working out microseconds every time) is expensive
+ *
  */
-bool ExitCondition(void)
+inline bool ExitCondition(void)
 {
-       bool result = (runstate != RUN || (options.timeout > 0.00 && ((unsigned)(time(NULL) - options.start_time.tv_sec) >= options.timeout))
-               || (options.num_steps > 0 && universe.steps > options.num_steps));
-       //printf("runstate %d\n timeout %d\n steps %d\n", (int)(runstate != RUN), (int)(options.timeout > 0.00 && ((unsigned)(time(NULL) - options.start_time.tv_sec) >= options.timeout)), (int)(options.num_steps > 0 && universe.steps > options.num_steps));
+       bool result = (runstate != RUN || 
+                       (options.num_steps >= 0 && universe.steps >= options.num_steps) ||
+                       (options.timeout >= 0 && ((int)(time(NULL) - options.start_time.tv_sec) >= options.timeout)));
+       //fprintf(stderr,"runstate %d\n timeout %d\n steps %d\n", (int)(runstate != RUN), (int)(options.timeout > 0.00 && ((unsigned)(time(NULL) - options.start_time.tv_sec) >= options.timeout)), (int)(options.num_steps > 0 && universe.steps > options.num_steps));
        return result;
 }
index 4d53f9d..a9bfec4 100644 (file)
@@ -33,9 +33,11 @@ else \
 
 //Macro to be overwritten in multithreaded versions, called before the graphics is allowed to draw anything
 #define BeforeDraw() \
+System_Compute(&universe); \
+universe.steps += 1; \
 if (options.verbosity != 0 && universe.steps % options.verbosity == 0) \
        DisplayStatistics(); \
-System_Compute(&universe);
+
 
 
 
@@ -93,14 +95,14 @@ typedef struct
        const char * input; // initial body field
        const char * output; // file to write final positions / velocities of bodies to
        const char * program; // program name
-       unsigned num_threads; // number of worker threads to spawn (must be greater than 1 for any to be spawned)
-       unsigned nested_threads; // number of threads to nest computations with (must be greater than 1 for any to be spawned)
-       unsigned num_steps; // number of steps to run before stopping (run indefinately if equal to zero)
-       unsigned timeout; // number of seconds to run before stopping (run indefinately if equal to zero)
+       int num_threads; // number of worker threads to spawn (must be greater than 1 for any to be spawned)
+       int nested_threads; // number of threads to nest computations with (must be greater than 1 for any to be spawned)
+       int num_steps; // number of steps to run before stopping (run indefinately if less than zero)
+       int timeout; // number of seconds to run before stopping (run indefinately if less than zero)
        bool draw_graphics; // whether or not to actually draw graphics
        bool pedantic_graphics; // whether the graphics thread will synchronise with the computation thread (true) or just draw as fast as possible (false)
        bool print_positions; // print positions of bodies to stdout on every step
-       unsigned verbosity; // print statistics every number of steps indicated by this variable
+       int verbosity; // print statistics every number of steps indicated by this variable
        clock_t start_clock;  // clock cycles done when simulation starts
        struct timeval start_time; // time at which simulation starts
 } Options;

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