Parallel Programming - Finished OpenMP
[matches/honours.git] / course / semester2 / pprog / assignment1 / mthread / nbody.c~
1 /**
2  * @file nbody.c
3  * @purpose Implentation of multi-threaded N-Body simulator using pthreads
4  * @author Sam Moore (20503628) - 2012
5  */
6
7 #include "nbody.h" // Declarations
8 #include "../single-thread/nbody.c" // Include all functions from the single threaded version
9 #include <assert.h>
10 #include "graphics.h" // For declaration of Graphics_Run only
11 #include "barrier.c"
12
13 // --- Variable declarations --- //
14
15 pthread_t compute_thread; // The thread responsible for computations; it spawns worker threads (* terms and conditions apply)
16         
17 pthread_t * worker_thread = NULL; //Array of worker threads responsible for Force and Position updates
18 System * sub_system = NULL; //Array of Systems used to divide up the main "universe" System for worker threads
19 System * nested_sub_system = NULL; //Array of Systems for division of "universe" for the nested worker threads
20
21 pthread_mutex_t mutex_runstate; // Mutex around the runstate
22
23
24
25
26 Barrier force_barrier; // I laughed at this variable name. A bit sad really.
27 Barrier position_barrier;
28
29
30 Barrier graphics_barrier;
31
32 pthread_mutex_t mutex_threads_running;
33 int threads_running = 0;
34
35 /**
36  * @function Compute_Thread
37  * @purpose Thread - Continuously computes steps for a system of bodies. Seperate from graphics functions.
38  *      Spawns worker threads to divide up computation.
39  * @param arg - Can be cast to the System for which computations are performed 
40  *
41  *      NOTE:
42  *      This will always be (void*)(&universe) where universe is the global System variable that has every Body in it.
43  *      But I don't like global variables. And since the argument had to be passed, I thought I might as well use it.
44  *      That way, when I change "universe" to "solar_system", I only have to change the argument where this is called, not all through it.
45  *      Find and replace? Who uses that!?
46  */
47
48
49 void * Compute_Thread(void * arg)
50 {
51
52         System * s = (System*)(arg); //cast argument to a System*
53
54
55
56         // If no number of threads provided, use the default value, unless someone (me) changed that to a stupid value
57         if (options.num_threads <= 0)
58                 options.num_threads = (DEFAULT_WORKING_THREADS > 1) ? DEFAULT_WORKING_THREADS : 1; // Fear the ternary operator!
59
60         if (options.nested_threads <= 0)
61                 options.nested_threads = 1;
62
63         // Do a sanity check; there is no point spawning more threads than bodies.
64         if (options.num_threads > s->N)
65         {
66                 fprintf(stderr, 
67                         "Warning: Using %u threads instead of %u specified, because there are only %u bodies to simulate!\n",
68                         s->N, options.num_threads, s->N);       
69                 options.num_threads = s->N;
70         }
71
72         // Initialise the barriers ("shields up!")
73         Barrier_Init(&force_barrier, options.num_threads);
74         Barrier_Init(&position_barrier, options.num_threads);
75         Barrier_Init(&graphics_barrier, 1);
76
77         
78         
79         if (options.num_threads > 1) // If we require additional worker threads...
80         {
81                 // Allocate worker threads and sub systems
82                 
83                 sub_system = Split_System(&universe, options.num_threads);
84
85                 if (options.nested_threads > 1)
86                         nested_sub_system = Split_System(&universe, options.nested_threads);
87
88                 #ifdef PERSISTENT_THREADS // Code for the smart way of doing it (spawn threads once, keep them running)
89                 worker_thread = Allocate_Threads(options.num_threads-1);
90                 // Spawn a bunch of worker threads, and let them all do their thing.
91                 // Note the "-1". Because this thread can do work too!
92                 for (unsigned i = 0; i < options.num_threads-1; ++i)
93                 {
94                         if (pthread_create(worker_thread+i, NULL, Worker_Thread, (void*)(sub_system+i)) != 0)
95                         {
96                                 perror("In compute thread, couldn't create worker thread");
97                                 QuitProgram(true);
98                                 pthread_exit(NULL);
99                         }
100                 }
101                 Worker_Thread((void*)(sub_system+options.num_threads-1)); // This thread becomes a worker thread
102                 #else
103                 worker_thread = Allocate_Threads(options.num_threads);
104                 #endif //PERSISTENT_THREADS
105                 
106         }
107         #ifdef PERSISTENT_THREADS
108         else // We only require one worker thread...
109         {
110                 // So just do all computations in this thread
111                 while (!ExitCondition())
112                 {
113                         
114                         System_Forces(s, s);
115                         // If required, wait for graphics to finish drawing
116                         if (options.draw_graphics && options.pedantic_graphics)
117                                 Barrier_Wait(&graphics_barrier);
118                         System_Positions(s);
119                         StepFunction(s);
120
121                         if (options.draw_graphics && options.pedantic_graphics)
122                                 Barrier_ForceExit(&position_barrier); //Make the graphics continue
123                 }
124                 QuitProgram(false);
125                 pthread_exit(NULL);
126         }
127
128         #else // Code for the stupid way of doing it (respawn threads each step)
129                 // (ie: The way I immediately implemented and didn't realise was stupid until someone told me)
130
131
132         
133
134         // Run until we can't run anymore
135         while (!ExitCondition())
136         {
137                 
138
139                 if (options.num_threads <= 1) // If there is only 1 worker thread...
140                 {
141                         // Just do everything in this thread 
142                         System_Forces(s, s);
143                         // If required, wait for graphics to finish drawing
144                         if (options.draw_graphics && options.pedantic_graphics)
145                                 Barrier_Join(&graphics_barrier);
146
147                         System_Positions(s);
148                         StepFunction(s);
149
150                         if (options.draw_graphics && options.pedantic_graphics)
151                                 Barrier_Join(&position_barrier); //Make the graphics continue
152                         continue;
153                 }
154
155                 
156
157                 //Compute forces by spawning threads, each thread gets a sub system
158                 for (unsigned i = 0; i < options.num_threads; ++i)
159                 {
160                         if (pthread_create(worker_thread+i, NULL, Force_Thread, (void*)(sub_system+i)) != 0)
161                         {
162                                 perror("In compute thread, couldn't create worker thread (force)");
163                                 QuitProgram(true);
164                                 pthread_exit(NULL);
165                         }       
166                                 
167                 }
168
169                 for (unsigned i = 0; i < options.num_threads; ++i)
170                         pthread_join(worker_thread[i], NULL);
171
172                 
173                 // If required, wait for graphics to finish drawing
174                 if (options.draw_graphics && options.pedantic_graphics)
175                         Barrier_Wait(&graphics_barrier);
176
177
178                 Barrier_Enter(&position_barrier);
179
180                 //Compute positions by spawning a bunch of threads to do it
181                 for (unsigned i = 0; i < options.num_threads; ++i)
182                 {
183                         if (pthread_create(worker_thread+i, NULL, Position_Thread, (void*)(sub_system+i)) != 0)
184                         {
185                                 perror("In compute thread, couldn't create worker thread (position)");
186                                 QuitProgram(true);
187                                 pthread_exit(NULL);
188                         }       
189                 }
190
191                 
192                 for (unsigned i = 0; i < options.num_threads; ++i)
193                         pthread_join(worker_thread[i], NULL);
194
195
196                 StepFunction(s); // Execute single threaded stuff
197                 
198
199         }
200         QuitProgram(false);
201         #endif //PERSISTENT_THREADS
202         return NULL;
203 }
204
205 /**
206  * @function BeforeDraw
207  * @purpose Called in graphics thread before the draw loop
208  *      When --pedantic-graphics enabled, will wait for position computations to finish before drawing
209  *      Otherwise does nothing
210  *
211  *      This originally seemed like a good place to put the code now in StepFunction(), since only one thread runs this
212  *      But then I realised that the graphics might be disabled, 
213  *              and there was no point having a thread that only existed to call that code.
214  *
215  *      So I changed it to the horrible solution that I currently have.
216  */
217 void BeforeDraw()
218 {
219         
220         //printf("BEFORE DRAW\n");
221         if (!options.pedantic_graphics)
222                 return;
223         
224         //printf("Graphics thread waits on position barrier\n");
225         Barrier_Wait(&position_barrier);        
226         //printf("\tGraphics thread wakes up\n");
227         Barrier_Enter(&graphics_barrier);
228 }
229
230 /**
231  * @function AfterDraw
232  * @purpose Called in graphics thread after the draw loop
233  *      When --pedantic-graphics is supplied, will signal computation threads that drawing is finished
234  *              So that positions can be safely altered
235  *      Otherwise does nothing
236  */
237 void AfterDraw()
238 {
239         //universe.steps += 1;
240         if (!options.pedantic_graphics)
241                 return;
242         Barrier_Join(&graphics_barrier);
243         
244 }
245
246 #ifdef PERSISTENT_THREADS
247
248 /**
249  * @function Worker_Thread
250  * @purpose Thread - A self contained worker thread to compute a particular sub system of bodies
251  *
252  * This is the "smart" way to do it, because threads are only created once, and compute both force and position.
253  * The greatest difficulty with pthreads is getting a *single* thread from the team to execute certain code
254  *      (ie: The stuff in StepFunction()).
255  * With the "continuously respawning threads of stupidity" approach, 
256  * because there is one "master" thread (not necessarilly the main thread... don't get confused now)
257  *      to keep respawning the workers, the single threaded code can just be executed in the master thread.
258  *
259  * With this approach, I have created a hacky solution so that the *last* thread to leave the position barrier gets to call StepFunction.
260  *      
261  */
262 void * Worker_Thread(void * arg)
263 {
264         System * s = (System*)(arg); // This is mainly to save typing the RHS a lot of times
265
266         // Each thread runs until the whole program is supposed to end
267         while (!ExitCondition()) 
268         {
269                 
270         
271                 System_Forces(s, &universe); // Each thread computes the forces for its share of bodies
272
273                 // Do not confuse with "Barrier_Wait".
274                 // Barrier_Wait does not affect the barrier; it just waits for it
275                 // Barrier_Join actively updates the state of the barrier, and wakes up sleeping threads if required.
276
277                 Barrier_Join(&force_barrier); // All threads must reach here before moving on.
278                 if (ExitCondition()) return NULL;
279
280                 
281                 //fprintf(stderr,"Thread %p - force barrier finished\n", arg);
282                 //printf("Computed ALL forces\n");
283
284         
285                 // If required, wait for the graphics to finish drawing stuff
286                 if (options.draw_graphics && options.pedantic_graphics)
287                 {
288                         //printf("Worker %p waits on graphics barrier\n", arg);
289                         Barrier_Wait(&graphics_barrier);
290                         //printf("\tWorker %p wakes up after graphics barrier\n", arg);
291                         if (ExitCondition()) return NULL;
292                 }
293                 
294
295                 
296                 Barrier_Enter(&position_barrier);
297                 System_Positions(s); // Each thread updates the positions for its share of bodies
298
299
300                 // Barrier_JoinCall behaves in the same way as Barrier_Join, except the *last* thread 
301                 //      (ie: the one that wakes up the others) also calls the function with arguments given.
302                 Barrier_JoinCall(&position_barrier, StepFunction, (void*)(&universe));
303                 if (ExitCondition()) return NULL;
304                 //Barrier_Join(&position_barrier);
305
306                 // All threads have computed positions, and *one* thread calls StepFunction()
307                 
308         }
309         QuitProgram(false); // Set the run state of the program
310         return NULL;
311 }
312
313 #endif //PERSISTENT_THREADS
314
315 /**
316  * @function Force_Thread
317  * @purpose Thread - Calculates the forces on objects in a System
318  * @param s - Can be cast to the System*
319  */
320 void * Force_Thread(void * s)
321 {
322         //System_ForcePair * pair = (System_ForcePair*)s;
323
324         
325         System_Forces(s, &universe); //Simple wrapper
326         //printf("Force_Thread waits\n");
327
328         return NULL;
329 }
330
331
332 /**
333  * @function Position_Thread
334  * @purpose Thread - Calculates the positions of objects in a System 
335  * @param s - Can be cast to the System*
336  */
337 void * Position_Thread(void * s)
338 {
339
340         System_Positions((System*)s); // Simple wrapper
341         Barrier_Join(&position_barrier); // This needed so that graphics will wait
342
343         return NULL;
344 }       
345
346 /**
347  * @function QuitProgram
348  * @purpose This function can be called in any thread to signal all threads to exit
349  *      Repeated calls to this function have no effect
350  *
351  *      All threads periodically call ExitCondition(), which will return true if the program should exit.
352  *              One (not the only way) to return true is if this function has been called.
353  *      Threads will call this function if they detect ExitCondition() is true. Only the first call has any effect.
354  */
355 inline void QuitProgram(bool error)
356 {
357         //If already quitting, don't do anything
358         if (runstate == QUIT || runstate == QUIT_ERROR) 
359                 return; 
360
361         
362
363         // set the runstate (checked in ExitCondition())
364
365         pthread_mutex_lock(&mutex_runstate); // aquire mutex
366         if (error) 
367                 runstate = QUIT_ERROR; // Program is exiting due to an error
368         else
369                 runstate = QUIT; // Program is exiting naturally
370         pthread_mutex_unlock(&mutex_runstate); //release mutex
371
372         
373 }
374
375 /**
376  * @function Thread_Cleanup
377  * @purpose Will be called in the *main* thread when exit() is called
378  *      Ensures working threads will exit, and waits for them to finish.
379  *      Then waits for them to finish.
380  *      Also frees memory associated with the worker threads.   
381  */
382 void Thread_Cleanup(void)
383 {
384         
385
386         // Threads recheck the exit condition whenever they leave a barrier.
387         // These calls will stop any threads waiting forever in a barrier for threads that exited before getting to the barrier.
388         Barrier_ForceExit(&force_barrier); 
389         Barrier_ForceExit(&position_barrier);
390
391
392         if (options.draw_graphics) // If the graphics are enabled...
393         {
394                 // Then there is a computation thread, since graphics are done in the main thread
395                 pthread_join(compute_thread, NULL); 
396         }
397
398         #ifdef PERSISTENT_THREADS
399                 for (unsigned i = 0; i < options.num_threads-1; ++i)
400                 {
401                         pthread_join(worker_thread[i], NULL);
402                 }
403         #else 
404                 // All other worker threads (if they were spawned) are terminated in Compute_Thread
405         #endif //PERSISTENT_THREADS
406
407         // Scary memory management here.
408         if (worker_thread != NULL)
409                 free(worker_thread);
410         if (sub_system != NULL)
411                 free(sub_system);
412         worker_thread = NULL;
413         sub_system = NULL;
414
415 }
416
417
418 /**
419  * @function Simulation_Run
420  * @purpose Initialise and start the simulation. Will be called in the main thread.
421  *      Replaces the single-threaded macro that does nothing, and sets up the graphics and computation threads
422  * @param argc - Number of arguments - Passed to Graphics_Run if needed
423  * @param argv - Argument strings - Passed to Graphics_Run if needed
424  */
425 void Simulation_Run(int argc, char ** argv)
426 {
427         atexit(Thread_Cleanup);
428         if (options.draw_graphics) // The graphics are enabled
429         {
430                 // I have chosen to do graphics in the main thread in this case.
431                 // A *single* seperate thread is spawned here to do computations.
432                 // This computation thread will spawn any additional worker threads required.
433                 if (pthread_create(&compute_thread, NULL, Compute_Thread, (void*)&universe) != 0)
434                 {
435                         perror("Error creating compute thread");
436                         exit(EXIT_FAILURE);
437                 }
438
439                 // This is run in the main thread
440                 // It is effectively the graphics initialisation, followed by the glut loop
441                 Graphics_Run(argc, argv);
442
443                 // The main thread reaches here after leaving the glut loop when ExitCondition() returns true.
444
445                 QuitProgram(false);
446
447                 exit(EXIT_SUCCESS); // This is the main thread; use exit()
448                 
449         }
450         else //The graphics are disabled
451         {
452                 // If graphics are disabled, there is no point spawning an extra thread.
453                 // In this case, the *main* thread starts computations.
454                 // Note that it will probably spawn additional worker threads (unless options.num_threads <= 1)
455                 Compute_Thread((void*)(&universe));
456                 QuitProgram(false);
457                 exit(EXIT_SUCCESS);
458         }
459 }
460
461
462
463
464
465 /**
466  * @function Allocate_Threads
467  * @purpose Helper function to allocate an array of pthread_t objects
468  *      Handles all the pointless, er, "important" error checking that should be done
469  * @param n - Number of threads in the array
470  * 
471  * WARNING: Remember to free() the array!!!
472  */
473 pthread_t * Allocate_Threads(unsigned n)
474 {
475         pthread_t * result = (pthread_t*)(calloc(n, sizeof(pthread_t)));
476         if (result == NULL)
477         {
478                 perror("Unable to allocate memory for threads");
479                 QuitProgram(true);
480                 pthread_exit(NULL);
481         }
482         return result;
483 }
484
485 /**
486  * @function StepFunction
487  * @purpose Helper to perform stuff in a single thread every step, after position computations are done
488  *      The reason this has void* all over the place is so that I can pass the function pointer (to horrible dragons and fiendish demons).
489  * @param arg - Can be cast to System* for which steps are to be updated
490  *      Will always be (void*)(&universe). But I have been brainwashed into the "global variables are baaaaad" philosophy.
491  * @returns arg
492  */
493 void * StepFunction(void * arg)
494 {
495         //fprintf(stderr, "StepFunction called\n");
496         System * s = (System*)(arg);
497         s->steps += 1; //Increment number of steps computed
498
499         if (options.verbosity != 0 && s->steps % options.verbosity == 1)
500                 DisplayStatistics();
501
502
503         return arg;
504 }
505

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