MEM management: Add data storage only destructed after memleak detection.
Add a new API to store data that is guaranteed to not be freed
before the memleak detector has run.
This will be used in next commit by the readfile code to improve
reporting on leaks from blendfile readingi process.
This is done by a two-layer approach:
A new templated `MEM_construct_leak_detection_data` allows to
create any type of data. Its ownership and lifetime are handled
internally, and guaranteed to not be destroyed before the memleak
detector has run.
Add a new template-based 'allocation string storage' system to
`intern/memutil`. This uses the new `Guardedalloc Persistent Storage`
system to store all 'complex' allocation messages, that cannot be
defined as literals.
Internally, the storage is done through an owning reference (a
`shared_ptr`) of the created data into a mutex-protected static
vector.
`MEM_init_memleak_detection` code ensures that this static storage
is created before the memleak detection data, so that it is destructed
after the memleak detector has ran.
The main container (`AllocStringStorageContainer`) is wrapping a
map of `{string -> AllocStringStorage<key_type, hash_type>}`.
The key is a storage identifier.
Each storage is also a map wrapped into a simple templated API
class (`AllocStringStorage`), where the values are the alloc strings,
and the keys type is defined by the user code.
Pull Request: https://projects.blender.org/blender/blender/pulls/125320
This commit is contained in:
@@ -270,6 +270,8 @@ void MEM_use_guarded_allocator(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
# include <any>
|
||||
# include <memory>
|
||||
# include <new>
|
||||
# include <type_traits>
|
||||
# include <utility>
|
||||
@@ -421,6 +423,23 @@ template<typename T> inline T *MEM_cnew(const char *allocation_name, const T &ot
|
||||
*/ \
|
||||
void operator delete(void * /*ptr_to_free*/, void * /*ptr*/) {}
|
||||
|
||||
/**
|
||||
* Construct a T that will only be destructed after leak detection is run.
|
||||
*
|
||||
* This call is thread-safe. Calling code should typically keep a reference to that data as a
|
||||
* `static thread_local` variable, or use some lock, to prevent concurrent accesses.
|
||||
*
|
||||
* The returned value should not own any memory allocated with `MEM_*` functions, since these would
|
||||
* then be detected as leaked.
|
||||
*/
|
||||
template<typename T, typename... Args> T &MEM_construct_leak_detection_data(Args &&...args)
|
||||
{
|
||||
std::shared_ptr<T> data = std::make_shared<T>(std::forward<Args>(args)...);
|
||||
std::any any_data = std::make_any<std::shared_ptr<T>>(data);
|
||||
mem_guarded::internal::add_memleak_data(any_data);
|
||||
return *data;
|
||||
}
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __MEM_GUARDEDALLOC_H__ */
|
||||
|
||||
@@ -6,8 +6,11 @@
|
||||
* \ingroup intern_mem
|
||||
*/
|
||||
|
||||
#include <any>
|
||||
#include <cstdio> /* Needed for `printf` on WIN32/APPLE. */
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
#include "mallocn_intern.hh"
|
||||
@@ -63,6 +66,11 @@ void MEM_init_memleak_detection()
|
||||
/* Calling this ensures that the memory usage counters outlive the memory leak detection. */
|
||||
memory_usage_init();
|
||||
|
||||
/* Ensure that the static memleak data storage is initialized before the #MemLeakPrinter one, so
|
||||
* that it outlives the memory leak detection. */
|
||||
std::any any_data = std::make_any<int>(0);
|
||||
mem_guarded::internal::add_memleak_data(any_data);
|
||||
|
||||
/**
|
||||
* This variable is constructed when this function is first called. This should happen as soon as
|
||||
* possible when the program starts.
|
||||
@@ -84,3 +92,11 @@ void MEM_enable_fail_on_memleak()
|
||||
{
|
||||
fail_on_memleak = true;
|
||||
}
|
||||
|
||||
void mem_guarded::internal::add_memleak_data(std::any data)
|
||||
{
|
||||
static std::mutex mutex;
|
||||
static std::vector<std::any> data_vec;
|
||||
std::lock_guard lock{mutex};
|
||||
data_vec.push_back(std::move(data));
|
||||
}
|
||||
|
||||
@@ -27,4 +27,20 @@ extern void *(*mem_mallocN_aligned_ex)(size_t len,
|
||||
const char *str,
|
||||
AllocationType allocation_type);
|
||||
|
||||
/**
|
||||
* Store a std::any into a static opaque storage vector. The only purpose of this call is to
|
||||
* control the lifetime of the given data, there is no way to access it from here afterwards. User
|
||||
* code is expected to keep its own reference to the data contained in the `std::any` as long as it
|
||||
* needs it.
|
||||
*
|
||||
* Typically, this `any` should contain a `shared_ptr` to the actual data, to ensure that the data
|
||||
* itself is not duplicated, and that the static storage does become an owner of it.
|
||||
*
|
||||
* That way, the memleak data does not get destructed before the static storage is. Since this
|
||||
* storage is created before the memleak detection data (see the implementation of
|
||||
* #MEM_init_memleak_detection), it is guaranteed to happen after the execution and destruction of
|
||||
* the memleak detector.
|
||||
*/
|
||||
void add_memleak_data(std::any data);
|
||||
|
||||
} // namespace mem_guarded::internal
|
||||
|
||||
Reference in New Issue
Block a user