C++ OpenGL – Multithreading and Throwing Destructors in OpenGL

cmultithreadingopengl

How do you make an class that properly warns a developer in the future that they've made a mistake somewhere in their implementation that resulted in an object that gets deconstructed in a state that prevents the release of it's resources?

Background:

I recently upgraded to Visual Studio 2015 and began reloading and compiling code for a game engine I'm working on and ran into a new series of warnings "warning C4297: '*': function assumed not to throw an exception but does". A quick search revealed a C++ convention that I'd missed and the reasons behind said convention: destructors should not throw exceptions. I also can't really argue with the reasons, but I'm also not sure how to work around the problem.

Within OpenGL a Context basically holds all of the state information for the OpenGL engine. Only one thread may have a context at any given time and each thread may only have one current context. When the engine starts it creates the context and then relinquishes control over the context and starts up another thread which picks it up and proceeds to handle the graphics rendering for the engine. To handle all of this, I created a graphics engine class that uses semantics similar to a mutex to claim and relinquish the graphics engine and make sure that no mistakes are made that might some day result in someone attempting to do things with a context that it doesn't own.

During destruction, the graphics engine and a number of other classes that rely on it all check to make sure that the current thread has claimed the graphics engine before they perform actions that are necessary in their destruction. If the thread didn't have the graphics context claimed, the destructor was throwing. My goal was really to provide some basic protection against the class being used improperly on accident in the future, not to make the graphics engine thread-safe. Now… I'm uncertain of how best to handle this.

I've contemplated just switching over to a mutex-based approach which I could use to block access to the graphics context until a thread was done, possibly making the graphics engine class fully multi-threading capable (not that I can understand why you'd want to perform multi-threading with an OpenGL context, as the calls needed to do so are expensive enough to negate any benefit you might get out of it from what I understand).

The most tempting option has been to just log an error terminate any thread that attempts to misuse the class. Unfortunately, I can't find an OS-independent way of terminating just the current thread. If I was to go this route, I'd have to look up OS-appropriate ways to terminate the current threads.

I'm also not certain that I'm not being overly paranoid. Maybe I should just document the proper use of the class and if someone misuses it let them and hope that they're able to figure out why their application isn't doing what it's supposed to. I'm also worried about myself being the fool who misuses the class some day in the future.

Best Answer

So I understand that you want to be able to check for these potential problems with the thread not owning the context, but how will throwing an exception help? How do you plan to recover from the problem? And at what point in the program? My guess is that in general you can't fix the problem by the time this has happened, its just a major structural error in the program. So why not just make the best error report you can and then call std::abort, instead of throwing an exception?

Throwing exceptions from destructors violates the fundamental idea of C++ error handling, which is that destructors are used to clean up objects when an exception is thrown and the stack is being unwound. If one of those destructors throws an exception during stack unwinding for a different exception, so that there are two unresolved exceptions at the same scope, your program is terminated. In C++11, to make it safer, your program usually terminates whenever a destructor throws an exception, unless you take special boiler-plate steps to allow it. There are lots of other big problems with destructors that throw -- I have yet to see a situation where it's a good idea, or expedient in any way to make a throwing destructor.