Skip to content
This repository has been archived by the owner on Feb 11, 2021. It is now read-only.

Commit

Permalink
Clean up assertion API for public use.
Browse files Browse the repository at this point in the history
  • Loading branch information
icculus committed Jan 13, 2010
1 parent febea1a commit 6878444
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 23 deletions.
64 changes: 63 additions & 1 deletion include/SDL_assert.h
Expand Up @@ -109,7 +109,7 @@ typedef struct SDL_assert_data
const char *filename;
int linenum;
const char *function;
struct SDL_assert_data *next;
const struct SDL_assert_data *next;
} SDL_assert_data;

/* Never call this directly. Use the SDL_assert* macros. */
Expand Down Expand Up @@ -166,6 +166,68 @@ extern DECLSPEC SDL_assert_state SDLCALL SDL_ReportAssertion(SDL_assert_data *,
# error Unknown assertion level.
#endif


typedef SDL_assert_state (SDLCALL *SDL_AssertionHandler)(
const SDL_assert_data *, void *userdata);

/**
* \brief Set an application-defined assertion handler.
*
* This allows an app to show its own assertion UI and/or force the
* response to an assertion failure. If the app doesn't provide this, SDL
* will try to do the right thing, popping up a system-specific GUI dialog,
* and probably minimizing any fullscreen windows.
*
* This callback may fire from any thread, but it runs wrapped in a mutex, so
* it will only fire from one thread at a time.
*
* Setting the callback to NULL restores SDL's original internal handler.
*
* This callback is NOT reset to SDL's internal handler upon SDL_Quit()!
*
* \return SDL_assert_state value of how to handle the assertion failure.
*
* \param handler Callback function, called when an assertion fails.
* \param userdata A pointer passed to the callback as-is.
*/
extern DECLSPEC void SDLCALL SDL_SetAssertionHandler(
SDL_AssertionHandler handler,
void *userdata);

/**
* \brief Get a list of all assertion failures.
*
* Get all assertions triggered since last call to SDL_ResetAssertionReport(),
* or the start of the program.
*
* The proper way to examine this data looks something like this:
*
* <code>
* const SDL_assert_data *item = SDL_GetAssertionReport();
* while (item->condition) {
* printf("'%s', %s (%s:%d), triggered %u times, always ignore: %s.\n",
* item->condition, item->function, item->filename,
* item->linenum, item->trigger_count,
* item->always_ignore ? "yes" : "no");
* item = item->next;
* }
* </code>
*
* \return List of all assertions. This never returns NULL,
* even if there are no items.
* \sa SDL_ResetAssertionReport
*/
extern DECLSPEC const SDL_assert_data * SDLCALL SDL_GetAssertionReport(void);

/**
* \brief Reset the list of all assertion failures.
*
* Reset list of all assertions triggered.
*
* \sa SDL_GetAssertionReport
*/
extern DECLSPEC void SDLCALL SDL_ResetAssertionReport(void);

/* Ends C function definitions when using C++ */
#ifdef __cplusplus
/* *INDENT-OFF* */
Expand Down
78 changes: 56 additions & 22 deletions src/SDL_assert.c
Expand Up @@ -23,8 +23,6 @@
#include "SDL.h"
#include "SDL_assert.h"

#if (SDL_ASSERT_LEVEL > 0)

#ifdef _WINDOWS
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
Expand All @@ -34,13 +32,19 @@
#include <unistd.h>
#endif

/* We can keep all triggered assertions in a singly-linked list so we can
static SDL_assert_state
SDL_PromptAssertion(const SDL_assert_data *data, void *userdata);

/*
* We keep all triggered assertions in a singly-linked list so we can
* generate a report later.
*/
#if !SDL_ASSERTION_REPORT_DISABLED
static SDL_assert_data assertion_list_terminator = { 0, 0, 0, 0, 0, 0, 0 };
static SDL_assert_data *triggered_assertions = &assertion_list_terminator;
#endif

static SDL_mutex *assertion_mutex = NULL;
static SDL_AssertionHandler assertion_handler = SDL_PromptAssertion;
static void *assertion_userdata = NULL;

#ifdef __GNUC__
static void
Expand Down Expand Up @@ -198,27 +202,30 @@ SDL_PromptAssertion_windows(const SDL_assert_data *data)

static void SDL_AddAssertionToReport(SDL_assert_data *data)
{
#if !SDL_ASSERTION_REPORT_DISABLED
/* (data) is always a static struct defined with the assert macros, so
we don't have to worry about copying or allocating them. */
if (data->next == NULL) { /* not yet added? */
data->next = triggered_assertions;
triggered_assertions = data;
}
#endif
}


static void SDL_GenerateAssertionReport(void)
{
#if !SDL_ASSERTION_REPORT_DISABLED
if (triggered_assertions != &assertion_list_terminator)
{
SDL_assert_data *item = triggered_assertions;
const SDL_assert_data *item;

/* only do this if the app hasn't assigned an assertion handler. */
if (assertion_handler != SDL_PromptAssertion)
return;

item = SDL_GetAssertionReport();
if (item->condition)
{
debug_print("\n\nSDL assertion report.\n");
debug_print("All SDL assertions between last init/quit:\n\n");

while (item != &assertion_list_terminator) {
while (item->condition) {
debug_print(
"'%s'\n"
" * %s (%s:%d)\n"
Expand All @@ -232,9 +239,8 @@ static void SDL_GenerateAssertionReport(void)
}
debug_print("\n");

triggered_assertions = &assertion_list_terminator;
SDL_ResetAssertionReport();
}
#endif
}

static void SDL_ExitProcess(int exitcode)
Expand All @@ -253,12 +259,15 @@ static void SDL_AbortAssertion(void)
}


static SDL_assert_state SDL_PromptAssertion(const SDL_assert_data *data)
static SDL_assert_state
SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
{
const char *envr;
SDL_assert_state state = SDL_ASSERTION_ABORT;
SDL_WindowID window;

(void) userdata; /* unused in default handler. */

debug_print("\n\n"
"Assertion failure at %s (%s:%d), triggered %u time%s:\n"
" '%s'\n"
Expand Down Expand Up @@ -291,6 +300,7 @@ static SDL_assert_state SDL_PromptAssertion(const SDL_assert_data *data)
if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
SDL_MinimizeWindow(window);
} else {
/* !!! FIXME: ungrab the input if we're not fullscreen? */
/* No need to mess with the window */
window = 0;
}
Expand Down Expand Up @@ -344,8 +354,6 @@ static SDL_assert_state SDL_PromptAssertion(const SDL_assert_data *data)
}


static SDL_mutex *assertion_mutex = NULL;

SDL_assert_state
SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
int line)
Expand Down Expand Up @@ -391,7 +399,7 @@ SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
}

if (!data->always_ignore) {
state = SDL_PromptAssertion(data);
state = assertion_handler(data, assertion_userdata);
}

switch (state)
Expand All @@ -417,8 +425,6 @@ SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
return state;
}

#endif /* SDL_ASSERT_LEVEL > 0 */


int SDL_AssertionsInit(void)
{
Expand All @@ -428,13 +434,41 @@ int SDL_AssertionsInit(void)

void SDL_AssertionsQuit(void)
{
#if (SDL_ASSERT_LEVEL > 0)
SDL_GenerateAssertionReport();
if (assertion_mutex != NULL) {
SDL_DestroyMutex(assertion_mutex);
assertion_mutex = NULL;
}
#endif
}

void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
{
if (handler != NULL) {
assertion_handler = handler;
assertion_userdata = userdata;
} else {
assertion_handler = SDL_PromptAssertion;
assertion_userdata = NULL;
}
}

const SDL_assert_data *SDL_GetAssertionReport(void)
{
return triggered_assertions;
}

void SDL_ResetAssertionReport(void)
{
SDL_assert_data *item = triggered_assertions;
SDL_assert_data *next = NULL;
for (item = triggered_assertions; item->condition; item = next) {
next = (SDL_assert_data *) item->next;
item->always_ignore = SDL_FALSE;
item->trigger_count = 0;
item->next = NULL;
}

triggered_assertions = &assertion_list_terminator;
}

/* vi: set ts=4 sw=4 expandtab: */

0 comments on commit 6878444

Please sign in to comment.