cerror-handlingexitexit-codeatexit

C exit codes and atexit() in a realtime / non-batch app


I'm writing a game in which a number of different conditions can cause a failure, for example an image or shader failed to load, OpenGL failed to get a valid context, etc.

In an ideal world, I'd like to use exit codes in the way I believe was intended for C, i.e. non-zero for failure conditions. However, there are some factors that play into this:

What arguments can be made for and against using non-zero exit codes in my situation?


Solution

  • Triggering registered atexit() functions

    Contrary to your question, functions registered with atexit() are still called even when the program attempts to return a non-zero termination status to the host environment. A call to exit(), or the main() function returning, will trigger the functions registered with atexit(), regardless of the value given.

    Example:

    #include <stdlib.h>
    #include <stdio.h>
    
    void print_stuff(void)
    {
        puts("Stuff");
    }
    
    int main(void)
    {
        atexit(print_stuff);
        exit(1);
    }
    

    This will print Stuff, even though 1 is returned.

    Technical details

    By the ISO C standard, the functions registered in atexit() are called after exit() is called. The following situations are also defined as calling exit() (and therefore triggering the functions registered with atexit()):

    1. main() returning is equivalent to calling exit().

    2. After the final thread calls thrd_exit(), exit(EXIT_SUCCESS) is called.

    The following are possible implementation-defined sources of calling exit():

    1. The default signal handler for SIGTERM.

    2. The default constraint handler before using set_constraint_handler_s().

    The standard mentions the following situations where exit() and the calls to functions registered with atexit() are circumvented:

    1. An unhandled SIGABRT or a SIGABRT that finishes being handled by a function registered with signal(). SIGABRT can be raised by abort().

    2. Calling _Exit().

    3. Calling quick_exit().

    The host environment for your implementation may terminate the program in some situations without calling functions registered with atexit(), such as after a segfault.

    Regarding exit codes for a game

    Your choice of exit code shouldn't matter too much for a game. Yes, you won't be relying on a shell script to run your game and report errors to the user. Error feedback is probably more useful in the form of dialog popups, a log, or stderr for systems like Linux.