How to securely exit program from a function?

I would like to know how to exit from a function like you would with exit().

In Ryan Gordon’s tutorial series, he writes it like this:

#if defined(__GNUC__) || defined(__clang__)
static void panic_and_abort(const char *title, const char *text) __attribute__((noreturn));
#endif

static void panic_and_abort(const char *title, const char *text)
{
    fprintf(stderr, "PANIC: %s ... %s\n", title, text);
    SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, text, window);
    SDL_Quit();
    exit(1);
}

I am confused because in a secret function called SDL_ExitProcess that is intentionally left out of the header file so “no one uses it without EXTREMELY good reason”, there is some platform-specific exit handling which looks good, and ultimately boils down to just calling exit() most of the time.

Soooo, how does one oficially exit from a function, and why is the function that actually handles this secretly hidden?

Thank you!

1 Like

I don’t understand what exactly you want to know. If you want to exit a function, use return, and if you want to exit the process immediately, use a function in your language that allows it. This panic_and_abort function does nothing out of the ordinary — it logs and shows an error, shuts down SDL and kills the process.

Killing a process should only be used when it makes sense, i.e. when the process needs to be terminated immediately without freeing its resources manually (after killing a process, the OS will free everything automatically). When this applies depends on the context.

For example, this way you can quickly and conveniently end the game process after any critical error that causes the game to not work properly (e.g. missing mandatory game data files) and instead of programming error handling and manually freeing resources, one instruction ends everything.

It is not dangerous, but it has its consequences. For example, if you are debugging your game with memory leak trackers, killing the process means that resources have not been freed by the game process, so any dynamically allocated memory block will be reported as a leak (hundreds or even hundreds of thousands). Such data can be printed in the terminal, but it can also be written to a log file (the log can be gigantic, which is not good).

I think he means “exit [the process] from within a function”.

I think my answer explains all these aspects. The standard libraries, depending on the language, provide at least one function to terminate the process. In C there is exit, in C++ there is also abort, in Pascal there is Halt (and RunError which calls Halt internally), in Go there is os.Exit etc.

However, it should be borne in mind that this is not a typical program closure, but a forced termination of the process. Such a solution is used in specific cases, which depends on many design decisions and the technology used.

Can someone who actually knows something on the matter chime in, please? :face_exhaling:

2 Likes

Mhm, so you think I don’t know much about it. Sorry to hear my answer didn’t help you.

Both C and C++ have both exit and abort. Apparently there is also quick_exit and _Exit, and a POSIX function named _exit.

I’m certainly not an expert on this but from what I can gather the difference seems to be that:

exit causes “normal termination”. I guess this makes it similar to what happens when reaching the end of main(). Functions registered with atexit will be executed, standard streams are flushed, etc. In C++ it calls destructors of “global” objects (but not for local objects).

abort generates a SIGABRT signal that if not caught causes “abnormal termination” (I guess this means your OS might show a message that it crashed). This does not call functions registered with atexit and whether standard streams are flushed is implementation-defined. In C++ no destructors are called.

quick_exit causes “normal termination” but does not call functions registered with atexit. Instead it calls functions registered with at_quick_exit. In C++ it doesn’t seem to call any destructors.

_Exit seems similar to quick_exit but does not call functions registered with at_quick_exit.

_exit seems to be a POSIX synonym for _Exit.

Now, looking at the “secret” SDL_ExitProcess function it seems like it essentially calls _Exit or similar. I don’t know why it calls TerminateProcess on Windows but as far as I can understand it seems to do something similar.

I think programs are normally recommended to terminate by calling exit or let the execution reach the end of main. I guess that might be why they have hidden this function, because if people saw it they might think it is the preferred way to terminate the program, which I don’t think it is.

This is mostly speculation. Someone who is involved in the SDL development process might be able to give a better answer…

SDL has wrappers for all the standard library functions, where it does platform-specific stuff.
Everyone always says to only use SDL_ functions.

So it is obvious that some SDL_Exit() should exist.
And it kind of does in the form of SDL_ExitProcess(), which shows that there is indeed a need for platform-specific exit functions.

So everything points to exit() not being the “correct” way of exiting the program.
Going back to main to return 0 is probably the most correct approach, but I refuse to believe there is no official exit-like functionality.

2 Likes

There are many standard functions that doesn’t have a SDL equivalent, especially if you use the latest C standard, or if you use C++.

Who is everyone? I have not heard this advice.

For example, if you write a program using C99 then why use SDL_round when you can use the standard round function?

Searching the SDL code base for SDL_ExitProcess I only find it being used in two places.

1. When failing to initialize a dynamic API:

/* Now we're screwed. Should definitely abort now. */
dynapi_warn("Failed to initialize internal SDL dynapi. As this would otherwise crash, we have to abort now.");
SDL_ExitProcess(86);

2. When there is an assertion inside an assertion (or something like that)

assertion_running++;
if (assertion_running > 1) { /* assert during assert! Abort. */
    if (assertion_running == 2) {
        SDL_AbortAssertion(); <-- This also calls SDL_ExitProcess internally
    } else if (assertion_running == 3) { /* Abort asserted! */
        SDL_ExitProcess(42);
    } else {
        while (1) { /* do nothing but spin; what else can you do?! */
        }
    }
}

These seems to be quite serious and unexpected situations. The sort of “exit” that is most appropriate in these situations might not be most appropriate in other situations.

Well, you’ve got exit. It’s in both the C and C++ standard. What’s more “official” than that?

1 Like

I found this post by “slouken” (i.e. Sam Lantinga, the name you see in all SDL copyright notices) which I think is relevant to this discussion.

2 Likes