src/SDL_assert.c
author Ryan C. Gordon
Mon, 23 Jan 2017 16:45:50 -0500
changeset 10840 3c3708a0b217
parent 10737 3406a0f8b041
child 11015 b00c4088f687
permissions -rw-r--r--
audio: Resampler now special-cases stereo and mono processing.

Turns out that iterating from 0 to channels-1 was a serious performance hit!

These cases now tend to match or beat the original audio resampler's speed!
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "./SDL_internal.h"
    22 
    23 #if defined(__WIN32__)
    24 #include "core/windows/SDL_windows.h"
    25 #endif
    26 
    27 #include "SDL.h"
    28 #include "SDL_atomic.h"
    29 #include "SDL_messagebox.h"
    30 #include "SDL_video.h"
    31 #include "SDL_assert.h"
    32 #include "SDL_assert_c.h"
    33 #include "video/SDL_sysvideo.h"
    34 
    35 #ifdef __WIN32__
    36 #ifndef WS_OVERLAPPEDWINDOW
    37 #define WS_OVERLAPPEDWINDOW 0
    38 #endif
    39 #else  /* fprintf, _exit(), etc. */
    40 #include <stdio.h>
    41 #include <stdlib.h>
    42 #if ! defined(__WINRT__)
    43 #include <unistd.h>
    44 #endif
    45 #endif
    46 
    47 static SDL_assert_state
    48 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata);
    49 
    50 /*
    51  * We keep all triggered assertions in a singly-linked list so we can
    52  *  generate a report later.
    53  */
    54 static SDL_assert_data *triggered_assertions = NULL;
    55 
    56 static SDL_mutex *assertion_mutex = NULL;
    57 static SDL_AssertionHandler assertion_handler = SDL_PromptAssertion;
    58 static void *assertion_userdata = NULL;
    59 
    60 #ifdef __GNUC__
    61 static void
    62 debug_print(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
    63 #endif
    64 
    65 static void
    66 debug_print(const char *fmt, ...)
    67 {
    68     va_list ap;
    69     va_start(ap, fmt);
    70     SDL_LogMessageV(SDL_LOG_CATEGORY_ASSERT, SDL_LOG_PRIORITY_WARN, fmt, ap);
    71     va_end(ap);
    72 }
    73 
    74 
    75 static void SDL_AddAssertionToReport(SDL_assert_data *data)
    76 {
    77     /* (data) is always a static struct defined with the assert macros, so
    78        we don't have to worry about copying or allocating them. */
    79     data->trigger_count++;
    80     if (data->trigger_count == 1) {  /* not yet added? */
    81         data->next = triggered_assertions;
    82         triggered_assertions = data;
    83     }
    84 }
    85 
    86 
    87 static void SDL_GenerateAssertionReport(void)
    88 {
    89     const SDL_assert_data *item = triggered_assertions;
    90 
    91     /* only do this if the app hasn't assigned an assertion handler. */
    92     if ((item != NULL) && (assertion_handler != SDL_PromptAssertion)) {
    93         debug_print("\n\nSDL assertion report.\n");
    94         debug_print("All SDL assertions between last init/quit:\n\n");
    95 
    96         while (item != NULL) {
    97             debug_print(
    98                 "'%s'\n"
    99                 "    * %s (%s:%d)\n"
   100                 "    * triggered %u time%s.\n"
   101                 "    * always ignore: %s.\n",
   102                 item->condition, item->function, item->filename,
   103                 item->linenum, item->trigger_count,
   104                 (item->trigger_count == 1) ? "" : "s",
   105                 item->always_ignore ? "yes" : "no");
   106             item = item->next;
   107         }
   108         debug_print("\n");
   109 
   110         SDL_ResetAssertionReport();
   111     }
   112 }
   113 
   114 
   115 static SDL_NORETURN void SDL_ExitProcess(int exitcode)
   116 {
   117 #ifdef __WIN32__
   118     ExitProcess(exitcode);
   119 #else
   120     _exit(exitcode);
   121 #endif
   122 }
   123 
   124 
   125 static SDL_NORETURN void SDL_AbortAssertion(void)
   126 {
   127     SDL_Quit();
   128     SDL_ExitProcess(42);
   129 }
   130 
   131 
   132 static SDL_assert_state
   133 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
   134 {
   135 #ifdef __WIN32__
   136     #define ENDLINE "\r\n"
   137 #else
   138     #define ENDLINE "\n"
   139 #endif
   140 
   141     const char *envr;
   142     SDL_assert_state state = SDL_ASSERTION_ABORT;
   143     SDL_Window *window;
   144     SDL_MessageBoxData messagebox;
   145     SDL_MessageBoxButtonData buttons[] = {
   146         {   0,  SDL_ASSERTION_RETRY,            "Retry" },
   147         {   0,  SDL_ASSERTION_BREAK,            "Break" },
   148         {   0,  SDL_ASSERTION_ABORT,            "Abort" },
   149         {   SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
   150                 SDL_ASSERTION_IGNORE,           "Ignore" },
   151         {   SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
   152                 SDL_ASSERTION_ALWAYS_IGNORE,    "Always Ignore" }
   153     };
   154     char *message;
   155     int selected;
   156 
   157     (void) userdata;  /* unused in default handler. */
   158 
   159     message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   160     if (!message) {
   161         /* Uh oh, we're in real trouble now... */
   162         return SDL_ASSERTION_ABORT;
   163     }
   164     SDL_snprintf(message, SDL_MAX_LOG_MESSAGE,
   165                  "Assertion failure at %s (%s:%d), triggered %u %s:" ENDLINE
   166                     "  '%s'",
   167                  data->function, data->filename, data->linenum,
   168                  data->trigger_count, (data->trigger_count == 1) ? "time" : "times",
   169                  data->condition);
   170 
   171     debug_print("\n\n%s\n\n", message);
   172 
   173     /* let env. variable override, so unit tests won't block in a GUI. */
   174     envr = SDL_getenv("SDL_ASSERT");
   175     if (envr != NULL) {
   176         SDL_stack_free(message);
   177 
   178         if (SDL_strcmp(envr, "abort") == 0) {
   179             return SDL_ASSERTION_ABORT;
   180         } else if (SDL_strcmp(envr, "break") == 0) {
   181             return SDL_ASSERTION_BREAK;
   182         } else if (SDL_strcmp(envr, "retry") == 0) {
   183             return SDL_ASSERTION_RETRY;
   184         } else if (SDL_strcmp(envr, "ignore") == 0) {
   185             return SDL_ASSERTION_IGNORE;
   186         } else if (SDL_strcmp(envr, "always_ignore") == 0) {
   187             return SDL_ASSERTION_ALWAYS_IGNORE;
   188         } else {
   189             return SDL_ASSERTION_ABORT;  /* oh well. */
   190         }
   191     }
   192 
   193     /* Leave fullscreen mode, if possible (scary!) */
   194     window = SDL_GetFocusWindow();
   195     if (window) {
   196         if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
   197             SDL_MinimizeWindow(window);
   198         } else {
   199             /* !!! FIXME: ungrab the input if we're not fullscreen? */
   200             /* No need to mess with the window */
   201             window = NULL;
   202         }
   203     }
   204 
   205     /* Show a messagebox if we can, otherwise fall back to stdio */
   206     SDL_zero(messagebox);
   207     messagebox.flags = SDL_MESSAGEBOX_WARNING;
   208     messagebox.window = window;
   209     messagebox.title = "Assertion Failed";
   210     messagebox.message = message;
   211     messagebox.numbuttons = SDL_arraysize(buttons);
   212     messagebox.buttons = buttons;
   213 
   214     if (SDL_ShowMessageBox(&messagebox, &selected) == 0) {
   215         if (selected == -1) {
   216             state = SDL_ASSERTION_IGNORE;
   217         } else {
   218             state = (SDL_assert_state)selected;
   219         }
   220     }
   221 #ifdef HAVE_STDIO_H
   222     else
   223     {
   224         /* this is a little hacky. */
   225         for ( ; ; ) {
   226             char buf[32];
   227             fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
   228             fflush(stderr);
   229             if (fgets(buf, sizeof (buf), stdin) == NULL) {
   230                 break;
   231             }
   232 
   233             if (SDL_strcmp(buf, "a") == 0) {
   234                 state = SDL_ASSERTION_ABORT;
   235                 break;
   236             } else if (SDL_strcmp(buf, "b") == 0) {
   237                 state = SDL_ASSERTION_BREAK;
   238                 break;
   239             } else if (SDL_strcmp(buf, "r") == 0) {
   240                 state = SDL_ASSERTION_RETRY;
   241                 break;
   242             } else if (SDL_strcmp(buf, "i") == 0) {
   243                 state = SDL_ASSERTION_IGNORE;
   244                 break;
   245             } else if (SDL_strcmp(buf, "A") == 0) {
   246                 state = SDL_ASSERTION_ALWAYS_IGNORE;
   247                 break;
   248             }
   249         }
   250     }
   251 #endif /* HAVE_STDIO_H */
   252 
   253     /* Re-enter fullscreen mode */
   254     if (window) {
   255         SDL_RestoreWindow(window);
   256     }
   257 
   258     SDL_stack_free(message);
   259 
   260     return state;
   261 }
   262 
   263 
   264 SDL_assert_state
   265 SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
   266                     int line)
   267 {
   268     static int assertion_running = 0;
   269     static SDL_SpinLock spinlock = 0;
   270     SDL_assert_state state = SDL_ASSERTION_IGNORE;
   271 
   272     SDL_AtomicLock(&spinlock);
   273     if (assertion_mutex == NULL) { /* never called SDL_Init()? */
   274         assertion_mutex = SDL_CreateMutex();
   275         if (assertion_mutex == NULL) {
   276             SDL_AtomicUnlock(&spinlock);
   277             return SDL_ASSERTION_IGNORE;   /* oh well, I guess. */
   278         }
   279     }
   280     SDL_AtomicUnlock(&spinlock);
   281 
   282     if (SDL_LockMutex(assertion_mutex) < 0) {
   283         return SDL_ASSERTION_IGNORE;   /* oh well, I guess. */
   284     }
   285 
   286     /* doing this because Visual C is upset over assigning in the macro. */
   287     if (data->trigger_count == 0) {
   288         data->function = func;
   289         data->filename = file;
   290         data->linenum = line;
   291     }
   292 
   293     SDL_AddAssertionToReport(data);
   294 
   295     assertion_running++;
   296     if (assertion_running > 1) {   /* assert during assert! Abort. */
   297         if (assertion_running == 2) {
   298             SDL_AbortAssertion();
   299         } else if (assertion_running == 3) {  /* Abort asserted! */
   300             SDL_ExitProcess(42);
   301         } else {
   302             while (1) { /* do nothing but spin; what else can you do?! */ }
   303         }
   304     }
   305 
   306     if (!data->always_ignore) {
   307         state = assertion_handler(data, assertion_userdata);
   308     }
   309 
   310     switch (state)
   311     {
   312         case SDL_ASSERTION_ABORT:
   313             SDL_AbortAssertion();
   314             return SDL_ASSERTION_IGNORE;  /* shouldn't return, but oh well. */
   315 
   316         case SDL_ASSERTION_ALWAYS_IGNORE:
   317             state = SDL_ASSERTION_IGNORE;
   318             data->always_ignore = 1;
   319             break;
   320 
   321         case SDL_ASSERTION_IGNORE:
   322         case SDL_ASSERTION_RETRY:
   323         case SDL_ASSERTION_BREAK:
   324             break;  /* macro handles these. */
   325     }
   326 
   327     assertion_running--;
   328     SDL_UnlockMutex(assertion_mutex);
   329 
   330     return state;
   331 }
   332 
   333 
   334 void SDL_AssertionsQuit(void)
   335 {
   336     SDL_GenerateAssertionReport();
   337     if (assertion_mutex != NULL) {
   338         SDL_DestroyMutex(assertion_mutex);
   339         assertion_mutex = NULL;
   340     }
   341 }
   342 
   343 void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
   344 {
   345     if (handler != NULL) {
   346         assertion_handler = handler;
   347         assertion_userdata = userdata;
   348     } else {
   349         assertion_handler = SDL_PromptAssertion;
   350         assertion_userdata = NULL;
   351     }
   352 }
   353 
   354 const SDL_assert_data *SDL_GetAssertionReport(void)
   355 {
   356     return triggered_assertions;
   357 }
   358 
   359 void SDL_ResetAssertionReport(void)
   360 {
   361     SDL_assert_data *next = NULL;
   362     SDL_assert_data *item;
   363     for (item = triggered_assertions; item != NULL; item = next) {
   364         next = (SDL_assert_data *) item->next;
   365         item->always_ignore = SDL_FALSE;
   366         item->trigger_count = 0;
   367         item->next = NULL;
   368     }
   369 
   370     triggered_assertions = NULL;
   371 }
   372 
   373 SDL_AssertionHandler SDL_GetDefaultAssertionHandler(void)
   374 {
   375     return SDL_PromptAssertion;
   376 }
   377 
   378 SDL_AssertionHandler SDL_GetAssertionHandler(void **userdata)
   379 {
   380     if (userdata != NULL) {
   381         *userdata = assertion_userdata;
   382     }
   383     return assertion_handler;
   384 }
   385 
   386 /* vi: set ts=4 sw=4 expandtab: */