System universe;
pthread_t compute_thread;
-pthread_mutex_t mutex_terminate;
-bool terminate = false;
+pthread_mutex_t mutex_runstate;
+RUNSTATE runstate = RUN;
-void Cleanup_Threads(void); //Cleanup for threads
+void Thread_Cleanup(void)
+{
+ if (runstate == RUN)
+ QuitProgram(false);
+ pthread_join(compute_thread, NULL);
+}
// This is main function. Do not change it.
int main(int argc, char** argv)
numberOfProcessors = atoi(argv[2]);
}
- atexit(Cleanup_Threads);
+ atexit(Thread_Cleanup);
// Create a thread to handle computation loop
Graphics_Run(argc, argv); // Run graphics loop in the main thread
+ printf("Close!\n");
+ //We get to this point when the "close" button is clicked in the graphics window
+ QuitProgram(false);
+ pthread_join(compute_thread, NULL);
}
-/**
- * Function will be called when exit() is called.
- * Will set condition for child threads to terminate, and then join with them.
- * The main thread is responsible for calling exit().
- */
-void Cleanup_Threads()
-{
- printf("Set terminate\n");
- pthread_mutex_lock(&mutex_terminate);
- terminate = true;
- pthread_mutex_unlock(&mutex_terminate);
- pthread_join(compute_thread, NULL);
- pthread_exit(NULL);
-}
#include <time.h>
#include <string.h>
#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
#include "nbody.h"
+//Stuff to do with worker threads
+// The worker threads (if used) are spawned by the computation thread, not the main thread
+// The main thread (which handles initialisation, graphics, user input, cleanup and exit) does not need to know about these.
+#if NUM_THREADS > 1
+
+ pthread_t worker_thread[NUM_THREADS]; //Worker threads responsible for Force and Position updates
+ System sub_system[NUM_THREADS]; //Array of System's used to divide up the main "universe" System for worker threads
+ pthread_mutex_t mutex_workers;
+ pthread_cond_t workers_done_cv;
+ unsigned worker_count;
+#endif
+
/**
* Prints the body on screen
*/
}
/**
- * Main compute function
+ * Compute forces on each object in System s1 due to all bodies in System s2
*/
-void System_Compute(System * s)
+void System_Forces(System * s1, System * s2)
{
-// clock_t start, finish;
-
-// start = clock();
-
- for (unsigned i = 0; i < s->N; ++i)
+ //printf("Compute forces for %p - %d bodies...\n", (void*)s1, s1->N);
+ for (unsigned i = 0; i < s1->N; ++i)
{
- Body_Force(s->body+i, s);
- Body_Velocity(s->body+i);
- Body_Position(s->body+i);
+ Body_Force(s1->body+i, s2);
+ Body_Velocity(s1->body+i);
}
+ //printf("Compute positions for %p - Done!\n", (void*)s1);
+}
+/**
+ * Compute positions of all objects in System
+ */
+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);
}
void Universe_Cleanup()
{
free(universe.body);
- pthread_exit(NULL);
+
}
/**
* Thread - Continuously computes steps for a system of bodies
*/
-void * Compute_Thread(void * s)
+void * Compute_Thread(void * arg)
{
+
+ System * s = (System*)(arg); //cast argument to a System*
+ // First set up the "sub_system" array
+ #if NUM_THREADS > 1
+ unsigned bodies_per_system = (s->N) / NUM_THREADS;
+ unsigned remainder = (s->N) % NUM_THREADS;
+ for (unsigned i = 0; i < NUM_THREADS; ++i)
+ {
+ sub_system[i].body = (s->body)+(i*bodies_per_system);
+ sub_system[i].N = bodies_per_system;
+ if (i == NUM_THREADS - 1)
+ sub_system[i].N += remainder;
+ }
+
+ pthread_attr_t attr; //thread attribute for the workers
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ #endif
while (true)
{
- if (terminate) pthread_exit(NULL);
- System_Compute((System*)s);
+
+ if (runstate != RUN) pthread_exit(NULL); //Check whether the main thread wants to exit
+
+
+
+ #if NUM_THREADS <= 1
+ //No worker threads; do everything in this thread
+ System_Forces(s, s);
+ System_Positions(s);
+ #else
+
+
+
+
+ worker_count = NUM_THREADS; //All threads working
+
+ //Compute forces
+ for (unsigned i = 0; i < NUM_THREADS; ++i)
+ {
+ if (pthread_create(worker_thread+i, &attr, Force_Thread, (void*)(sub_system+i)) != 0)
+ {
+ perror("In compute thread, couldn't create worker thread (force)");
+ QuitProgram(true);
+ pthread_exit(NULL);
+ }
+ }
+
+
+
+ //Wait for forces to be computed
+ pthread_mutex_lock(&mutex_workers);
+ while (worker_count > 0)
+ pthread_cond_wait(&workers_done_cv, &mutex_workers);
+ pthread_mutex_unlock(&mutex_workers);
+
+ worker_count = NUM_THREADS; //All threads working
+
+ //Compute positions
+ for (unsigned i = 0; i < NUM_THREADS; ++i)
+ {
+ if (pthread_create(worker_thread+i, &attr, Position_Thread, (void*)(sub_system+i)) != 0)
+ {
+ perror("In compute thread, couldn't create worker thread (position)");
+ QuitProgram(true);
+ pthread_exit(NULL);
+ }
+ }
+
+ //Wait for positions to be computed
+ pthread_mutex_lock(&mutex_workers);
+ while (worker_count > 0)
+ pthread_cond_wait(&workers_done_cv, &mutex_workers);
+ pthread_mutex_unlock(&mutex_workers);
+
+
+ #endif
+
}
}
+void * Force_Thread(void * s)
+{
+
+ System_Forces((System*)s, &universe);
+ pthread_mutex_lock(&mutex_workers);
+ worker_count -= 1;
+ if (worker_count == 0)
+ {
+ pthread_cond_signal(&workers_done_cv);
+ }
+ pthread_mutex_unlock(&mutex_workers);
+ return NULL;
+}
+
+void * Position_Thread(void * s)
+{
+
+ System_Positions((System*)s);
+ pthread_mutex_lock(&mutex_workers);
+ worker_count -= 1;
+ if (worker_count == 0)
+ {
+ pthread_cond_signal(&workers_done_cv);
+ }
+ pthread_mutex_unlock(&mutex_workers);
+ return NULL;
+}
+
+/**
+ * Child threads can call this to signal the main thread to quit the program
+ * The main thread also uses this to tell child threads that the program is quitting
+ * Note that the function doesn't call exit(), that is done by the main thread
+ */
+void QuitProgram(bool error)
+{
+
+ pthread_mutex_lock(&mutex_runstate);
+ if (error)
+ runstate = QUIT_ERROR;
+ else
+ runstate = QUIT;
+ pthread_mutex_unlock(&mutex_runstate);
+}
* @purpose N-Body simulator: declarations of simulation related parameters
*/
-#define NUM_THREADS 4
+
+#define NUM_THREADS 5 //The number of *worker* threads (not including the computation thread) to use.
+ // If this is 1, the computation thread will not spawn any workers (faster to just do the work itself),
+ // otherwise it will spawn NUM_THREADS workers.
#include <pthread.h>
#include <stdbool.h>
void System_Init(System * s, char * fileName); //Initialise System (array of bodies) from a text file
-void System_Compute(System * system); //Perform a single computation step for a System of bodies
+void System_Forces(System * s1, System * s2); //Compute forces for bodies in s1 due to bodies in s2 (also updates velocities)
+void System_Positions(System * s); //Update positions for bodies in s1
void Universe_Cleanup();
extern System universe; // The main array of bodies; global variable.
*/
extern pthread_t compute_thread; // ID of the thread that runs Compute_Thread. Set in main()
-void * Compute_Thread(void * system); //Thread - Continuously perform computations for a System of bodies
+void * Compute_Thread(void * system); //Thread - Continuously perform computations for a System of bodies. May spawn additional worker threads.
+
+void * Force_Thread(void * system); //Thread - Compute forces for all objects in a system
+void * Position_Thread(void * system); //Thread - Compute positions for all objects in a system
+
+typedef enum {RUN, QUIT, QUIT_ERROR} RUNSTATE;
+extern RUNSTATE runstate;
+extern pthread_mutex_t mutex_runstate; //Mutex around the "runstate" variable
+
+void QuitProgram(bool error);
-extern bool terminate; //Will be set to true by main thread to signal children to terminate. Child threads are responsible for checking.
-extern pthread_mutex_t mutex_terminate; //Mutex around the "terminate" variable
#endif //_NBODY_H