MEM_guardedalloc: Organize and Document the public API.
Pull Request: https://projects.blender.org/blender/blender/pulls/135534
This commit is contained in:
committed by
Bastien Montagne
parent
337f0fc2fc
commit
5f2d3c49c5
@@ -9,7 +9,7 @@
|
||||
*
|
||||
* \page MEMPage Guarded memory(de)allocation
|
||||
*
|
||||
* \section aboutmem c-style guarded memory allocation
|
||||
* \section aboutmem c-style & C++-style guarded memory allocation
|
||||
*
|
||||
* \subsection memabout About the MEM module
|
||||
*
|
||||
@@ -18,10 +18,10 @@
|
||||
* linked list, so they remain reachable at all times. There is no
|
||||
* back-up in case the linked-list related data is lost.
|
||||
*
|
||||
* \subsection memissues Known issues with MEM
|
||||
*
|
||||
* There are currently no known issues with MEM. Note that there is a
|
||||
* second intern/ module with MEM_ prefix, for use in c++.
|
||||
* It also provides C++ template versions of [cm]alloc and related API,
|
||||
* which prodives improved type safety, ensures that the allocated types
|
||||
* are trivial, and reduces the casting verbosity by directly returning
|
||||
* a pointer of the expected type.
|
||||
*
|
||||
* \subsection memdependencies Dependencies
|
||||
* - `stdlib`
|
||||
@@ -44,6 +44,19 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/**
|
||||
* \name Untyped Allocation API.
|
||||
*
|
||||
* Defines the 'C-style' part of the API, where memory management is fully untyped (i.e. done with
|
||||
* void pointers and explicit size values).
|
||||
*
|
||||
* This API should usually not be used anymore in C++ code, unless some form of raw memory
|
||||
* mamangement is necessary (e.g. for allocation of various ID types based on their
|
||||
* #IDTypeInfo::struct_size data).
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Returns the length of the allocated memory segment pointed at
|
||||
* by vmemh. If the pointer was not previously allocated by this
|
||||
@@ -156,6 +169,36 @@ extern void *(*MEM_calloc_arrayN_aligned)(
|
||||
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1, 2)
|
||||
ATTR_NONNULL(4);
|
||||
|
||||
#ifdef __cplusplus
|
||||
/* Implicitely uses the templated, type-safe version of #MEM_freeN<T>, unless `v` is `void *`. */
|
||||
# define MEM_SAFE_FREE(v) \
|
||||
do { \
|
||||
if (v) { \
|
||||
MEM_freeN(v); \
|
||||
(v) = nullptr; \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
# define MEM_SAFE_FREE(v) \
|
||||
do { \
|
||||
void **_v = (void **)&(v); \
|
||||
if (*_v) { \
|
||||
MEM_freeN(*_v); \
|
||||
*_v = NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/**
|
||||
* \name Various Helpers.
|
||||
*
|
||||
* These functions allow to control the behavior of the guarded allocator, and to retrieve (debug)
|
||||
* information about allocated memory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Print a list of the names and sizes of all allocated memory
|
||||
* blocks. as a python dict for easy investigation.
|
||||
@@ -197,25 +240,6 @@ extern void (*MEM_reset_peak_memory)(void);
|
||||
/** Get the peak memory usage in bytes, including `mmap` allocations. */
|
||||
extern size_t (*MEM_get_peak_memory)(void) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
#ifdef __cplusplus
|
||||
# define MEM_SAFE_FREE(v) \
|
||||
do { \
|
||||
if (v) { \
|
||||
MEM_freeN(v); \
|
||||
(v) = nullptr; \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
# define MEM_SAFE_FREE(v) \
|
||||
do { \
|
||||
void **_v = (void **)&(v); \
|
||||
if (*_v) { \
|
||||
MEM_freeN(*_v); \
|
||||
*_v = NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/** Overhead for lockfree allocator (use to avoid slop-space). */
|
||||
#define MEM_SIZE_OVERHEAD sizeof(size_t)
|
||||
#define MEM_SIZE_OPTIMAL(size) ((size)-MEM_SIZE_OVERHEAD)
|
||||
@@ -274,6 +298,8 @@ void MEM_use_lockfree_allocator(void);
|
||||
*/
|
||||
void MEM_use_guarded_allocator(void);
|
||||
|
||||
/** \} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
@@ -297,6 +323,23 @@ void MEM_use_guarded_allocator(void);
|
||||
(__STDCPP_DEFAULT_NEW_ALIGNMENT__ < alignof(void *) ? __STDCPP_DEFAULT_NEW_ALIGNMENT__ : \
|
||||
alignof(void *))
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/**
|
||||
* \name Type-aware allocation & construction API.
|
||||
*
|
||||
* Defines some `new`/`delete`-like helpers, which allocate/free memory using `MEM_guardedalloc`,
|
||||
* and construct/destruct the objects.
|
||||
*
|
||||
* When possible, it is prefferred to use these, even on trivial types, as it makes potential
|
||||
* future changes to these types less disruptive, and is overall closer to standard C++ data
|
||||
* creation and destruction.
|
||||
*
|
||||
* However, if the type is trivial, `MEM_[cm]allocN<T>` and related functions can be used to
|
||||
* allocate an object that will be managed by external historic code still using C-style
|
||||
* allocation/duplication/freeing.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Allocate new memory for an object of type #T, and construct it.
|
||||
* #MEM_delete must be used to delete the object. Calling #MEM_freeN on it is illegal.
|
||||
@@ -354,155 +397,7 @@ template<typename T> inline void MEM_delete(const T *ptr)
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Allocate zero-initialized memory for an object of type #T. The constructor of #T is not called,
|
||||
* therefore this must only be used with trivial types (like all C types).
|
||||
*
|
||||
* When allocating an enforced specific amount of bytes, the C version of this function should be
|
||||
* used instead. While this should be avoided in C++ code, it is still required in some cases, e.g.
|
||||
* for ID allocation based on #IDTypeInfo::struct_size.
|
||||
*
|
||||
* #MEM_freeN must be used to free a pointer returned by this call. Calling #MEM_delete on it is
|
||||
* illegal.
|
||||
*/
|
||||
template<typename T> inline T *MEM_callocN(const char *allocation_name)
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
/* MSVC considers C-style types using the DNA_DEFINE_CXX_METHODS as non-trivial (more
|
||||
* specifically, non-trivially copyable, likely because the default copy constructors are
|
||||
* deleted). GCC and clang (both on linux, OSX, and clang-cl on Windows on Arm) do not.
|
||||
*
|
||||
* So for now, use a more restricted check on MSVC, should still catch most of actual invalid
|
||||
* cases. */
|
||||
static_assert(std::is_trivially_constructible_v<T>,
|
||||
"For non-trivial types, MEM_new must be used.");
|
||||
# else
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
|
||||
# endif
|
||||
return static_cast<T *>(MEM_calloc_arrayN_aligned(1, sizeof(T), alignof(T), allocation_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Type-safe version of #MEM_calloc_arrayN/#MEM_calloc_array_alignedN.
|
||||
*/
|
||||
template<typename T> inline T *MEM_calloc_arrayN(const size_t length, const char *allocation_name)
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
/* MSVC considers C-style types using the DNA_DEFINE_CXX_METHODS as non-trivial (more
|
||||
* specifically, non-trivially copyable, likely because the default copy constructors are
|
||||
* deleted). GCC and clang (both on linux, OSX, and clang-cl on Windows on Arm) do not.
|
||||
*
|
||||
* So for now, use a more restricted check on MSVC, should still catch most of actual invalid
|
||||
* cases. */
|
||||
static_assert(std::is_trivially_constructible_v<T>,
|
||||
"For non-trivial types, MEM_new must be used.");
|
||||
# else
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
|
||||
# endif
|
||||
return static_cast<T *>(
|
||||
MEM_calloc_arrayN_aligned(length, sizeof(T), alignof(T), allocation_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate uninitialized memory for an object of type #T. The constructor of #T is not called,
|
||||
* therefore this must only be used with trivial types (like all C types).
|
||||
*
|
||||
* When allocating an enforced specific amount of bytes, the C version of this function should be
|
||||
* used instead. While this should be avoided in C++ code, it is still required in some cases, e.g.
|
||||
* for ID allocation based on #IDTypeInfo::struct_size.
|
||||
*
|
||||
* #MEM_freeN must be used to free a pointer returned by this call. Calling #MEM_delete on it is
|
||||
* illegal.
|
||||
*/
|
||||
template<typename T> inline T *MEM_mallocN(const char *allocation_name)
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
/* MSVC considers C-style types using the DNA_DEFINE_CXX_METHODS as non-trivial (more
|
||||
* specifically, non-trivially copyable, likely because the default copy constructors are
|
||||
* deleted). GCC and clang (both on linux, OSX, and clang-cl on Windows on Arm) do not.
|
||||
*
|
||||
* So for now, use a more restricted check on MSVC, should still catch most of actual invalid
|
||||
* cases. */
|
||||
static_assert(std::is_trivially_constructible_v<T>,
|
||||
"For non-trivial types, MEM_new must be used.");
|
||||
# else
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
|
||||
# endif
|
||||
return static_cast<T *>(MEM_malloc_arrayN_aligned(1, sizeof(T), alignof(T), allocation_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Type-safe version of #MEM_malloc_arrayN/#MEM_malloc_array_alignedN.
|
||||
*/
|
||||
template<typename T> inline T *MEM_malloc_arrayN(const size_t length, const char *allocation_name)
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
/* MSVC considers C-style types using the DNA_DEFINE_CXX_METHODS as non-trivial (more
|
||||
* specifically, non-trivially copyable, likely because the default copy constructors are
|
||||
* deleted). GCC and clang (both on linux, OSX, and clang-cl on Windows on Arm) do not.
|
||||
*
|
||||
* So for now, use a more restricted check on MSVC, should still catch most of actual invalid
|
||||
* cases. */
|
||||
static_assert(std::is_trivially_constructible_v<T>,
|
||||
"For non-trivial types, MEM_new must be used.");
|
||||
# else
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
|
||||
# endif
|
||||
return static_cast<T *>(
|
||||
MEM_malloc_arrayN_aligned(length, sizeof(T), alignof(T), allocation_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate memory for an object of type #T and memory-copy `other` into it.
|
||||
* Only applicable for trivial types.
|
||||
*
|
||||
* This function works around the problem of copy-constructing DNA structs which contains
|
||||
* deprecated fields: some compilers will generate access deprecated field warnings in implicitly
|
||||
* defined copy constructors.
|
||||
*
|
||||
* This is a better alternative to the C-style implementation of #MEM_dupallocN, unless the source
|
||||
* is an array or of a non-fully-defined type.
|
||||
*/
|
||||
template<typename T> inline T *MEM_dupallocN(const char *allocation_name, const T &other)
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
/* MSVC considers C-style types using the DNA_DEFINE_CXX_METHODS as non-trivial (more
|
||||
* specifically, non-trivially copyable, likely because the default copy constructors are
|
||||
* deleted). GCC and clang (both on linux, OSX, and clang-cl on Windows on Arm) do not.
|
||||
*
|
||||
* So for now, use a more restricted check on MSVC, should still catch most of actual invalid
|
||||
* cases. */
|
||||
static_assert(std::is_trivially_constructible_v<T>,
|
||||
"For non-trivial types, MEM_new must be used.");
|
||||
# else
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
|
||||
# endif
|
||||
T *new_object = static_cast<T *>(MEM_mallocN_aligned(sizeof(T), alignof(T), allocation_name));
|
||||
if (new_object) {
|
||||
memcpy(new_object, &other, sizeof(T));
|
||||
}
|
||||
return new_object;
|
||||
}
|
||||
|
||||
template<typename T> inline void MEM_freeN(T *ptr)
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
/* MSVC considers C-style types using the DNA_DEFINE_CXX_METHODS as non-trivial (more
|
||||
* specifically, non-trivially copyable, likely because the default copy constructors are
|
||||
* deleted). GCC and clang (both on linux, OSX, and clang-cl on Windows on Arm) do not.
|
||||
*
|
||||
* So for now, use a more restricted check on MSVC, should still catch most of actual invalid
|
||||
* cases. */
|
||||
static_assert(std::is_trivially_destructible_v<T>,
|
||||
"For non-trivial types, MEM_delete must be used.");
|
||||
# else
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_delete must be used.");
|
||||
# endif
|
||||
mem_guarded::internal::mem_freeN_ex(const_cast<void *>(static_cast<const void *>(ptr)),
|
||||
mem_guarded::internal::AllocationType::ALLOC_FREE);
|
||||
}
|
||||
|
||||
/** Allocation functions (for C++ only). */
|
||||
/** Define overloaded new/delete operators for C++ types. */
|
||||
# define MEM_CXX_CLASS_ALLOC_FUNCS(_id) \
|
||||
public: \
|
||||
void *operator new(size_t num_bytes) \
|
||||
@@ -559,6 +454,142 @@ template<typename T> inline void MEM_freeN(T *ptr)
|
||||
*/ \
|
||||
void operator delete(void * /*ptr_to_free*/, void * /*ptr*/) {}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/**
|
||||
* \name Type-aware allocation API.
|
||||
*
|
||||
* Templated, type-safe versions of C-style allocation & freeing API.
|
||||
*
|
||||
* These functions only allocate or free memory, without any calls to constructors or destructors.
|
||||
*
|
||||
* \note MSVC considers C-style types using the #DNA_DEFINE_CXX_METHODS as non-trivial (more
|
||||
* specifically, non-trivially copyable, likely because the default copy constructors are
|
||||
* deleted by this macro). GCC and clang (both on linux, OSX, and clang-cl on Windows on Arm) do
|
||||
* not. So for now, `MEM_[cm]allocN<T>` and related templates use slightly more relaxed checks on
|
||||
* MSVC. These should still catch most of the real-life invalid cases.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Allocate zero-initialized memory for an object of type #T. The constructor of #T is not called,
|
||||
* therefore this must only be used with trivial types (like all C types).
|
||||
*
|
||||
* When allocating an enforced specific amount of bytes, the C version of this function should be
|
||||
* used instead. While this should be avoided in C++ code, it is still required in some cases, e.g.
|
||||
* for ID allocation based on #IDTypeInfo::struct_size.
|
||||
*
|
||||
* #MEM_freeN must be used to free a pointer returned by this call. Calling #MEM_delete on it is
|
||||
* illegal.
|
||||
*/
|
||||
template<typename T> inline T *MEM_callocN(const char *allocation_name)
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
static_assert(std::is_trivially_constructible_v<T>,
|
||||
"For non-trivial types, MEM_new must be used.");
|
||||
# else
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
|
||||
# endif
|
||||
return static_cast<T *>(MEM_calloc_arrayN_aligned(1, sizeof(T), alignof(T), allocation_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Type-safe version of #MEM_calloc_arrayN/#MEM_calloc_array_alignedN.
|
||||
*
|
||||
* It has the same restrictions and limitations as the type-safe version of #MEM_callocN<T>.
|
||||
*/
|
||||
template<typename T> inline T *MEM_calloc_arrayN(const size_t length, const char *allocation_name)
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
static_assert(std::is_trivially_constructible_v<T>,
|
||||
"For non-trivial types, MEM_new must be used.");
|
||||
# else
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
|
||||
# endif
|
||||
return static_cast<T *>(
|
||||
MEM_calloc_arrayN_aligned(length, sizeof(T), alignof(T), allocation_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate uninitialized memory for an object of type #T. The constructor of #T is not called,
|
||||
* therefore this must only be used with trivial types (like all C types).
|
||||
*
|
||||
* When allocating an enforced specific amount of bytes, the C version of this function should be
|
||||
* used instead. While this should be avoided in C++ code, it is still required in some cases, e.g.
|
||||
* for ID allocation based on #IDTypeInfo::struct_size.
|
||||
*
|
||||
* #MEM_freeN must be used to free a pointer returned by this call. Calling #MEM_delete on it is
|
||||
* illegal.
|
||||
*/
|
||||
template<typename T> inline T *MEM_mallocN(const char *allocation_name)
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
static_assert(std::is_trivially_constructible_v<T>,
|
||||
"For non-trivial types, MEM_new must be used.");
|
||||
# else
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
|
||||
# endif
|
||||
return static_cast<T *>(MEM_malloc_arrayN_aligned(1, sizeof(T), alignof(T), allocation_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Type-safe version of #MEM_malloc_arrayN/#MEM_malloc_array_alignedN.
|
||||
*
|
||||
* It has the same restrictions and limitations as the type-safe version of #MEM_mallocN<T>.
|
||||
*/
|
||||
template<typename T> inline T *MEM_malloc_arrayN(const size_t length, const char *allocation_name)
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
static_assert(std::is_trivially_constructible_v<T>,
|
||||
"For non-trivial types, MEM_new must be used.");
|
||||
# else
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
|
||||
# endif
|
||||
return static_cast<T *>(
|
||||
MEM_malloc_arrayN_aligned(length, sizeof(T), alignof(T), allocation_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate memory for an object of type #T and memory-copy `other` into it.
|
||||
* Only applicable for trivial types.
|
||||
*
|
||||
* This function works around the problem of copy-constructing DNA structs which contains
|
||||
* deprecated fields: some compilers will generate access deprecated field warnings in implicitly
|
||||
* defined copy constructors.
|
||||
*
|
||||
* This is a better alternative to the C-style implementation of #MEM_dupallocN, unless the source
|
||||
* is an array or of a non-fully-defined type.
|
||||
*/
|
||||
template<typename T> inline T *MEM_dupallocN(const char *allocation_name, const T &other)
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
static_assert(std::is_trivially_constructible_v<T>,
|
||||
"For non-trivial types, MEM_new must be used.");
|
||||
# else
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
|
||||
# endif
|
||||
T *new_object = static_cast<T *>(MEM_mallocN_aligned(sizeof(T), alignof(T), allocation_name));
|
||||
if (new_object) {
|
||||
memcpy(new_object, &other, sizeof(T));
|
||||
}
|
||||
return new_object;
|
||||
}
|
||||
|
||||
template<typename T> inline void MEM_freeN(T *ptr)
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
static_assert(std::is_trivially_destructible_v<T>,
|
||||
"For non-trivial types, MEM_delete must be used.");
|
||||
# else
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_delete must be used.");
|
||||
# endif
|
||||
mem_guarded::internal::mem_freeN_ex(const_cast<void *>(static_cast<const void *>(ptr)),
|
||||
mem_guarded::internal::AllocationType::ALLOC_FREE);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/**
|
||||
* Construct a T that will only be destructed after leak detection is run.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user