From 686514fba55dc36647dbce8da667c82bc27ff418 Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Tue, 16 Aug 2016 18:01:12 +0200 Subject: [PATCH] fix memory leaks and other mem-bugs --- src/gamestate.c | 7 ++-- src/internal.c | 52 ++++++++++++++++++----- src/libsuperderpy.c | 22 ++++++---- src/timeline.c | 100 +++++++++++++++++++++----------------------- src/timeline.h | 2 + 5 files changed, 109 insertions(+), 74 deletions(-) diff --git a/src/gamestate.c b/src/gamestate.c index dece1a4..edaa76d 100644 --- a/src/gamestate.c +++ b/src/gamestate.c @@ -23,7 +23,7 @@ #include "gamestate.h" #include "internal.h" -SYMBOL_INTERNAL struct Gamestate* AddNewGamestate(struct Game *game) { +SYMBOL_INTERNAL struct Gamestate* AddNewGamestate(struct Game *game, const char* name) { struct Gamestate *tmp = game->_priv.gamestates; if (!tmp) { game->_priv.gamestates = malloc(sizeof(struct Gamestate)); @@ -35,7 +35,7 @@ SYMBOL_INTERNAL struct Gamestate* AddNewGamestate(struct Game *game) { tmp->next = malloc(sizeof(struct Gamestate)); tmp = tmp->next; } - tmp->name = NULL; + tmp->name = strdup(name); tmp->handle = NULL; tmp->loaded = false; tmp->paused = false; @@ -68,8 +68,7 @@ SYMBOL_EXPORT void LoadGamestate(struct Game *game, const char* name) { } gs->pending_load = true; } else { - gs = AddNewGamestate(game); - gs->name = strdup(name); + gs = AddNewGamestate(game, name); gs->pending_load = true; gs->showLoading = true; } diff --git a/src/internal.c b/src/internal.c index 7668ece..40546e9 100644 --- a/src/internal.c +++ b/src/internal.c @@ -137,23 +137,55 @@ SYMBOL_INTERNAL void GamestateProgress(struct Game *game) { } } -SYMBOL_INTERNAL void* AddGarbage(struct Game *game, void* data) { - if (!game->_priv.garbage) { - game->_priv.garbage = malloc(sizeof(struct libsuperderpy_list)); - game->_priv.garbage->data = data; - game->_priv.garbage->next = NULL; +SYMBOL_INTERNAL struct libsuperderpy_list* AddToList(struct libsuperderpy_list *list, void* data) { + if (!list) { + list = malloc(sizeof(struct libsuperderpy_list)); + list->data = data; + list->next = NULL; } else { - struct libsuperderpy_list *garbage = malloc(sizeof(struct libsuperderpy_list)); - garbage->next = game->_priv.garbage; - garbage->data = data; - game->_priv.garbage = garbage; + struct libsuperderpy_list *elem = malloc(sizeof(struct libsuperderpy_list)); + elem->next = list; + elem->data = data; + list = elem; } + return list; +} + +SYMBOL_INTERNAL struct libsuperderpy_list* RemoveFromList(struct libsuperderpy_list **list, bool (*identity)(struct libsuperderpy_list* elem, void* data), void* data) { + struct libsuperderpy_list *prev = NULL, *tmp = *list, *start = *list; + void* d = NULL; + while (tmp) { + if (identity(tmp, data)) { + if (prev) { + prev->next = tmp->next; + d = tmp->data; + free(tmp); + return d; + } else { + start = tmp->next; + d = tmp->data; + free(tmp); + *list = start; + return d; + } + } + prev = tmp; + tmp = tmp->next; + } + return NULL; +} + +SYMBOL_INTERNAL void* AddGarbage(struct Game *game, void* data) { + game->_priv.garbage = AddToList(game->_priv.garbage, data); return data; } SYMBOL_INTERNAL void ClearGarbage(struct Game *game) { + struct libsuperderpy_list *tmp; while (game->_priv.garbage) { free(game->_priv.garbage->data); - game->_priv.garbage = game->_priv.garbage->next; + tmp = game->_priv.garbage->next; + free(game->_priv.garbage); + game->_priv.garbage = tmp; } } diff --git a/src/libsuperderpy.c b/src/libsuperderpy.c index 213c97f..05ebd32 100644 --- a/src/libsuperderpy.c +++ b/src/libsuperderpy.c @@ -78,6 +78,8 @@ SYMBOL_EXPORT struct Game* libsuperderpy_init(int argc, char** argv, const char* game->config.height = atoi(GetConfigOptionDefault(game, "SuperDerpy", "height", "720")); if (game->config.height<180) game->config.height=180; + game->_priv.showconsole = game->config.debug; + if(!al_init_image_addon()) { fprintf(stderr, "failed to initialize image addon!\n"); /*al_show_native_message_box(display, "Error", "Error", "Failed to initialize al_init_image_addon!", @@ -167,8 +169,6 @@ SYMBOL_EXPORT struct Game* libsuperderpy_init(int argc, char** argv, const char* al_set_mixer_gain(game->audio.music, game->config.music/10.0); al_set_mixer_gain(game->audio.voice, game->config.voice/10.0); - game->_priv.showconsole = game->config.debug; - setlocale(LC_NUMERIC, "C"); game->_priv.argc = argc; @@ -328,7 +328,6 @@ SYMBOL_EXPORT int libsuperderpy_run(struct Game *game) { tmp=game->_priv.gamestates; while (tmp) { - if ((tmp->pending_start) && (tmp->loaded)) { PrintConsole(game, "Starting gamestate \"%s\"...", tmp->name); al_stop_timer(game->_priv.timer); @@ -415,8 +414,10 @@ SYMBOL_EXPORT int libsuperderpy_run(struct Game *game) { SYMBOL_EXPORT void libsuperderpy_destroy(struct Game *game) { game->shuttingdown = true; + ClearGarbage(game); + // in case of restart - struct Gamestate *tmp = game->_priv.gamestates; + struct Gamestate *tmp = game->_priv.gamestates, *pom; while (tmp) { if (tmp->started) { PrintConsole(game, "Stopping gamestate \"%s\"...", tmp->name); @@ -429,14 +430,20 @@ SYMBOL_EXPORT void libsuperderpy_destroy(struct Game *game) { dlclose(tmp->handle); tmp->loaded = false; } - tmp=tmp->next; + free(tmp->name); + pom = tmp->next; + free(tmp); + tmp=pom; } al_clear_to_color(al_map_rgb(0,0,0)); PrintConsole(game, "Shutting down..."); DrawConsole(game); al_flip_display(); - al_rest(0.1); + while (game->_priv.garbage) { + free(game->_priv.garbage->data); + game->_priv.garbage = game->_priv.garbage->next; + } (*game->_priv.loading.Unload)(game, game->_priv.loading.data); al_destroy_timer(game->_priv.timer); Console_Unload(game); @@ -452,8 +459,9 @@ SYMBOL_EXPORT void libsuperderpy_destroy(struct Game *game) { al_shutdown_ttf_addon(); al_shutdown_font_addon(); char** argv = game->_priv.argv; + bool restart = game->restart; free(game); - if (game->restart) { + if (restart) { execv(argv[0], argv); // FIXME: on OSX there's chdir called which might break it } } diff --git a/src/timeline.c b/src/timeline.c index e76f056..04590f7 100644 --- a/src/timeline.c +++ b/src/timeline.c @@ -50,6 +50,7 @@ SYMBOL_EXPORT void TM_Process(struct Timeline* timeline) { struct TM_Action *tmp = timeline->queue; timeline->queue = timeline->queue->next; (*tmp->function)(timeline->game, tmp, TM_ACTIONSTATE_DESTROY); + TM_DestroyArgs(tmp->arguments); free(tmp->name); free(tmp); } @@ -101,6 +102,7 @@ SYMBOL_EXPORT void TM_Process(struct Timeline* timeline) { tmp = pom; pom = pom->next; } else { + TM_DestroyArgs(pom->arguments); free(pom->name); free(pom); tmp2 = tmp; @@ -264,19 +266,30 @@ SYMBOL_EXPORT struct TM_Action* TM_AddBackgroundAction(struct Timeline* timeline /*! \brief Predefined action used by TM_AddQueuedBackgroundAction */ SYMBOL_INTERNAL bool runinbackground(struct Game* game, struct TM_Action* action, enum TM_ActionState state) { - if (state != TM_ACTIONSTATE_RUNNING) return false; - int* delay = (int*) action->arguments->next->value; - char* name = (char*) action->arguments->next->next->value; - struct Timeline *timeline = (struct Timeline*) action->arguments->next->next->next->value; - TM_AddBackgroundAction(timeline, action->arguments->value, action->arguments->next->next->next->next, *delay, name); - free(name); + int* delay = (int*) TM_GetArg(action->arguments, 1); + char* name = (char*) TM_GetArg(action->arguments, 2); + struct Timeline *timeline = (struct Timeline*) TM_GetArg(action->arguments, 3); + struct TM_Arguments *arguments = (struct TM_Arguments*) TM_GetArg(action->arguments, 4); + bool *used = (bool*) TM_GetArg(action->arguments, 5); + if (state == TM_ACTIONSTATE_START) { + TM_AddBackgroundAction(timeline, TM_GetArg(action->arguments, 0), arguments, *delay, name); + *used = true; + } + if (state == TM_ACTIONSTATE_DESTROY) { + free(name); + free(delay); + if (!(*used)) { + TM_DestroyArgs(arguments); + } + free(used); + } return true; } SYMBOL_EXPORT struct TM_Action* TM_AddQueuedBackgroundAction(struct Timeline* timeline, bool (*func)(struct Game*, struct TM_Action*, enum TM_ActionState), struct TM_Arguments* args, int delay, char* name) { TM_WrapArg(int, del, delay); - struct TM_Arguments* arguments = TM_AddToArgs(NULL, 4, (void*) func, del, strdup(name), (void*) timeline); - arguments->next->next->next->next = args; + TM_WrapArg(bool, used, false); + struct TM_Arguments* arguments = TM_AddToArgs(NULL, 6, (void*) func, del, strdup(name), (void*) timeline, args, used); return TM_AddAction(timeline, *runinbackground, arguments, "TM_BackgroundAction"); } @@ -294,63 +307,37 @@ SYMBOL_EXPORT void TM_AddDelay(struct Timeline* timeline, int delay) { SYMBOL_EXPORT void TM_CleanQueue(struct Timeline* timeline) { PrintConsole(timeline->game, "Timeline Manager[%s]: cleaning queue", timeline->name); - struct TM_Action *tmp, *tmp2, *pom = timeline->queue; - tmp = NULL; + struct TM_Action *tmp, *pom = timeline->queue; while (pom!=NULL) { - if (pom->active) { - if (*pom->function) (*pom->function)(timeline->game, pom, TM_ACTIONSTATE_DESTROY); - if (pom->timer) { - al_stop_timer(pom->timer); - al_destroy_timer(pom->timer); - } - } else { - TM_DestroyArgs(pom->arguments); - pom->arguments = NULL; - } - if ((!tmp) || (tmp->next==pom)) { - tmp = pom; - pom = pom->next; - } else { - free(pom->name); - free(pom); - tmp2 = tmp; - if (!tmp) pom=timeline->queue->next; - else pom=tmp->next; - tmp = tmp2; + if (*pom->function) (*pom->function)(timeline->game, pom, TM_ACTIONSTATE_DESTROY); + if (pom->timer) { + al_stop_timer(pom->timer); + al_destroy_timer(pom->timer); } + TM_DestroyArgs(pom->arguments); + tmp = pom->next; + free(pom->name); + free(pom); + pom = tmp; } - // TODO: it shouldn't be needed, but is. Debug! timeline->queue = NULL; } SYMBOL_EXPORT void TM_CleanBackgroundQueue(struct Timeline* timeline) { PrintConsole(timeline->game, "Timeline Manager[%s]: cleaning background queue", timeline->name); - struct TM_Action *tmp, *tmp2, *pom = timeline->background; - tmp = NULL; + struct TM_Action *tmp, *pom = timeline->background; while (pom!=NULL) { - if (pom->active) { - if (*pom->function) (*pom->function)(timeline->game, pom, TM_ACTIONSTATE_DESTROY); - if (pom->timer) { - al_stop_timer(pom->timer); + if (*pom->function) (*pom->function)(timeline->game, pom, TM_ACTIONSTATE_DESTROY); + if (pom->timer) { + al_stop_timer(pom->timer); al_destroy_timer(pom->timer); - } - } else { - TM_DestroyArgs(pom->arguments); - pom->arguments = NULL; - } - if ((!tmp) || (tmp->next==pom)) { - tmp = pom; - pom = pom->next; - } else { - free(pom->name); - free(pom); - tmp2 = tmp; - if (!tmp) pom=timeline->background->next; - else pom=tmp->next; - tmp = tmp2; } + TM_DestroyArgs(pom->arguments); + tmp = pom->next; + free(pom->name); + free(pom); + pom = tmp; } - // TODO: it shouldn't be needed, but is. Debug! timeline->background = NULL; } @@ -389,6 +376,13 @@ SYMBOL_EXPORT struct TM_Arguments* TM_AddToArgs(struct TM_Arguments* args, int n return args; } +SYMBOL_EXPORT void* TM_GetArg(struct TM_Arguments *args, int num) { + for (int i=0; inext; + } + return args->value; +} + SYMBOL_EXPORT void TM_DestroyArgs(struct TM_Arguments* args) { struct TM_Arguments *pom; while (args) { diff --git a/src/timeline.h b/src/timeline.h index b344377..266c356 100644 --- a/src/timeline.h +++ b/src/timeline.h @@ -92,6 +92,8 @@ void TM_CleanBackgroundQueue(struct Timeline*); void TM_Destroy(struct Timeline*); /*! \brief Add data to TM_Arguments queue. */ struct TM_Arguments* TM_AddToArgs(struct TM_Arguments* args, int num, ...); +/*! \brief Get nth argument from TM_Arguments queue (counted from 0). */ +void* TM_GetArg(struct TM_Arguments *args, int num); /*! \brief Destroy TM_Arguments queue. */ void TM_DestroyArgs(struct TM_Arguments* args);