Core: always free memory on exit, always report leaks
Instead of allowing leaks when parsing arguments, always cleanup before calling exit(). This impacts -a (animation player), --help & --version arguments, as well as scripts executed via --python which meant tests that ran scripts could leak memory without raising an error as intended. Avoid having suppress warnings & rationalize in code-comments when leaking memory is/isn't acceptable, any leaks from the animation-player are now reported as well. This change exposed leaks: !140182, !140116. Ref !140098
This commit is contained in:
@@ -269,12 +269,6 @@ extern void (*MEM_name_ptr_set)(void *vmemh, const char *str) ATTR_NONNULL();
|
||||
*/
|
||||
void MEM_init_memleak_detection(void);
|
||||
|
||||
/**
|
||||
* Use this if we want to call #exit during argument parsing for example,
|
||||
* without having to free all data.
|
||||
*/
|
||||
void MEM_use_memleak_detection(bool enabled);
|
||||
|
||||
/**
|
||||
* When this has been called and memory leaks have been detected, the process will have an exit
|
||||
* code that indicates failure. This can be used for when checking for memory leaks with automated
|
||||
|
||||
@@ -24,15 +24,11 @@ char free_after_leak_detection_message[] =
|
||||
namespace {
|
||||
|
||||
bool fail_on_memleak = false;
|
||||
bool ignore_memleak = false;
|
||||
|
||||
class MemLeakPrinter {
|
||||
public:
|
||||
~MemLeakPrinter()
|
||||
{
|
||||
if (ignore_memleak) {
|
||||
return;
|
||||
}
|
||||
leak_detector_has_run = true;
|
||||
const uint leaked_blocks = MEM_get_memory_blocks_in_use();
|
||||
if (leaked_blocks == 0) {
|
||||
@@ -83,11 +79,6 @@ void MEM_init_memleak_detection()
|
||||
static MemLeakPrinter printer;
|
||||
}
|
||||
|
||||
void MEM_use_memleak_detection(bool enabled)
|
||||
{
|
||||
ignore_memleak = !enabled;
|
||||
}
|
||||
|
||||
void MEM_enable_fail_on_memleak()
|
||||
{
|
||||
fail_on_memleak = true;
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "MOV_read.hh"
|
||||
#include "MOV_util.hh"
|
||||
|
||||
#include "BKE_blender.hh"
|
||||
#include "BKE_image.hh"
|
||||
|
||||
#include "BIF_glutil.hh"
|
||||
@@ -2249,15 +2250,8 @@ int WM_main_playanim(int argc, const char **argv)
|
||||
AUD_exitOnce();
|
||||
#endif
|
||||
|
||||
/* NOTE(@ideasman42): Not useful unless all subsystems are properly shutdown. */
|
||||
if (false) {
|
||||
const int totblock = MEM_get_memory_blocks_in_use();
|
||||
if (totblock != 0) {
|
||||
/* Prints many `bAKey`, `bArgument` messages which are tricky to fix. */
|
||||
printf("Error Totblock: %d\n", totblock);
|
||||
MEM_printmemlist();
|
||||
}
|
||||
}
|
||||
/* Cleanup sub-systems started before this function was called. */
|
||||
BKE_blender_atexit();
|
||||
|
||||
return exit_code.value();
|
||||
}
|
||||
|
||||
@@ -144,6 +144,11 @@ static void main_callback_setup()
|
||||
MEM_set_error_callback(callback_mem_error);
|
||||
}
|
||||
|
||||
/** Data to free when Blender exits early on. */
|
||||
struct CreatorAtExitData_EarlyExit {
|
||||
bContext *C;
|
||||
};
|
||||
|
||||
/** Free data on early exit (if Python calls `sys.exit()` while parsing args for eg). */
|
||||
struct CreatorAtExitData {
|
||||
#ifndef WITH_PYTHON_MODULE
|
||||
@@ -155,9 +160,11 @@ struct CreatorAtExitData {
|
||||
int argv_num;
|
||||
#endif
|
||||
|
||||
#if defined(WITH_PYTHON_MODULE) && !defined(USE_WIN32_UNICODE_ARGS)
|
||||
void *_empty; /* Prevent empty struct error with MSVC. */
|
||||
#endif
|
||||
/**
|
||||
* When non-null, run additional exit logic.
|
||||
* Cleared once early initialization is over.
|
||||
*/
|
||||
CreatorAtExitData_EarlyExit *early_exit = nullptr;
|
||||
};
|
||||
|
||||
static void callback_main_atexit(void *user_data)
|
||||
@@ -169,8 +176,6 @@ static void callback_main_atexit(void *user_data)
|
||||
BLI_args_destroy(app_init_data->ba);
|
||||
app_init_data->ba = nullptr;
|
||||
}
|
||||
#else
|
||||
UNUSED_VARS(app_init_data); /* May be unused. */
|
||||
#endif
|
||||
|
||||
#ifdef USE_WIN32_UNICODE_ARGS
|
||||
@@ -181,9 +186,21 @@ static void callback_main_atexit(void *user_data)
|
||||
free((void *)app_init_data->argv);
|
||||
app_init_data->argv = nullptr;
|
||||
}
|
||||
#else
|
||||
UNUSED_VARS(app_init_data); /* May be unused. */
|
||||
#endif
|
||||
|
||||
if (CreatorAtExitData_EarlyExit *early_exit = app_init_data->early_exit) {
|
||||
CTX_free(early_exit->C);
|
||||
|
||||
RE_texture_rng_exit();
|
||||
BKE_brush_system_exit();
|
||||
|
||||
BKE_blender_globals_clear();
|
||||
BKE_appdir_exit();
|
||||
|
||||
DNA_sdna_current_free();
|
||||
|
||||
CLG_exit();
|
||||
}
|
||||
}
|
||||
|
||||
static void callback_clg_fatal(void *fp)
|
||||
@@ -288,6 +305,9 @@ int main(int argc,
|
||||
CreatorAtExitData app_init_data = {nullptr};
|
||||
BKE_blender_atexit_register(callback_main_atexit, &app_init_data);
|
||||
|
||||
CreatorAtExitData_EarlyExit app_init_data_early_exit = {nullptr};
|
||||
app_init_data.early_exit = &app_init_data_early_exit;
|
||||
|
||||
/* Un-buffered `stdout` makes `stdout` and `stderr` better synchronized, and helps
|
||||
* when stepping through code in a debugger (prints are immediately
|
||||
* visible). However disabling buffering causes lock contention on windows
|
||||
@@ -366,6 +386,8 @@ int main(int argc,
|
||||
|
||||
C = CTX_create();
|
||||
|
||||
app_init_data_early_exit.C = C;
|
||||
|
||||
#ifdef WITH_PYTHON_MODULE
|
||||
# ifdef __APPLE__
|
||||
environ = *_NSGetEnviron();
|
||||
@@ -439,10 +461,6 @@ int main(int argc,
|
||||
|
||||
main_args_setup(C, ba, false);
|
||||
|
||||
/* Begin argument parsing, ignore leaks so arguments that call #exit
|
||||
* (such as `--version` & `--help`) don't report leaks. */
|
||||
MEM_use_memleak_detection(false);
|
||||
|
||||
/* Parse environment handling arguments. */
|
||||
BLI_args_parse(ba, ARG_PASS_ENVIRONMENT, nullptr, nullptr);
|
||||
|
||||
@@ -476,6 +494,9 @@ int main(int argc,
|
||||
main_signal_setup();
|
||||
#endif
|
||||
|
||||
/* Continue with regular initialization, no need to use "early" exit. */
|
||||
app_init_data.early_exit = nullptr;
|
||||
|
||||
/* Must be initialized after #BKE_appdir_init to account for color-management paths. */
|
||||
IMB_init();
|
||||
/* Keep after #ARG_PASS_SETTINGS since debug flags are checked. */
|
||||
@@ -543,9 +564,6 @@ int main(int argc,
|
||||
callback_main_atexit(&app_init_data);
|
||||
BKE_blender_atexit_unregister(callback_main_atexit, &app_init_data);
|
||||
|
||||
/* End argument parsing, allow memory leaks to be printed. */
|
||||
MEM_use_memleak_detection(true);
|
||||
|
||||
/* Paranoid, avoid accidental re-use. */
|
||||
#ifndef WITH_PYTHON_MODULE
|
||||
ba = nullptr;
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
# endif
|
||||
|
||||
# include "BKE_appdir.hh"
|
||||
# include "BKE_blender.hh"
|
||||
# include "BKE_blender_cli_command.hh"
|
||||
# include "BKE_blender_version.h"
|
||||
# include "BKE_blendfile.hh"
|
||||
@@ -627,6 +628,10 @@ static const char arg_handle_print_version_doc[] =
|
||||
static int arg_handle_print_version(int /*argc*/, const char ** /*argv*/, void * /*data*/)
|
||||
{
|
||||
print_version_full();
|
||||
|
||||
/* Handles cleanup before exit. */
|
||||
BKE_blender_atexit();
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
BLI_assert_unreachable();
|
||||
return 0;
|
||||
@@ -922,6 +927,9 @@ static int arg_handle_print_help(int /*argc*/, const char ** /*argv*/, void *dat
|
||||
|
||||
print_help(ba, false);
|
||||
|
||||
/* Handles cleanup before exit. */
|
||||
BKE_blender_atexit();
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
BLI_assert_unreachable();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user