Sunday, 1 July 2012

Mandelbrot Part II

A few months ago I hinted at the project I was doing to consolidate some of my newly found Windows programming knowledge.  The idea was to create a program that generated a Mandelbrot set, the reason for this project being that the rules are simple, but the program can be computationally intensive and have a lot of extra features.  I got to use dialog boxes, the mouse, menus and resources, multithreading, bitmaps and the clipboard to name a few different areas of the windows API. 

The program was a perfect example of where multithreading works well, because different parts of the set are completely independent and so the different processors can each have their own job.  In my implementation I could make it so there was no need for the locking of resources, and so no possibility of deadlocks.  The only real issue was how to kill the threads when the program terminates.  I decided, because the user may want to generate many different sets in one run of the program, that it would be a better idea to create all the threads at the beginning of the program and keep them "alive" until the program is quit.  To kill the thread, a flag is set (one of the threads local variables), but this won't ensure the thread is killed instantly.  However, without realising, I was deallocating all of the threads' resources even though they weren't guarenteed to have finished their job.  I never actually got a crash because of this, maybe partly because it's done during the WM_DESTROY message (when the window is already finishing up), although I decided I wanted to do things properly.  My fix was to create another set of event objects, which the threads set just before they're killed.  Then, in the WM_DESTROY message, there's a call to WaitForMultipleObjects() which hangs around until the threads are guarenteed to have finished.

In my last update on this, I was a little optimistic on how finished I was.  The dialog box stuff was painless enough to implement, and even the zooming was okay.  But once I finished that, I looked over the code and realised it could be much better done.  It had been a nagging thought for a while and I guess I finally tipped at that point - I set myself the task of making it into much smaller modules and specifically breaking up the window procedure into more functions.  I moved all the static variables in the window procedure into a struct which then allowed me freedom in creating functions that could initialise the program and clean up.  I reduced the window procedure from about 400 lines to about 100 lines.  It took a fair bit of effort but by the end I was much more satisfied with the finished product.

I think that programming with the Windows API in C makes it possible, and very tempting just to have a huge window procedure, consiting of just a monstrous switch statement itself containing switch statements.  I've found that to split programs like this up, I just end up containing all related variables in largeish structs and passing these around wherever needed.  I don't know if this is conventional, but it usually means you create functions to initialise that struct, to destroy the struct, to process it etc.  It sometimes feels like creating a class but with none of the guarentees of construction, destruction.  Although at the same time it's fun to build from scratch.

There were no catastrophic bugs this time around.  There were issues when selecting the area to zoom into.  The problem was the zoom in area didn't go back to the default when the user made a new selection - it just kept zooming more and more without the user (me) realising.  It was easy to fix though.  Also, the image was coming out upside down for a while.

It's no doubt been a worthwhile project, and at some point in the near future, I'll put up the small amount of code the project uses.  Until then, below is some more program output.

A zoom in of some part of the set

No comments:

Post a Comment