From 1d2612151439bd4dab6d78af930f3f2f088e6fef Mon Sep 17 00:00:00 2001 From: helixhorned Date: Fri, 30 May 2014 00:02:16 +0000 Subject: [PATCH] Add X{m,c,re}alloc and Xstrdup macros that call an out-of-memory handler on failure. These wrap the x*alloc or xstrdup functions in compat.c. The handler gets passed __FILE__, __LINE__ and __func__ (if available) in debugging builds. Terminating the application process immediately in case of allocation failure will let us prune many error handling paths and simplify a good portion of code. git-svn-id: https://svn.eduke32.com/eduke32@4490 1a8010ca-5511-0410-912e-c29ae57300e0 --- polymer/eduke32/build/include/compat.h | 33 +++++++++++ polymer/eduke32/build/src/compat.c | 78 ++++++++++++++++++++++++++ polymer/eduke32/source/astub.c | 8 +++ polymer/eduke32/source/game.c | 9 +++ 4 files changed, 128 insertions(+) diff --git a/polymer/eduke32/build/include/compat.h b/polymer/eduke32/build/include/compat.h index 891f5a17f..f161c44df 100644 --- a/polymer/eduke32/build/include/compat.h +++ b/polymer/eduke32/build/include/compat.h @@ -775,6 +775,15 @@ static inline void append_ext_UNSAFE(char *outbuf, const char *ext) Bstrcpy(p, ext); } +#ifdef DEBUGGINGAIDS +extern void xalloc_set_location(int32_t line, const char *file, const char *func); +#endif +void set_memerr_handler(void (*handlerfunc)(int32_t, const char *, const char *)); +char *xstrdup(const char *s); +void *xmalloc(bsize_t size); +void *xcalloc(bsize_t nmemb, bsize_t size); +void *xrealloc(void *ptr, bsize_t size); + #ifdef EXTERNC } #endif @@ -839,5 +848,29 @@ static inline void append_ext_UNSAFE(char *outbuf, const char *ext) #define ARRAY_SIZE(Ar) (sizeof(Ar)/sizeof((Ar)[0])) #define ARRAY_SSIZE(Ar) (bssize_t)ARRAY_SIZE(Ar) +////////// PANICKING ALLOCATION MACROS (wrapping the functions) ////////// +#ifdef DEBUGGINGAIDS +// Detection of __func__ or equivalent functionality, found in SDL_assert.h +# if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 supports __func__ as a standard. */ +# define EDUKE32_FUNCTION __func__ +# elif ((__GNUC__ >= 2) || defined(_MSC_VER)) +# define EDUKE32_FUNCTION __FUNCTION__ +# else +# define EDUKE32_FUNCTION "???" +# endif + +# define EDUKE32_PRE_XALLLOC xalloc_set_location(__LINE__, __FILE__, EDUKE32_FUNCTION) +# define Xstrdup(s) (EDUKE32_PRE_XALLLOC, xstrdup(s)) +# define Xmalloc(size) (EDUKE32_PRE_XALLLOC, xmalloc(size)) +# define Xcalloc(nmemb, size) (EDUKE32_PRE_XALLLOC, xcalloc(nmemb, size)) +# define Xrealloc(ptr, size) (EDUKE32_PRE_XALLLOC, xrealloc(ptr, size)) +#else +# define Xstrdup xstrdup +# define Xmalloc xmalloc +# define Xcalloc xcalloc +# define Xrealloc xrealloc +#endif +////////// + #endif // __compat_h__ diff --git a/polymer/eduke32/build/src/compat.c b/polymer/eduke32/build/src/compat.c index 0d681a1ef..775755015 100644 --- a/polymer/eduke32/build/src/compat.c +++ b/polymer/eduke32/build/src/compat.c @@ -45,6 +45,84 @@ #include "compat.h" #include "baselayer.h" +////////// PANICKING ALLOCATION FUNCTIONS ////////// + +static void (*g_MemErrHandler)(int32_t line, const char *file, const char *func); + +#ifdef DEBUGGINGAIDS +static const char *g_MemErrFunc = "???"; +static const char *g_MemErrFile = "???"; +static int32_t g_MemErrLine; + +void xalloc_set_location(int32_t line, const char *file, const char *func) +{ + g_MemErrLine = line; + g_MemErrFile = file; + + if (func) + g_MemErrFunc = func; +} +#endif + +static void handle_potential_memerr(void *ptr) +{ + if (ptr == NULL) + { + if (g_MemErrHandler) + { +#ifdef DEBUGGINGAIDS + g_MemErrHandler(g_MemErrLine, g_MemErrFile, g_MemErrFunc); +#else + g_MemErrHandler(0, "???", "???"); +#endif + } + + exit(EXIT_FAILURE); + } +} + +void set_memerr_handler(void (*handlerfunc)(int32_t, const char *, const char *)) +{ + g_MemErrHandler = handlerfunc; +} + +char *xstrdup(const char *s) +{ + char *ptr = Bstrdup(s); + handle_potential_memerr(ptr); + return ptr; +} + +void *xmalloc(bsize_t size) +{ + void *ptr = Bmalloc(size); + handle_potential_memerr(ptr); + return ptr; +} + +void *xcalloc(bsize_t nmemb, bsize_t size) +{ + void *ptr = Bcalloc(nmemb, size); + handle_potential_memerr(ptr); + return ptr; +} + +void *xrealloc(void *ptr, bsize_t size) +{ + void *newptr = Brealloc(ptr, size); + + // According to the C Standard, + // - ptr == NULL makes realloc() behave like malloc() + // - size == 0 make it behave like free() if ptr != NULL + // Since we want to catch an out-of-mem in the first case, this leaves: + if (size != 0) + handle_potential_memerr(newptr); + + return newptr; +} + +////////// + #ifndef __compat_h_macrodef__ void Bassert(int expr) diff --git a/polymer/eduke32/source/astub.c b/polymer/eduke32/source/astub.c index d98490bbf..f6349dd08 100644 --- a/polymer/eduke32/source/astub.c +++ b/polymer/eduke32/source/astub.c @@ -10403,12 +10403,20 @@ static void m32script_interrupt_handler(int signo) } } +static void M32_HandleMemErr(int32_t line, const char *file, const char *func) +{ + initprintf("Out of memory in %s:%d (%s)\n", file, line, func); + osdcmd_quit(NULL); +} + int32_t ExtInit(void) { int32_t rv = 0; int32_t i; char cwd[BMAX_PATH]; + set_memerr_handler(&M32_HandleMemErr); + G_AddSearchPaths(); if (getcwd(cwd,BMAX_PATH)) diff --git a/polymer/eduke32/source/game.c b/polymer/eduke32/source/game.c index 39dc6e1aa..bde300725 100644 --- a/polymer/eduke32/source/game.c +++ b/polymer/eduke32/source/game.c @@ -10822,10 +10822,19 @@ void G_PostCreateGameState(void) A_InitEnemyFlags(); } +static void G_HandleMemErr(int32_t line, const char *file, const char *func) +{ + static char msg[128]; + snprintf(msg, sizeof(msg), "Out of memory in %s:%d (%s)\n", file, line, func); + G_GameExit(msg); +} + static void G_Startup(void) { int32_t i; + set_memerr_handler(&G_HandleMemErr); + inittimer(TICRATE); initcrc32table();