Tuesday, March 28, 2006

fork + execve - so happy together!

A long time ago in the 70s when everyone smoked a lot of dope, someone thought it would be cute to break the application launch APIs into two pieces. First you call fork to clone yourself (but this is really fast because the OS is lazy) and then execve turns one copy into something new. Cute!

I just learned two interesting things about Mac OS X tonight:

1. Apparently fork without execve won’t always work.

2. execve without fork doesn’t work so well either.

In the first case, you can read here about why forking doesn’t always work. In Apple’s defense, I don’t blame them for this - fork is supposed to be fast, and cloning all of the apps resources wouldn’t be. Oh yeah, some of those resources might be owned in some exclusive mode. Historically fork was a way to use processes like threads. Or are threads processes? I don’t know…it’s 2:30 AM and my brain hurts.

In the second case, frankly I have no idea why execve won’t work on its own. I get error 45:

#define ENOTSUP 45 /* Operation not supported */

My speculation is that execve requires a “clean” process of some type, e.g. a forked process and not a regular one.

Either way the moral of the story is clear: apparently no one has tried to use these two functions separately since they made disco illegal, so neither should you. Fork even if you don’t need to, then kill off the parent.

Tuesday, February 14, 2006

So that’s where HINSTANCES come from

I realize that this is probably totally trivial for real Win32 developers, but it took me long enough to find this that I’ll blog it and help Google guide future developers.

RegisterClassEx requires an HINSTANCE…I always thought of this as “that thing that is passed to WinMain. Now I realize - it’s a reference to the DLL that the Window Class function is in

So if you’re like me and want to register a class from library code without having that HINSTANCE from WinMain and you’re not using DLLs, you can just use GetModuleHandle(NULL) and call it a day. Easy!

Saturday, February 11, 2006

var-args and macros? Sure!

I didn’t realize until recently that this works:

#define WED_ThrowPrintf(fmt, …) \
throw wed_error_exception(__FILE__, __LINE__, fmt, __VA_ARGS__)
The reason I like var-args and macros together: they make thorough error reporting relatively easy in C++ without a lot of code.

if (err != noErr) WED_ThrowPrintf(”Unable to open document %s (OS Error %d: %s)”, file_name.c_str(), err, OSTranslate(err));

C++ makes almost anything possible, but does it make it easy? I’m a lazy human programmer; the easier a subsystem is to use, the more I’ll use it. So it makes sense to make good practices easier. The easier it is to encode a lot of context information into an error report, the more likely I am to do it, and the more likely a field-report of an error will contain useful diagnostic information.

Friday, February 10, 2006

What makes a professional?

“My definition of an expert in any field is a person who knows enough about what’s really going on to be scared.”PJ Plauger

It doesn’t get any simpler than that. You can always spot the inexperienced programmers in a meeting saying “Oh yeah that can be done. That’s easy!” That’s when you know to be terrified.

Saturday, February 04, 2006

Don’t Trust Error Messages

I just spent some time chasing an error message that I was getting from a Windows Media component when I tried to open a Windows Media Stream. The error was NS_E_BAD_CONTROL_DATA which is defined as:

The server received invalid data from the client on the control connection

This is a very cryptic message because when dealing with COM objects, the terms “Server” and “Client” can have many many meanings. In this case, I had no real clue whether it was referring to a conventional network Server and Client or a COM server and client. That threw me off for a bit.

I ended up finding the problem and it wasn’t even remotely close to what the error claimed. The problem was that my destination folder where I planned on recording the stream TO didn’t exist. Yeah it’s a stupid mistake but what does that have to do with reading the stream? Remember it was WMASFReader that died not WMASFWriter.

In any case, it’s important to remember that sometimes errors will occur and they will not be caught for quite some time or sometimes not at all. If their effects cascade throughout your program, you may find them causing errors that don’t make sense. If you could peel back the layers of the onion enough to see Microsoft’s underlying code, maybe the error and how it got there would make sense however on the outside, they’ll often seem unrelated and can be VERY misleading.

Do NOT fixate on what the error SAYS. Work backwards and find out what triggered it.

Thursday, February 02, 2006

Your Friend the Priority Queue

One of the things I like about the STL is that it makes easy in C++ higher level data structures that make programs better but would otherwise be too cumbersome to use. For example, when was the last time you coded a red-black tree in C? But std::map is pretty easy.

Here’s my extremely unsound and imprecise definition of a priority queue: a priority queue is a data structure where you can rapidly insert “todo” items, find the most important one, reprioritize an item, and remove a finished item. Ideally I’d like all of these tasks to run in logarithmic time or better.

One way to hack out a priority queue using the STL is like this:

struct my_item;
typedef multimap queue_type;
struct my_item {
queue_type::iterator self;
xint more_stuff;
};

The idea is simple: map already gives us logarithmic time to locate the first item, insert and remove an item. The struct has a pointer to its own iterator allowing it to reprioritize by adding and removing itself, like this:

void reprioritize(my_item * item, float new_prio, queue_type * a_queue)
{
a_queue->erase(my_item->self);
my_item->self = a_queue->insert(queue_type::value_type(new_prio, item));
}

I can’t say that this is terribly good C++ but I can say that it works quite well. An examples of priority queues used to create X-Plane scenery:

We use a greedy algorithm to build our triangulations from elevation data. Basically we find the locatoin on our mesh that is the most wrong and fix it by inserting a new point into our mesh. We then re-evaluate the mesh and fix the next-worst point. We repeat until we’ve added too many points or the worst point isn’t that bad. Here’s where the priority queue comes in: when we insert a point, the error between our local mesh and the ideal elevations change, but only for a few points near the insert. So we want to rapidly reprioritize those points without having to reconsider the others. With a 1.5 million potential points and perhaps a few hundred thousand inserts, the priority queue is critical. With a good priority queue this algorithm takes a few seconds; without it a few hours. (This algorithm comes from a paper - I wish I could find it but I did not record the URL in my code. When I find it I will post a link.)

When should you think priority queue? Well, any time you have to evaluate a whole series of conditions at specific events, and the time of the events is known in advance, a priority queue can help. It may be necessary to replace polling logic (e.g. “is it time to flush the queue now”) with solid predictions (e.g. “I know that this queue is 25 bytes away from being full and that one is 13 bytes from being full”).

The STL provides another approach to priority queues: tucked away in the algorithms section are routines like make_heap, is_heap, push_heap, and pop_heap. A heap is basically an array of data that’s sorted just enough that the smallest item is in front. It’s faster to preserve these relaxed requirements than to do a full sort. I haven’t utilized heaps in my code yet, so I can’t comment on heap vs map performance.

Tuesday, January 31, 2006

Debugging OpenGL

There are only two debugging techniques in the universe:

  1. printf.
  2. /* */

(I would say a nice IDE that gives you stepping, logging, variable examination, etc. is just a really nice variant of these two techniques that avoids recompiles.)

OpenGL poses some particular challenges to debugging: often OpenGL’s failure mode will be to draw nothing, or fill the entire screen with solid black or white. A lot of OpenGL’s internal state is not easily printed by the application, and since OpenGL is state-based, removing code can have cascading effects.

You can still utilize these debugging techniques with OpenGL. Here are some things I’ve done when working on X-Plane to try to retain my sanity while debugging OpenGL code:

  • Premature swapping: in a double-buffered context, you can swap the context and then wait for a mouse click. If you do this at intermediate points in your drawing, you can see your drawing in steps as it takes place. This is sort of like printf in that it lets you see internal program state that would otherwise be hidden.
  • Removing drawing state: when something goes wrong you can start to remove the OpenGL tricks you do until you arrive at a simpler problem. Turn off texturing, turn off texture projection, turn off blending, etc. Lighting is a good one to turn off when things get strange, since bad normals can cause abnormal results.
  • Use a different primitive. If your triangle fan looks funny, try drawing it as a line loop. Or set the polygon mode from fill to line - the wire frame may reveal something not obvious in a solid model.
  • If you use display lists, set up your code to allow for immediate-mode execution as an option; this will let you catch display-list-related problems (I have seen drivers do things differently in display lists, and there are lots of ways to make coding mistakes with them) and also let you apply other techniques inside the list, like swapping to see work in progress.
  • #define common modes: for all of the above tricks, if you’re going to be doing them a lot, design the code for debugging. I try to leave a series of #define switches at the top of my translation unit that apply debug-mode effects.

As a side thought, I have found Vertex Buffer Objects (VBOs) to be easier to debug than display lists for a few reasons: it’s pretty easy to revert VBOs to client vertex arrays (CVAs) at which point you can inspect the data in the debugger. Display lists can contain state commands as well as geometry, so it may not be obvious what the full effects of calling a display list is; since VBOs are just geometry you know what you are getting.

An example: in X-Plane we had some code that essentially did this:

static int has_dl = 0;
if (has_dl == 0)
{
// Build the car display list the firs time we need it.
has_dl = glGenLists(1);
glNewList(has_dl, GL_COMPILE);
draw_a_car(); glEndList();
}
glCallList(has_dl);

This would be okay except that everything draw_a_car does gets compiled into the list. Furthermore, any non-OpenGL side-effects of draw_a_car get run only once. Neither of these problems are made clear by the code.