Cleanup and return from main in case of a crash or just display error message and exit?

Started by
7 comments, last by frob 7 years, 6 months ago

Hi everyone,

I'm creating a game engine project that gets exported as a dll file. My question is how should I handle crashes in my engine code? Lets say D3D11 fails to initialize how should I handle that? Should I print the error on a message box and then call "exit(EXIT_FAILURE);" when the user presses Ok without cleaning up anything and let the OS handle all the cleaning for me or should I clean up by calling every destructor on the way and exit through main?

Is this a good way of handling errors?


hr = D3D11CreateDeviceAndSwapChain(NULL,
        D3D_DRIVER_TYPE_HARDWARE,
        NULL,
        creationFlags,
        FeatureLevelsRequested,
	numLevelsRequested,
	D3D11_SDK_VERSION,
	&swapChainDesc,
	&swapChain,
	&device,
	&FeatureLevelsSupported,
	&context);

if (FAILED(hr))
{
    if(MessageBox(NULL, hr, "error", MB_OK) == IDOK)
        exit(EXIT_FAILURE);
} 

If not, what is a good logging system or crash handling system to put in my engine? Keep in mine all my engine code resides in a dll file.

Advertisement

It's generally a bad idea if your library code does things like call MessageBox, exit(), or force process termination. You should catch the error and propagate it out across your library's API boundary. You can do this with exceptions or by returning error codes, or both. This is less of a concern if you're going to be the only person consuming your DLL, but it's still worth thinking about.

Separately from this is the idea that crashes and errors aren't the same thing. Failure to initialize D3D should not crash anything. It should be an error that is gracefully reported to the consumer of the library and in turn gracefully reports to the end-user.

There are various interesting things you can do with actual crashes, such as trapping them via Structured Exception Handling and gathering process memory dumps which you then save or upload to a crash server or whatever for diagnostics.

So in your opinion, which one you would do? Throw an exception or return an error code?

I use both. I pick the one that I feel is most appropriate and useful for the specific context. Because throwing exceptions in C++ across DLL boundaries can be problematic due to C++'s lack of an ABI, I'd probably confine exceptions to the DLL itself and use error codes or some other kind of notification to report errors across the boundary if I were writing something generic where I couldn't guarantee for eternity that the same runtimes would be used.

Return error codes or similar result objects is generally a simpler solution that is less fraught with pitfalls, so I'd probably recommend that for your scenario.

Remember that you have more options than that. e.g., you can return richer monadic objects instead of simple C-like return code, among other things.

C/C++ sadly doesn't have the required language support for some of the more interesting error-handling primitives, but do please avoid believing in the false dichotomy between exceptions or C return coedes. :)

Sean Middleditch – Game Systems Engineer – Join my team!

So in your opinion, which one you would do? Throw an exception or return an error code?

Don't let exceptions leak out across DLL boundaries. Return an error code or error event.

Stephen M. Webb
Professional Free Software Developer

Alright, thanks everyone. ^_^

Your DLL is the game engine, right? In most cases that is NOT your user interaction layer. Even if the calling layer is another DLL you should let your interface to the user handle the display of messages to the user. Everything underneath should be lower level error codes, interfaces to other objects, and messages (not to the user, I'm talking system level stuff). Only the direct interface to the user should be interpreting codes and internal messages and presenting that information to the user in a user-friendly form. Depends on the architecture and code of course but the differences are usually trivial unless you've taken a completely non-object oriented architecture.

Going along with some of the above, there are many options, not just those two.

My question is how should I handle crashes in my engine code?

You fix the issue. If the code is crashing it is a critically serious A-level red-flag bug.

How you get around to fixing the issue is up to you.

Game engines should never crash when used as designed. If the person violates the design, perhaps passing invalid pointers or garbage values, the code's response should vary.

Leverage the fact that debug builds, testing builds, and final builds are different and can contain different error handlers.

DURING DEVELOPMENT

If this engine crash is during development you want it to fail noisily and provide all the information needed to the development team. This typically means a crash dump and a big ugly message. Write crash handlers for all the ways the program can die and make them create minidumps, and make it very easy for the person to get that minidump to the developer.

In most of the professional world, a crash bug is a critical bug that prevents shipping. If your game is being released on a game console then any crash will automatically fail the game certification process. When discovered this way the cost to a studio for a single crash is thousands of dollars. It costs time and salary to fix the bug, time and money to go back through certification, and possibly money if shipping dates are missed or in jeopardy. Of course, a console game crashing in the wild can end up destroying the entire product, so don't do that.

Note that an engine non-crash condition doesn't deserve that same response. During development you can provide big feedback to the developer that they are doing it wrong, abort the action, then attempt to continue. It may be a big warning "Error parsing data file <whatever.json> line 123", or "Code is using graphics functions after D3D11 surface was already reported as broken. FIX IT! Ignoring graphics call." During development you want these conditions to be called out visibly.

DURING MAINTENANCE

If the engine crash is during a product's maintenance cycle then you want it reported with all information needed to fix the bug. For game clients this typically means a crash dump and auto-uploading so you can correct and fix it. For servers this often means a bunch of emails auto-generated to the dev team with the dump attached or referenced.

For non-crash conditions during maintenance your engine should attempt to fail gracefully. This means you start logging the issues and provide a way to upload them. This is commonly done by games that have a checkbox like: "Report anonymous usage statistics to make the game better" If you don't run a server or can't run such a server in practice, this can be done with an exit handler that politely says "Problems were detected during gameplay, please email this log file to the developers" or something similar.

After you're done with product maintenance it doesn't really matter what you do.

Lets say D3D11 fails to initialize how should I handle that?

That is a known failure path, and it can happen for an enormous number of reasons. That should never be a crash by itself.

If your program is crashing due to this known and expected condition, that crash is a serious critical A-level red-flag bug. Identify your bug and fix it.

If someone else's code is crashing due to this known and expected condition, if your engine has the ability to detect and stop the behavior it should do so as described above. During development that means hard-to-ignore messages that get the defect fixed, and after development it means providing methods for the program to be patched.

For known failure paths generally a return value is sufficient. That is how DirectX itself handles it, and how most other major libraries handle similar events.

This topic is closed to new replies.

Advertisement