Lately, I’ve been toying with the idea of trying my hand at some graphics programming. After spending the better part of yesterday trying to figure out how to even get started, I think I have a way ahead.
Building hello triangle using OpenGL is a fairly involved task. First, you need to settle on a graphics library. There are two choices here: OpenGL and DirectX. Obviously, I’ll be selecting OpenGL in order to avoid Microsoft vendor lock-in.
Next, you need a library to display a window for you. Sure, you could do it yourself, but then you’d get bogged down in a quagmire of platform specific issues. If you’ve been reading the blog, you know I don’t care for this sort of platform dependent nonsense, so I’ve tenatively settled on SDL 2. SDL is a cross platform multimedia library that handles sound, input, window creation, and the like. I plan to use this, in conjunction with an OpenGL context to do my work.
After you have that in order, you need an OpenGL Function Loader. Apparently the folks at Khronos were inspired by DMP Photobooth’s module system, there isn’t some opengl.h file you can just include and get your functions: you get to call
dlopen and use
dlsym to get function pointers. This wouldn’t be a huge issue if there were just a few functions, but there are thousands of them. In light of this, I’ve elected to go with GL3W for the time being. GL3W is a simple python script that generates a .c file containig the function pointers, and a .h to include.
All of this leads us to the topic of today’s post. How do we build this mess of libraries and random .c files?
We’ll Make it Work
The obvious answer here is that we need to use some sort of build system. Given my past experience with the abominations produced by NetBeans, I’ve elected to roll my own. Let’s take a look:
.DEFAULT_GOAL := all
First, we have the default goal. By default, the default goal is the first one in the file. However, I like to make things explicit. Here, we set the default goal to “all”, which builds the code for all targets.
Next, we define some variables:
CC = gcc COMPILE_FLAGS = -c -g -Wall -Wextra -std=c11 $(OPTIMIZE_LEVEL) LINK_FLAGS = -g -Wall -Wextra -std=c11 $(OPTIMIZE_LEVEL) OPTIMIZE_LEVEL = -Og LINKER_LIBS = -lSDL2 -ldl -lGL RM = rm -f UNIVERSAL = gl3w gl_includes.h
The first variable,
CC is built-in, and defaults to gcc. Again, I’m redefining it here to be explicit. After that, I define
LINK_FLAGS, which are the flags I want to pass when I’m compiling someing to be link at a future time, and when I’m compiling and linking respectively. I define
OPTIMIZE_LEVEL separately, because I want to potentially change it, and I don’t want to have to worry about if the two are in sync.
LINKER_LIBS are the libraries I’m going to be using.
RM is the rm command, with flags, to be used in the clean target.
UNIVERSAL is a list of files and targets that all buid targets depend on.
all : chapter1 chapter2 chapter3 chapter4 chapter5 chapter6 chapter7 chapter8 \ chapter9 chapter10 chapter11 chapter12 chapter13 chapter14 chapter15 \ chapter16 chapter17 chapter1 : $(UNIVERSAL) chapter1.c @echo "Building chapter 1:" $(CC) -o chapter1 $(LINK_FLAGS) chapter1.c gl3w.o $(LINKER_LIBS) ... chapter17 : $(UNIVERSAL) @echo "Building chapter 17:"
Here we have the meat of our makefile. The tutorial I’m following has 17 chapters, and I’ll be building code from each. We have an “all” target that builds each chapter, and we have a target for each chapter that builds an executable. Each chapter target depends on UNIVERSAL and its own files.
gl3w : gl3w.c GL/gl3w.h GL/glcorearb.h $(CC) $(COMPILE_FLAGS) gl3w.c
Here we build the source files that GL3W produces. I’m compiling it into a .o file so that it can be linked into the code for the various chapters.
clean: @echo "Deleting .o files..." $(RM) *.o @echo "Deleting core..." $(RM) core @echo "Deleting chapters..." $(RM) chapter1 $(RM) chapter2 $(RM) chapter3 $(RM) chapter4 $(RM) chapter5 $(RM) chapter6 $(RM) chapter7 $(RM) chapter8 $(RM) chapter9 $(RM) chapter10 $(RM) chapter11 $(RM) chapter12 $(RM) chapter13 $(RM) chapter14 $(RM) chapter15 $(RM) chapter16 $(RM) chapter17
Finally, I have my clean target. Here we delete all the cruft that builds up in the build process.
It’s a simple makefile, but I feel it’ll make this process easier. I can just do the exercises and hopefully spend less time fiddling with gcc.
So, tell me if you’ve heard this one before: you’re sitting there programming for some customer, minding your own business. But guess what? The requirements for your program depend on some platform-specific behavior. Behavior that your computer doesn’t mirror. Say, the requirements depend on the fact that
0x80000000 < 0, but you have a 64-bit compiler installed. This means that you have to test your program on their computer using their compiler. Irritating, right?
Sure, you could just sftp your code to their computer and then compile, while doing the actual development on your computer, but that sounds like so much work, right? Not as much as you’d think, thanks to NetBeans.
Enter The Build Host
With NetBeans, you can configure a Build Host, and use a remote server for compilation. Got SSH access to a user account on the build host? Got internet access? Copied the source to your computer? Then we’re set, let’s fire up NetBeans and get to work.
First, we need to set up the build host. On the left pane that normally shows your project source files, there should be a
Services tab. Click on it. In the
Services pane, right-click
C/C++ Build Hosts and click
Add New Host...
Hostname: field, enter the FQDN of the build host. Click
Next, enter your username in the
Login: field. Click
Next > and you’ll be prompted to enter your password. Finally, NetBeans will attempt to connect to the build server.
Assuming all is well, you should be presented with the
Summary page. On the
Summary page, ensure the correct tool collection is selected, and set the
Access project files via: combo box to
SFTP. We can’t use Samba/NFS because that would require root on the remote server which we almost surely don’t have. When we’re done, click
Creating The Project
Now we’re ready to create a new project. This is a pretty straightforward process. First, click
File -> New Project...
C/C++ -> C/C++ Project with Existing Sources and then click
Select Mode page, select the local directory containing your source files. In the
Build Host: combo box, select the build host we just created. Finally, click
The project will
make clean and then
make. This process will take slightly longer than usual, due to the fact that your sources are being
SFTP‘d to the build host and build there.
NetBeans will create a
.netbeans directory in the home directory of the user used to connect to the remote server. This is where it stores the remote sources and files. However, this is largely irrelevant to us; we can treat it like a local project, and let NetBeans deal with all this clerical work!
Well, the day has come and gone. DMP Photo Booth’s final test on June 21st went off without issue, and DMP Photo Booth has left Beta and is now considered “production ready”. The initial 1.0 release can be found on GitHub.
The significance of June 21st is the very reason DMP Photo Booth was created; the 21st is the day of my wedding. My wife wanted a photo booth for the reception. We looked into renting a photo booth, but it turns out that they run around $1,000. I turned to open source. Some quick googling turned up some options, but they were all personal projects or out of date. Sure I could get somebody else’s project working, but what’s the fun in that? I decided that we didn’t need to rent one, or download one, I could build it!
In late 2013, I set to work in earnest. I had a couple of months of downtime in school, and since I’m not currently working it was the perfect time. I decided I had three main objectives for this project: get some arduino experience, get some GTK+ experience, and do this all as portably as possible. I had initially decided to mostly ignore GLib and focus on GTK, but slowly I grew to appreciate GLib for what it is: the standard library that C never had. First I used GModule to handle shared libraries in a portable manner. Next I decided to use GLib primitives to keep from having to deal with cross-platform type wonkiness. Next, having grown tired of dealing with return codes, I refactored the project to use GLib’s exception replacement: GError.
It’s not all roses and puppies though. There are certainly things I’d do differently. DMP Photo Booth is developed in an Object Oriented style, passing opaque structs with “method” functions that operate on them. Each component of the program are organized into their own source file with file scoped globals scattered throughout. Said globals are protected by mutexes to create a semblance of thread safety. That said, threading issues have been a major thorn in my side. Long story short: I regret this design choice. While I still feel that this is the correct way to structure C code, and that if globals are required, this is the correct way to handle them; I feel that I should have made more of an effort to limit side effects. Recently, I’ve spent some time doing functional programming, and if I could do it again I’d try to write in a more functional style. Fortunately for me, this is something that a little refactoring could help with.
Additionally, one thing I thought would be a major help is something that began to be a major thorn in my side: NetBeans. As the size of the project grew, NetBeans got slower and slower. It seemed that I spent more time fiddling with IDE settings than actually coding. Even worse is that the IDE-generated makefile is so convoluted that it’s extremely difficult to modify by hand in a satisfying way. I’ve always coded with and IDE so I wouldn’t have even considered not using one, but then I spent some time with Haskell. One of Haskell’s “problems” is that it doesn’t have good IDE support. It doesn’t seem like any IDE really handles it well, so most people use Emacs. Personally, I haven’t really warmed up to Emacs, but GEdit has syntax highlighting for Haskell and a built-in terminal for GHCI. GEdit also has syntax highlighting for C. Next time, I will seriously consider using a lighter-weight text editor for a C project. All this said, I think NetBeans for Java remains the way to go.
Like any program, version 1.0 is just one of many versions. There certainly remains a lot of work to do with DMP Photo Booth. Some major items you are likely to see whenever I get around to working on DMP Photo Booth some more:
I think anybody who has seen it will agree: the options dialog in DMP Photo Booth is bad. It’s poorly organized, and kind of wonky. Personally, I modify settings using the
.rc file, which is telling. This is certainly a high-priority improvement.
Like I said above, the code could use a pass to limit side effects. Funtions need to have their side effects limited, and globals need to be eliminated unless absolutely necessary. However, C is not a functional language. While one could argue that function pointers enable functional programming in C, this is a very pedantic argument. I won’t be going crazy with functional programming techniques. There will be no Monads, or for loops being turned into mappings of function pointers.
Optional Module API
An idea I’ve had on the back burner for a while is an optional module API. This would be used for very specific quality-of-life things. For instance, a module could provide a GTK widget to be shown in the options dialog. Any module that doesn’t want to implement any or all of the optional API can just ignore it. The module loading function will gracefully handle the
dlsym failure, just treating it as it is: declining to implement the API. I have no plans to change the current existing API, so all you module developers can rest easy!
User Interface Module
It occurred to me that it might be good to have a UI module. This would provide the UI, and wouldn’t be tied to the trigger/printer/camera module start/stop system. This module would be loaded at startup and unloaded on shutdown. This would allow the Photo Booth to use different widget toolkits: QT, Curses, Cocoa, WinForms, or whatever else. Under this scheme, the current GTK+ interface would be abstracted into the reference UI Module.
For as long as I’ve been trying (successfully or not) to program, I’ve been using C like languages. When I was a kid, I struggled in vain to learn C++. As an adult, I learned Java. After that, I used Java as a spring-board into the wonderful world of C Like Languages: C, C++, Perl, Lua. I wrote hello world in dozens of others as well. I found myself proudly proclaiming that “I’m confident I could pick up any C Like Language!”
Then one day I thought “what about the rest of them?” Sure, maybe I can speak Latin. Maybe I can pick up any Latin based language with relative ease. But what if I need to move to China? I speak C, but what if C falls out of favor for something else? I decided it was time to try something else.
C and it’s cousins broadly represent the Procedural and Object Oriented paradigms. We’ve all been there and done that. Procedures and Subroutines may or may not take arguments, do something, and may or may not return a result. The global or local state may or may not change. Loops happen. I don’t think it is a stretch to say that these are the two most mainstream paradigms. For the purposes of this blog post, I’m going to lump the Procedural and Imperative paradigms together. I understand that they are not the same thing, but roughly speaking, the Procedural paradigm is an evolution of the Imperative paradigm.
This leaves us with Functional Programming. Unlike the functions of an Object Oriented or Procedural language, the functions of a Functional language closely resemble those in math. In math,
f(x+2), where x = 2, will always return 4. Similarly, a function in a Functional language will always return the same result given the same input. Where a function in a Procedural or Object Oriented language describes the steps to perform some task (usually, this involves some sort of loop construct), a function in a functional language just describes what the result of some function is. (usually involving recursion)
f(x+f(x-1)) adds x to the result of
f(x-1), which recursively adds x-1 to
f(x'-1) and so on until the end of time.
So, what programming language to choose? Many languages support functional programming to an extent. Python, Lua, and even C# if you squint hard enough. However, these languages are multi-paradigm. As such, it will be easy to fall back into my C Like ways. What about Lisp?
Lisp is a family of languages: Common Lisp, Scheme, Clojure, Emacs Lisp. Sure, I could learn one, and theoretically be able to transition with ease, but this isn’t a level of fragmentation that I’m comfortable with. In addition, Lisps are multi-paradigm, so I’m more likely to not keep the faith. Which leaves me with…
Haskell is a “pure” Functional programming language. While any useful program must have the side effect of reading from or writing to some external source, Haskell places that part of the program neatly in a corner. Let’s talk about some of the neat features of Haskell:
Expressions in Haskell are evaluated lazily. What this means is that a value isn’t computed until it’s needed. Let’s take a look at an example:
embiggen :: Int -> [Int] embiggen x = x:embiggen (x + 1)
This function takes an integer, and creates a list out of it. (Lists in Haskell behave much the same way as a normal linked list: O(1) insertion, O(n) traversal) The passed-in integer is pushed on to the front of the list resulting from
embiggen (x + 1). You may have noticed that this function will go on forever. While maybe not ideal, this is ok in Haskell because of Lazy evaluation. The infinityeth element of this list will not be evaluated until it’s needed!
show (take 5 (embiggen 5)) [5,6,7,8,9] show (embiggen 5) !! 17 22 show (embiggen 5) [OMG INFINITE RECURSION!!!!!]
In the first example, we call the library function
take, which returns a list containing the first
n elements of the passed in list. In the second example, we call the library function
!! (all operators are functions), which is the list indexing operator, which returns the
nth element of the list. In a language with strict evaluation, the list would need to be completely evaluated before these things could happen. In Haskell, it doesn’t! Only in the third example, where we attempt to call show on the entire list, does infinite recursion occur.
Tail Call Optimization
This is one of those terms that gets thrown around a lot, but what does it actually mean? The short answer is that it prevents a recursive function call from consuming a new stack frame. In a language without this feature, if
foo() calls itself, the new call will consume a new stack frame. This will cause a stack overflow if allowed to go on too long. In Haskell, this isn’t a problem because of Tail Call Optimization.
Haskell’s type system is quite different from the usual type systems. Sure there are Ints, Chars, Floats, Bools, and the like, but there’s more to it than that. Haskell is very strongly typed. There is no casting in Haskell, if a function takes an Int, there’s no getting around giving it an Int. However, the whole type system operates in a manner similar to generics in languages like C++ or Java. Take the following examples:
putInList :: a -> [a] putInList thing = [thing] addStuff :: (Num a) => a -> a -> a addStuff lhs rhs = lhs + rhs
The first function takes some arbitrary type, and returns a singleton list containing the passed-in argument. Much like generics, the argument shouldn’t depend on any type-specific behavior.
The second function takes two arguments of the same type that behaves like a number (Int, Float, Double, and friends) adds them, and returns the result. the
addStuff function accomplishes this by specifying that arguments of type
a should be members of the
Num Typeclass. Despite the word “class”, Typeclasses aren’t the same as classes in Object Oriented languages. You CAN think of them as being the same as Java’s interfaces. When you create a type, you can make it a member of any number of Typeclasses. You must then implement the functions specified by the Typeclass, just like when a class in Java
implements some interface, it must define the methods of that interface.
This is just the tip of the iceberg, but I’m sure you’re beginning to see how you can make very general functions in a very type-safe way.
Partial Function Application
A feature of functional programming is higher order functions. This means that functions can take functions as arguments, and functions can return functions. While nice, this isn’t exactly a new concept. Even C supports this to an extent with function pointers. What is new is partial application of functions. Recall the
addStuff function above. It takes two arguments of type
a and returns a result of type
a. Now let’s look at an example:
doNumFunc :: (Num a) => (a -> a) -> a -> a doNumFunc f a = f a addThree :: (Num a) => a -> a addThree a = addStuff 3 a
doNumFunc function takes a function that takes a type
a and returns a type
a (This is what
(a -> a) means), and a second type
a, and returns a type
doNumFunc calls the passed in function with the second passed in argument. The
addThree function takes a type
a and returns a type
addThree takes an argument, and calls the
addStuff function we defined earlier with its argument and 3. How does this all pan out?
addThree 3 6 doNumFunc addThree 3 6
Seems pretty straightforward, right? Though, this isn’t very re-usable. What if I want to add 4? Do I need to define a function
addFour? No, I can partially apply
addStuff. If you call a function in Haskell with less arguments than it takes, it will return a function that takes the remaining arguments and returns a result! Observe:
doNumFunc (addStuff 3) 3 6
Now things are getting cool. By calling
(addStuff 3), we’ve created a function that takes a type
a, adds 3 to it, and returns the result! You can’t do that in C!
Excited yet? You know you are, don’t try to act like you’re not. But how does one get started? Like any language, you need two things to begin: a compiler/interpreter and some reading material.
First up, you should go download the Haskell Platform. This package contains your compiler/interpreter and all the standard libraries. Haskell can be compiled, or interpreted. Or, you could use
ghci, the interactive interpreter, if you just want to doop around and try stuff.
If you’re running a Linux distro,
haskell-platform is likely in the repositories. In Debian or Ubuntu, it’s a simple:
sudo apt-get install haskell-platform
… and you’re set! Unfortunately, there doesn’t seem to be a great IDE for Haskell. NetBeans definitely has nothing to offer in this regard. Luckily for us, Haskell is simple enough to not really need an IDE. GEdit, the default text editor that ships with Gnome, has built-in syntax highlighting for Haskell. Just enable the built-in terminal in GEdit to test stuff and you should be good to go. I like to run
ghci in the embedded terminal to test functions as I write them. Plus, as you code, you can periodically attempt to load the script in
ghci to make sure everything is formatted correctly and you haven’t messed up your syntax/types.
One of the biggest barriers to learning a new language is money. Nobody wants to put down cold hard cash on learning something new when what they have is working just fine. Luckily for us, you can learn you a Haskell for free! Learn You A Haskell For Great Good is a beginner’s guide to learning Haskell aimed at developers coming from C Like Languages. The best part is that the whole book is available to read online for free! You can check it out for the low-low price of zero dollars. If you like it, maybe you buy a copy for your bookshelf. Or maybe you just spread the word.
Whatever you do, you should have a good base of knowledge in Haskell. At that point, you can just consult Hoogle to learn more.
You’ve heard it before: “Premature optimization is the root of all Evil.” Capital Evil. So you go on about your day, arranging the ones and zeros in pretty christmas tree shapes and suddenly the day arrives: your program is slow as molasses. What are you going to do now?
Last monday was that day for me, and I’ve been underwater ever since. “Why is this happening to me?!” I thought. While not prematurely optimizing, I thought I did things right. I have no nested for loops. I’m not using an array when I need a list. Threads aren’t modifying the UI willy-nilly. Why has God forsaken me?
I first noticed it while working on the printer module. After the program is open for some length of time, my whole computer begins to lag. Not just a little bit either; things completely fall apart. In the space of about 5 minutes, the computer becomes unusably slow. Killing the Photo Booth process doesn’t help; only physically shutting the computer off helps. Of course, the computer is so slow that I can’t use the shutdown option; I have to press The Button.
At this point, I feel some context is in order. I had been trying to figure out how to make my printer print on photo paper. Apparently printing is one of the areas Linux still hasn’t caught up to windows on, so this was proving to be difficult. After printing a few strips, I realized that my low-res photo strips weren’t going to cut it, so I bumped the resolution from 100 pixels wide to 1000. It was then that I noticed things were off.
Ten years of troubleshooting experience kicked in: “what changed?” I thought. The obvious answer was the image size. Clearly my photo strip assembly algorithm was operating at O(n^n^n) or something. What can be done?
Doing It Wrong
I took a look at my assemble strips function. After poking around for a while, I zeroed in on something that had been bugging me for a while. I had been using a function
MagickResetImagePage combined with
MagickCoalesceImages to composite images over each other. I had decided to use these functions before I knew this operation was called “compositing”, and I had found them in a tutorial on making animated .gif files in MagickWand. At the time, I was never really happy with this implementation, so I went back to the API docs to see if there was a function with “composite” in its name. There was.
MagickCompositeImage is a lot more intuitive to use than
MagickResetImagePage. It doesn’t have that Magickal formatting string that
MagickResetImagePage uses, it just takes coordinates. Perhaps this was the solution to my problem. I refactored, and recompiled.
Measure, Don’t Guess
That old gem: I’m sure you’ve heard it too. I decided that maybe this was my best course of action. I decided it was time to learn how to use this Valgrind thing all the Cool Kids are talking about these days. For those of you not in the know, Valgrind is a utility that will tell you various things about your program. The most important/most well-known thing that it can do for you is identify memory leaks. Thinking that prehaps I have a memory leak, I installed Valgrind and got to work.
It turns out that GTK has more than a few memory leaks. Allegedly this is due to the fact that it doesn’t cleanup on exit, relying on the OS to free the memory on program termination. While the general consensus is that this is fine, it doesn’t help us. The folks at Gnome are aware of this, and there is even a Wiki page on ways to mitigate this. The cliff’s notes version of that page being: “Just search for ‘definitely lost'”.
Armed with this piece of wisdom, I set off. I ran the Photo Booth in Valgrind, and examined the results. Valgrind actually turned up some memory leaks, which I corrected. Maybe now we’re set!
Breaking Out The Profiler
This is what they usually want you to do when they tell you to Measure. Unfortunately for me, NetBeans’ built-in profiler is only for Java. After some google searching, I found gprof. Gprof is a pretty bare-bones profiler. It does what it says and not much else, which is fine. I hooked my program into the profiler and got to work. The results? Nothing. My two GTK idle functions ran some 7 million times, returning basically immediately each time as expected. Every other function performed as expected.
Trying The Process Monitor
Having run through Valgrind and GProf, coming out empty-handed, I was at a loss. I got into development because I wanted to fix my own broken code instead of mitigate somebody else’s, and fix it I will. Luckily I have 10 years of sysadmin experience to fall back on. I dusted off my process monitor and got to work.
I fired up DMP Photo Booth, and watched it in the process monitor. I pushed the button. I pushed it again. And again. memory use rose and fell predictably as the strip was assembled, but CPU usage stayed relatively low. Then boom!
I tried again, this time doing literally nothing. Still my computer sputtered and died. I killed the process, but again it was too late.
But wait, isn’t the OS supposed to clean up after me when my process ends? Something fishy is going on.
Have I Mentioned That Threads Are Hard?
Having eliminated all other possibilities, I was forced to consider that I was having a threading issue. “But I was so careful!” I thought. Shortly thereafter I noticed it: I was getting random pthread mutex errors on my console. Clearly I had a threading issue on my hand. Was I spawning extra threads? Was something not releasing its lock? Was I being victimized by gremlins? I set a break point on line one of main() and fired up my debugger. It was time to see just what was being done when nothing was being done.
So, I stepped through my program. Whenever I got to a
g_thread_new call, I made sure the thread function was solid. Finally, I got to my
g_idle_add calls. I had two of them, one to monitor the status indicators, and one to retrieve photo strip thumbnails. Both of these functions pop from a result from a GAsyncQueue. These Queues are fed by worker threads. I thought back to my profiler output and remembered how often these are called. Looking a few lines down I saw a call to
g_timeout_add_seconds. This function is basically adds an idle function, but is only called at most X seconds. Maybe replacing the
g_idle_add calls with
g_timeout_add_seconds was my answer. I refactored and reran.
Well, crud. “Are these functions even my problem?” I thought. I commented them out, recompiled and reran.
“So, what’s the difference?” I wondered. All three of these functions rely on the same basic behavior: pop from a GAsyncQueue some result placed there by a worker thread. I looked at the three threads: the thread that was working properly calls
g_async_queue_ref/unref, and the two that don’t work do not take a reference, instead accessing the static global variable in their module. I refactored all thread functions that access a GAsyncQueue to take a reference and work on their local copy only. I recompiled, reran, and went to bed. 46,100 seconds later, everything was humming along just fine.
Wait, So I Just Had To Increment A Reference Count?
It certainly seemed odd. That’s like your car not starting if the headlights are out. Sure, they’re important, but the car should still start right?
Looking through the source of glib didn’t help. So far as I can tell, all that does is increment the reference count, and return a pointer. I turned to the documentation, which says “… Whenever another thread is creating a new reference of (that is, pointer to) the queue, it has to increase the reference count (using g_async_queue_ref()). Also, before removing this reference, the reference count has to be decreased (using g_async_queue_unref()). …” While not definitive, this certainly seems to indicate that taking a reference is important.
Frankly, I’m not happy about this answer. This is just the sort of magic solution that I hate; it’s fixed, but I’m not sure why. For the time being, I won’t dwell on it. Moving forward, I’ll be sure that my threads take a reference of a GAsyncQueue before calling methods on it. At some point when all of this is said and done, perhaps I’ll investigate this mysterious reference count.
I have taken away from this a new appreciation of just how brittle threads are. Sure, they are powerful, but shooting yourself in the foot with a 50 cal hurts a lot more than with a 9 mm. I’ll have to be more careful.
It was also a good introduction to GProf and Valgrind. Expect blog posts on the usage of each of these tools soon!