C++ Exceptions: The Good, The Bad, And The Ugly

Recently, a recording of Titus Winters’ presentation from CPPCon 2014 found its way around the office. In the video, Titus discusses style guides and coding conventions as they apply to Google’s C++ codebase. One of the contentious topics touched upon was the use of exceptions, which are apparently verboten at Google. That, of course, sparked a few questions from Titus’ audience and incited some debate within our own organization.

It’s interesting how polarized C++ developers are when it comes to the use of exceptions. It doesn’t matter if you talk to a junior developer or a 20 year veteran. You’ll almost always get a response that lies at one of two ends of the love/hate spectrum. At one end is “Exceptions are evil. Like, goto-evil, man. It’s chaos. Biggest wart in the C++ standard, bar none.” At the other end is “Dude, it’s 2015. WTF aren’t you using exceptions? They’re so chic, so modern. So much better than those geriatric return codes.” Of course, put a few beers in these folks and maybe some free food, and their postures will waver. Both parties eventually admit there’s some good and some bad when it comes to exceptions. As it turns out, the exception is a language feature that’s not as cut-and-dry/black-and-white/1-and-0 as we programmery folks care to admit.

For this blog entry, I thought it might be useful to take a step back and look at things from a 10,000 foot view (or 3,048 meters for my imperially challenged friends). Let’s spend some time picking apart the arguments for and against C++ exceptions. Even if we can’t arrive at some grand conclusion, it’ll at least allow us to appreciate the perspectives of our peers a little better. And who knows? Maybe we’ll find some middle ground.

So let’s start on a positive note. How about some pros?

The Pros:

Exceptions cannot be ignored.

In C, the convention for communicating errors is the much beloved error code. It’s succinct, there aren’t many surprises, and it gets the job done. None of us are new to error codes. We encounter them every day in system calls, standard library functions, third party libraries, and even in our own code.

The dirty truth is that in C, errors are ignored by default. A function caller is free to exercise their right to ignorance. As a result, “failable” function calls often go unchecked. And when unexpected failures occurs, one of three things typically happen:

  1. Nothing. The failure wasn’t fatal. The code continues to operate just fine.
  2. The code doesn’t crash, but the application starts to behave strangely and sometimes eventually crashes.
  3. Boom. The application crashes and burns immediately.

The same code path with the same unchecked failure may even exhibit a random selection of one of these three behaviors every time it’s executed.

In contrast, exceptions cannot be ignored. An uncaught exception does one thing – crashes the application. It forces you, as the developer, to make error handling a priority.

Exceptions can carry more information than return codes.

To make sense of an error code, you usually need to look it up. You might have to refer to another source file, the API docs, or a sticky notes in the guy’s cube next door. And even then, you might not be able to determine exactly what caused the error.

“File open failed.” – Ok. But what file? And why?

“Connection reset by peer.” – Which peer?

Maybe you don’t care about the specifics. But if you do and you’re not sure what exactly caused the problem, a bit of sleuthing my be required. That means more work on the error-handling side of the fence.

Ideally, you’d be able to capture more details on the error-causing side of the fence. Exceptions can help with this. In C++, anything copyable can be thrown. With a copyable user-defined type, you can capture as much context relating to an error as you’d like and communicate that to the caller just by throwing it.

Exceptions allow error-handling code to exist separately from the place in the code where the error was detected.

How many times have you seen code that looked similar to this?

bool success = doSomeWork();
if (!success)
{
    logError("Error Occurred");
    return;
}    
success = doMoreWork();
if (!success)
{
    logError("Error Occurred");
    return;
}    
success = doEvenMoreWork();
if (!success)
{
    logError("Error Occurred");
    return;
}
// etc. 
// etc.

This code snippet contains a lot of noise. And it’s a little repetitive in how the error is handled. What if we need to change our error handling behavior? We’ll need to visit each place where success equals false. How different might this look if we were using exceptions?

Let’s assume the functions doSomeWork(), doMoreWork(), and doEvenMoreWork() throw exceptions instead of returning a success value. Our code might then look like this…

try
{
    doSomeWork();
    doMoreWork();
    doEvenMoreWork();
}
catch (...) // Or some specific exception type.
{
    logError("Error occurred.");
    return;
}

The code footprint is smaller and we’ve concentrated our error-handling in one spot. In the normal, non-exceptional case, the code actually runs faster than the previous code snippet because it doesn’t have to constantly check return values.

Exceptions allow errors to propagate out of constructors.

Constructors don’t return anything. They can’t. The standard says so. So what happens if an error occurs during the execution of the constructor? How does client code know something went wrong?

There is, of course, a brute force way to accomplish this. You could include error flags in the class being constructed and set them/check them appropriately. This is a bit of work and it requires both the class and its client to have an agreed-upon contract for error checking. It works, but it’s not really ideal. It results in a bit of extra code and it’s easy to make mistakes.

A much easier way to communicate errors from the constructor is to throw an exception. It’s straightforward and doesn’t require the class implementer to pollute the class with error flags and error-related getters/setters.

That being said, throwing exceptions from constructors does have a gotcha. If an exception is thrown from a constructor, no instance of the class is created, and therefore no destructor will be called. Think about that statement for a second. What this means is that that if there were any resources (heap allocations, opened handles, etc.) acquired in the constructor prior to the exception being thrown, they may be leaked unless appropriate steps are taken.

Exceptions are better than setjmp/longjmp.

The setjmp/longjmp dynamic duo comes to us from C. They provide a mechanism for performing what is referred to as a non-local jump, (sometimes called a non-local goto). setjmp is called to mark a point on the stack where program execution should return and longjmp is used to jump back to that point. The way it works is that setjmp records the contents of the CPU registers, which includes the stack pointer. When longjmp is called later, those register values are restored and the code executes as if it had just returned from the call to setjmp.

What happens to all the stuff that was on the stack between the call to setjmp and longjmp? As you might expect, it’s all discarded. It’s as if that stuff never existed.

You might be saying to yourself, “This just sounds like stack unwinding.” It’s certainly a form it. But when C++ programmers thinks of stack unwinding, they imagine walking down the stack frame by frame and executing little bits of code along the way until their destination is reached. In traditional stack unwinding, destructors for stack-allocated objects get called and RAII objects get the opportunity to clean house. If we’re dealing with compilers with try-finally extensions (Visual Studio), even finally blocks get executed.

Unfortunately, that’s not the form of stack unwinding we’re dealing with when we work with setjmp/longjmp. longjmp literally jumps down the stack to the point recorded by setjmp in one fell swoop. It doesn’t do it frame-by-frame. It doesn’t call snippets of code along the way. It’s one second you’re here, the next second you’re there. Destructors for local variables don’t get called. RAII objects never do any cleanup. And finally blocks never get a chance to do their job.

And that’s why exceptions shine over the use of setjmp/longjmp. Exceptions allow for the form of stack unwinding that C++ developers are comfortable with. They can sleep easy at night knowing that local variables get destroyed as expected and destructors will execute even under the most exceptional of circumstances.

Exceptions are easier to propagate than return codes.

Conceptually, error code propagation seems like a no-brainer. Functions that call other “failable” functions check error codes, react appropriately, and propagate the error down the call stack if it’s appropriate. Pretty simple, eh? Not so quick. Checking error codes for function calls requires a certain amount of vigilance. It can be tedious. And, as shown in a previous pro, it results in a lot of extra, repetitive code. In practice, it’s not uncommon for many function calls go unchecked. It’s usually just the “important” ones that get all of the attention. And because errors are ignored by default in C, many errors tend to slip through the cracks. As functions call other functions that call other functions, this problem compounds and an application can miss out on opportunities to react.

As mentioned before, exceptions are propagated by default. There’s no way to accidentally ignore an exception without crashing your application. If you’re writing a function that calls another function that throws, and you wish the caller of your function to handle all the errors, there’s nothing you need to do. (Disclaimer: In some circumstances, it may be desireable to catch and rethrow the exception. So you may actually need to do something. But it really depends on the needs of your function. See the references at the end up the article for situations where this might be appropriate.)

And now for the glass-half-empty list.

The Cons:

Exceptions are more complex than error codes.

Something an error code has over an exception is simplicity. The concept is so easy to grasp someone completely new to coding can learn how to use and apply error codes almost immediately. The very first function any C/C++ developer writes is main(). And guess what? It returns an error code.

Exceptions aren’t as simple. Not only do you need to understand things like throw, try, catch, nothrow, and dynamic exception specifications (deprecated), but also an assortment of supporting functions and data types, such as std::exception, std::exception_ptr, std::rethrow_exception, std::nested_exception, std::rethrow_if_nested, etc.
There are also plenty of rules and best practices that must followed like…

“Don’t emit exceptions from destructors.”

“Constructors must clean up before throwing because a destructor will not be called.”

“Assign ownership of every resource immediately upon allocation to a named manager object that manages no other resources” (dubbed Dimov’s Rule by Jon Kalb)

etc.

It’s a lot to absorb. Really. And it can be intimidating to both new and seasoned C++ developers alike.

Writing exception-safe code is hard.

Even if you understand all of the exception-related concepts and jargon mentioned in the previous item, writing exception-safe code is still hard. As Scott Meyers says in “More Effective C++”, “Exception-safe programs are not created by accident.” Code absolutely must be designed with exceptions in mind. This includes the throwers, the catchers, and EVERYTHING in between.

Bad things can happen in functions that aren’t expecting to be short-circuited by an exception. That’s one of many reasons the introduction of exceptions to a legacy codebase that doesn’t already use exceptions is a very, very bad idea.

Exceptions make coder harder to read because it creates invisible exit points.

A function that returns an error cannot cause the calling function to prematurely return (we’ll ignore interrupts, abnormal program termination, and setjmp/longjmp for the moment). If a function decides to check the error code produced by another function and return based on some criteria, you’ll see that written down in the code. It’s explicit and not easy to hide.

The potential for a thrown exception can be subtle. If your function calls another function that throws, and your function doesn’t catch the exception, your function will stop executing. From a readability standpoint, this can be awful. It may not be obvious that any given block of code may be prematurely terminated, even if it’s completely designed with exceptions in mind.

Knowing what to catch can be tricky.

There are some languages that are aggressive about making sure you follow the contract when it comes to exceptions. Java, I’m talking about you. In Java, if your function throws an exception, it MUST have an exception specification that says so. (I’m strictly referring to checked exceptions here. Java also has unchecked exceptions. Those are harder to recover from and are exempt from the rules I’m discussing here.) If it doesn’t have an appropriate exception specification, you’ll get a compile time error. Also in Java, if your function calls another function that throws an exception, you MUST either catch the exception or have a compatible exception specification on your function. If you don’t, you’ll get a compile time error. The compiler holds your hand a bit here.

C++ doesn’t support such things. The compiler doesn’t care one way or the other if an exception is caught. It assumes, perhaps naively, that you know what you’re doing (also perhaps naively). C++ does have exception specifications. But historically, dynamic exception specifications like you see in Java never really helped us out in C++ land. They just muddied the waters. In C++, if a function doesn’t have an exception specification, it can throw anything it wants. On the other hand, if a function has a dynamic exception specification, it can only throw the types, or subtypes of the types, specified. Throwing anything else results in std::unexpected being called. Of course, this a runtime check, and it does nothing for us at compile time.

As you might expect, dynamic exception specifications fell out of favor in C++. They were so loathed that they were deprecated in C++11. All we’re left with in the C++11/14 era, is noexcept or nothing at all (which means anything can be thrown).

Another aspect of this “knowing what to catch can be tricky” thing is that there’s no universal base class. Sure, there’s std::exception. But that class was intended to be a base for exceptions thrown by the standard library. There’s nothing forcing you to use that for your own exception types. You can technically throw anything copyable in C++ – int, char *, std::vector, your own made-up type, etc.

So how do you do know what needs to be caught when you call another function? The compiler certainly won’t help you. Unfortunately, you must turn to documentation, comments in code, and in the worst case, wading through actual source code. Blech.

Exceptions incur a cost.

Nothing is free. When an exception occurs, there is a cost. Older compilers may impose some performance overhead when executing a block of code whether it throws or not. Modern compilers only incur a performance cost when the exception actually occurs. Exceptions should be rare. So if there’s no throw, there should be no performance penalty.

Even if there’s no performance hit, there will always be a size cost. When an exception occurs, the application needs to do more work to make the exception work than it would, say, a simple return statement. There’s more bookkeeping involved. And that means more code. In constrained embedded environments where every byte of program size is critical, this can be an automatic deal breaker.

Exceptions are easily abused as control flow mechanisms.

Have you ever seen code like this?

bool someCondition = false;
 
try
{
    if (checkSomeValue)
    {
        someCondition = true;
        throw 0;
    }
    if (checkSomeOtherValue)
    {
        someCondition = true;
        throw 0;
    }
    if (checkSomeOtherOtherValue)
    {
        someCondition = true;
        throw 0;
    }
 
    // ... More of the same
}
catch (...)
{
}
 
if (someCondition)
{
    // do something that relies on someCondition being true
}

This is just one example where an exception could be used to control the flow of code. It kind of works like a break. And it’s so very wrong. Why? When exceptions are thrown, the application suffers both a size cost and a performance cost. Performance-wise, this code is much slower than it should be.

How about this one?

try
{
    int a = std::stoi(someInput);
    printInt(a);
}
catch (const std::logic_error &)
{
    std::string b = someInput;
    printString(b);
}

Again, here we’re leveraging exceptions for the happy path. The code is slower than it should be. Remember, exceptions should be the exception. We’d actually get better performance in this example with error-codes.

Something that’s guaranteed not to throw today may not have such a guarantee tomorrow.

Once a function is declared nothrow and starts being used, its interface SHOULD be set in stone. Consumers of that function will expect it to never throw. It’s easy to amass a large body of code dependent on such a contract. So what happens when someone comes along and decides said function really needs to throw an exception? Probably a significant amount of effort expended visiting every place in the source code where that function is called. It’s that or or nothing at all, fingers-crossed, and hope for the best. What if that function is a virtual method in a base class? What if the function is part of an API with consumers beyond your reach? There be dragons.

To declare a function nothrow requires careful consideration and perhaps even some fortune-telling abilities. Most folks generally don’t bother with nothrow functions unless they’re intended for use with things like std::swap or std::move. Tread lightly.

Conclusion

Those are the big-ticket items, in my opinion. I’m sure some of you will have your own pro/cons that you feel should be added to the list. I encourage you to leave them in comments below.

So where do I lie on the love/hate spectrum? It depends. There’s a time and a place for everything. And exceptions are no exception (bah dum dum). Exceptions work great in some scenarios (e.g., a modern C++ codebase designed with exceptions in mind) and poor in others (e.g., a legacy C codebase, a library with bindings to other languages, etc.). As Kenny Rogers said, “You gotta know when to hold ’em , know when to fold ’em.” Experience will be your guide.

If you’d like to learn how to be better at writing exception-safe code, below are a few resources for you.

Jon Kalb’s presentations from CppCon 2014 are excellent. He does a deep dive into modern C++ exception handling. Highly recommended.

Jon Kalb’s “Exception Safe Code Part 1”
Jon Kalb’s “Exception Safe Code Part 2”
Jon Kalb’s “Exception Safe Code Part 3”

Scott Meyers covers a great number of exception-related best practices in his Effective C++ books (Effective C++, More Effective C++, and Effective Modern C++), all of which are required reading for any C++ developer.

Andrei Alexandrescu gave the presentation “Systematic Error Handling in C++” at the “C++ and Beyond” seminar back in 2012 thats very much still relevant today. In this talk, he explains a mechanism to bridge the error code/exception worlds with Expected and touches upon a newer version of ScopeGuard (something you should be intimately familiar with).

Good luck. And happy coding.

Making Progress on the Windows Taskbar

We’ve all seen those programs that show progress on the Windows taskbar. WinRAR does it when creating or extracting RAR files. Firefox does it when downloading files. Windows Explorer does it when copying or moving files. Perhaps you’ve seen this and thought, “I wonder how I could do this in my own application.” It certainly adds polish. And as a user, being able to easily monitor a long running task while surfing the Interwebs has a lot of value. In this blog entry, I’m going to show you how to do it.

Prior to Windows 7, most applications would accomplish similar feats by manipulating window titles or system tray icons. It was kludgy. But it got the job done. Believe it or not, Windows 7 was the first version of Windows that gave us platform support for showing progress on the taskbar. It’s one of those silly little features we developers never knew we needed. After all, we thought we already had it figured out. But now that I know it’s there, I want to use it all of the time.

The key to showing taskbar progress is the ITaskbarList3 COM interface. It extends ITaskbarList and ITaskbarList2 interfaces, which are Windows 2000 and Windows XP era, respectively. As you can tell from the API docs, both ITaskbarList and ITaskbarList2 are pretty boring. It wasn’t until ITaskbarList3 showed up that things started getting interesting with the taskbar. It’s with ITaskbarList3 that we have the ability to manipulate things like overlay icons, thumbnail images, and progress, which is what we’re concerned with in this article.

(Note: If you’re a Windows developer and not experienced with COM, I strongly advise that you educate yourself on the topic. Much of the Windows platform is exposed through COM interfaces. There’s not much beyond the core Win32 API that you can do without dipping your toes into the world of COM. If you’re interested in learning more, I recommend picking up “Essential COM” by Don Box. It’s an older book (circa 1998). But it’s still the best introduction to COM and remains relevant after all of these years.)

I’m now going to demonstrate how to use ITaskbarList3 for showing progress by creating a simple wrapper class called TaskBarProgress. This is what the class declaration looks like.

class TaskBarProgress
{
    public:
 
        //! Constructor.
        TaskBarProgress(HWND hWnd) : m_hWnd(hWnd), 
            m_pTaskBarList3(NULL) {}
        //! Destructor.
        virtual ~TaskBarProgress() { _ASSERT(m_pTaskBarList3 == NULL); }
 
        //! Starts "progress mode".
        void startProgressMode();
        //! Ends "progress mode".
        void endProgressMode();
        //! Sets the current progress.
        void setProgress(ULONGLONG progressValue, ULONGLONG progressTotal);
 
    private:
 
        // We don't want the default implementations of the copy constructor 
        // and assignment operator because we have a COM pointer involved. 
        // Copying that without doing the appropriate AddRef'ing would be 
        // hazardous to our health. Let's just leave these out unless we
        // find we need them at a later date.
 
        //! Copy constructor. NOT IMPLEMENTED.
        TaskBarProgress(const TaskBarProgress &);
        //! Assignment operator. NOT IMPLEMENTED.
        const TaskBarProgress & operator=(const TaskBarProgress &);
 
        //! The window for which we're showing progress.
        HWND m_hWnd;
        //! The ITaskbarList3 implementer.
        ITaskbarList3 *m_pTaskBarList3;
};

TaskBarProgress is fairly simple. It has two member variables – m_hWnd and m_pTaskBarList3. Why do we need a window handle? When setting progress, ITaskbarList3 needs to know what window we’re setting progress on. So TaskBarProgress accepts the window handle as an argument to the constructor, which then gets stashed in m_hWnd.

The copy constructor and assignment operator are not implemented here. This is meant to keep us from shooting ourselves in the foot with COM reference count bookkeeping. For demo purposes (and probably for most practical purposes), we don’t need to support copies of this class.

The three methods startProgressMode(), endProgressMode(), and setProgress() are the interesting methods here. They do what their names suggest. When you want to begin displaying progress on the taskbar, you call startProgressMode(). As progress is made, you update the progress value by calling setProgress(). And when you’re all finished, tidy things up by calling endProgressMode().

Starting Progress

The implementation for startProgressMode() looks like so.

void TaskBarProgress::startProgressMode()
{
    if (!m_hWnd)
        return;
 
    if (!m_pTaskBarList3)
    {
        HRESULT hr = ::CoCreateInstance(CLSID_TaskbarList, NULL, 
            CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (void **)&m_pTaskBarList3);
 
        if (hr != S_OK)
            return; // Not a supported platform. Nothing we can do.
    }
 
    // "Turning on" progress mode and setting the initial progress value to 0.
    m_pTaskBarList3->SetProgressState(m_hWnd, TBPF_NORMAL);
    m_pTaskBarList3->SetProgressValue(m_hWnd, 0, 100);
}

We first obtain a pointer to the ITaskbarList3 interface if we need to and verify that it was successful (this will fail on older platforms like Vista or XP). If it’s successful, we then enable the progress state with a call to ITaskbarList3::SetProgressState(). ITaskbarList3::SetProgressState() accepts two arguments – a window handle and the state flag. If you peruse the documentation for this method, you’ll find 5 different flags you can use for the state. These are described below.

TBPF_NOPROGRESS This is the normal taskbar mode. If a progress bar is present on the taskbar, setting this flag will dismiss it. You’ll notice we set this flag in our endProgressMode() method described later.
TBPF_INDETERMINATE This is what it sounds like. It means you have something going on that you want to show progress for, but you have no idea how to track a progress value for it. For example, imagine calling a third party library function that takes a long time to finish. You may not be able to receive progress notifications from it, so you have no way of knowing when it’ll be finished until it’s actually done. This is when you might use this flag.
TBPF_NORMAL Despite being called “normal”, this is actually the “in-progress” state. When you send this flag, the taskbar icon will begin showing progress.
TBPF_ERROR Turns the icon red to indicate an error has occurred.
TBPF_PAUSED Turns the icon yellow to indicate an action is needed before more progress can be made.

For this article, we’re only concerned with TBPF_NORMAL and TBPF_NOPROGRESS.

After we set the progress mode in startProgressMode(), we initialize the progress value to 0 with a call to ITaskbarList3::SetProgressValue(). This isn’t absolutely necessary, but it’s a good practice.

Updating Progress

Updating the progress value occur in our setProgress method. This method has two parameters – progressValue and progressTotal. progressValue is whatever the current progress value is and progressTotal is the maximum value the progress value can be. Both of these parameters are passed straight through to ITaskbarList3::SetProgressValue()

void TaskBarProgress::setProgress(ULONGLONG progressValue, ULONGLONG progressTotal)
{
    if (!m_pTaskBarList3 || !m_hWnd)
        return;
 
    m_pTaskBarList3->SetProgressValue(m_hWnd, progressValue, progressTotal);
}

Ending Progress Mode

Ending progress occurs in our endProgressMode() method. This method simply sets the state value to TBPF_NOPROGRESS as described above and then releases the ITaskbarList3 pointer.

void TaskBarProgress::endProgressMode()
{
    if (!m_pTaskBarList3 || !m_hWnd)
        return;
 
    m_pTaskBarList3->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
    m_pTaskBarList3->Release();
    m_pTaskBarList3 = NULL;
}

Demo

And that’s all there is to it. We’ll demonstrate the use of this class with a simple console-based application (yes, consoles can have progress too!).

int _tmain(int argc, _TCHAR* argv[])
{
    CoInitialize(NULL);
 
    HWND hwnd = ::GetConsoleWindow();
    TaskBarProgress progress(hwnd);
 
    progress.startProgressMode();
    for (int i = 0; i < 20; ++i)
    {
        progress.setProgress(i, 20);
        Sleep(200);
    }
    progress.endProgressMode();
 
    CoUninitialize();
 
    return 0;
}

The first thing we do here is initialize COM with a call to CoInitialize(). You MUST do this before doing anything COM-related.

After initializing COM, we get a handle to the console window. We use that handle to construct an instance of TaskBarProgress. We then call the instance’s startProgressMode() and enter a loop that iterates 20 times. Each time through the loop, TaskBarProgress’s setProgress() method is called with the current progress value and then we sleep for 200 milliseconds. Once the the loop has finished, the TaskBarProgress’s endProgressMode() method is called.

Just before exiting, COM is uninitialized.

If you build this and run it from the console, you’ll see that the console window taskbar icon shows progress while the application is running and the icon returns to normal just before the application terminates.

Go Forth and Create Progress

The code for this article can be downloaded here. The sample was written and built using the Visual Studio 2012 Express edition. If you’re using an older platform SDK, you may need to update it in order to obtain definitions for ITaskbarList3. If you’ve included shobjidl.h and the compiler complains about missing definitions for ITaskbarList3, CLSID_TaskbarList, and/or IID_ITaskbarList3, that’s usually a good indication you need to update.

And that’s it! The ITaskbarList3 interface is pretty simple. You may not feel the need to wrap it in a class at all. That’s fine. Or you may have a better idea of how to wrap it (a scope-based progress class, perhaps?). That’s fine too. In any case, adding support for taskbar icon progress will definitely add some polish to whatever project you may be working on. Have fun with it!

-Shane

Naming Linux Threads

Here’s a quickie tip for all you C/C++ developers working on multithreaded apps in Linux. If you find yourself frequently scratching your head over which thread is which when monitoring your application using, say, top or strace, try naming your threads!

#include 

void myThreadFunc()
{
    prctl( PR_SET_NAME, "WorkerThread", 0, 0, 0 );
    // Other interesting code
}

At runtime, you can easily correlate thread names with an ID by taking a peek at /proc/[PID]/task/[THREADID]/stat or /proc/[THREADID]/stat. Just make sure your thread names are 16 characters or less (see PR_SET_NAME documentation). And it probably goes without saying, but call prctl() from within the thread you wish to name.

This little trick saved my butt the last couple of days. Maybe it’ll help you too.