mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-12 23:46:17 +01:00
Merge branch 'ebassi/aligned-alloc' into 'main'
Add aligned memory allocators Closes #2574 See merge request GNOME/glib!2421
This commit is contained in:
commit
f9c08308ea
@ -1393,6 +1393,11 @@ g_alloca0
|
|||||||
g_newa
|
g_newa
|
||||||
g_newa0
|
g_newa0
|
||||||
|
|
||||||
|
<SUBSECTION>
|
||||||
|
g_aligned_alloc
|
||||||
|
g_aligned_alloc0
|
||||||
|
g_aligned_free
|
||||||
|
|
||||||
<SUBSECTION>
|
<SUBSECTION>
|
||||||
g_memmove
|
g_memmove
|
||||||
g_memdup
|
g_memdup
|
||||||
|
157
glib/gmem.c
157
glib/gmem.c
@ -30,6 +30,25 @@
|
|||||||
|
|
||||||
#include "gmem.h"
|
#include "gmem.h"
|
||||||
|
|
||||||
|
#if defined(HAVE_POSIX_MEMALIGN) && !defined(_XOPEN_SOURCE)
|
||||||
|
# define _XOPEN_SOURCE 600
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_MEMALIGN) || defined(HAVE__ALIGNED_MALLOC)
|
||||||
|
/* Required for _aligned_malloc() and _aligned_free() on Windows */
|
||||||
|
#include <malloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE__ALIGNED_MALLOC
|
||||||
|
/* _aligned_malloc() takes parameters of aligned_malloc() in reverse order */
|
||||||
|
# define aligned_alloc(alignment, size) _aligned_malloc (size, alignment)
|
||||||
|
|
||||||
|
/* _aligned_malloc()'ed memory must be freed by _align_free() on MSVC */
|
||||||
|
# define aligned_free(x) _aligned_free (x)
|
||||||
|
#else
|
||||||
|
# define aligned_free(x) free (x)
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@ -522,3 +541,141 @@ g_mem_profile (void)
|
|||||||
{
|
{
|
||||||
g_warning (G_STRLOC ": memory profiling not supported");
|
g_warning (G_STRLOC ": memory profiling not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_aligned_alloc:
|
||||||
|
* @n_blocks: the number of blocks to allocate
|
||||||
|
* @n_block_bytes: the size of each block in bytes
|
||||||
|
* @alignment: the alignment to be enforced, which must be a positive power of 2
|
||||||
|
* and a multiple of `sizeof(void*)`
|
||||||
|
*
|
||||||
|
* This function is similar to g_malloc(), allocating (@n_blocks * @n_block_bytes)
|
||||||
|
* bytes, but care is taken to align the allocated memory to with the given
|
||||||
|
* alignment value. Additionally, it will detect possible overflow during
|
||||||
|
* multiplication.
|
||||||
|
*
|
||||||
|
* Aligned memory allocations returned by this function can only be
|
||||||
|
* freed using g_aligned_free().
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): the allocated memory
|
||||||
|
*
|
||||||
|
* Since: 2.72
|
||||||
|
*/
|
||||||
|
gpointer
|
||||||
|
g_aligned_alloc (gsize n_blocks,
|
||||||
|
gsize n_block_bytes,
|
||||||
|
gsize alignment)
|
||||||
|
{
|
||||||
|
gpointer res = NULL;
|
||||||
|
gsize real_size;
|
||||||
|
|
||||||
|
if (G_UNLIKELY ((alignment == 0) || (alignment & (alignment - 1)) != 0))
|
||||||
|
{
|
||||||
|
g_error ("%s: alignment %"G_GSIZE_FORMAT" must be a positive power of two",
|
||||||
|
G_STRLOC, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_UNLIKELY ((alignment % sizeof (void *)) != 0))
|
||||||
|
{
|
||||||
|
g_error ("%s: alignment %"G_GSIZE_FORMAT" must be a multiple of %"G_GSIZE_FORMAT,
|
||||||
|
G_STRLOC, alignment, sizeof (void *));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SIZE_OVERFLOWS (n_blocks, n_block_bytes))
|
||||||
|
{
|
||||||
|
g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes",
|
||||||
|
G_STRLOC, n_blocks, n_block_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
real_size = n_blocks * n_block_bytes;
|
||||||
|
|
||||||
|
if (G_UNLIKELY (real_size == 0))
|
||||||
|
{
|
||||||
|
TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) real_size, 0, 0));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need to clear errno because posix_memalign() will use its return
|
||||||
|
* value in the same way memalign() and aligned_alloc() will set errno.
|
||||||
|
* Additionally, posix_memalign() will warn if its return value is left
|
||||||
|
* unassigned.
|
||||||
|
*
|
||||||
|
* We handle all possible return values (ENOMEM and EINVAL) with either
|
||||||
|
* precondition or postcondition checking.
|
||||||
|
*/
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
#if defined(HAVE_POSIX_MEMALIGN)
|
||||||
|
errno = posix_memalign (&res, alignment, real_size);
|
||||||
|
#elif defined(HAVE_ALIGNED_ALLOC) || defined(HAVE__ALIGNED_MALLOC)
|
||||||
|
/* real_size must be a multiple of alignment */
|
||||||
|
if (real_size % alignment != 0)
|
||||||
|
{
|
||||||
|
gsize offset = real_size % alignment;
|
||||||
|
|
||||||
|
if (G_MAXSIZE - real_size < (alignment - offset))
|
||||||
|
{
|
||||||
|
g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"+%"G_GSIZE_FORMAT" bytes",
|
||||||
|
G_STRLOC, real_size, (alignment - offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
real_size += (alignment - offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = aligned_alloc (alignment, real_size);
|
||||||
|
#elif defined(HAVE_MEMALIGN)
|
||||||
|
res = memalign (alignment, real_size);
|
||||||
|
#else
|
||||||
|
# error "This platform does not have an aligned memory allocator."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TRACE (GLIB_MEM_ALLOC((void*) res, (unsigned int) real_size, 0, 0));
|
||||||
|
if (res)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes",
|
||||||
|
G_STRLOC, real_size);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_aligned_alloc0:
|
||||||
|
* @n_blocks: the number of blocks to allocate
|
||||||
|
* @n_block_bytes: the size of each block in bytes
|
||||||
|
* @alignment: the alignment to be enforced, which must be a positive power of 2
|
||||||
|
* and a multiple of `sizeof(void*)`
|
||||||
|
*
|
||||||
|
* This function is similar to g_aligned_alloc(), but it will
|
||||||
|
* also clear the allocated memory before returning it.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): the allocated, cleared memory
|
||||||
|
*
|
||||||
|
* Since: 2.72
|
||||||
|
*/
|
||||||
|
gpointer
|
||||||
|
g_aligned_alloc0 (gsize n_blocks,
|
||||||
|
gsize n_block_bytes,
|
||||||
|
gsize alignment)
|
||||||
|
{
|
||||||
|
gpointer res = g_aligned_alloc (n_blocks, n_block_bytes, alignment);
|
||||||
|
|
||||||
|
if (G_LIKELY (res != NULL))
|
||||||
|
memset (res, 0, n_blocks * n_block_bytes);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_aligned_free:
|
||||||
|
* @mem: (nullable): the memory to deallocate
|
||||||
|
*
|
||||||
|
* Frees the memory allocated by g_aligned_alloc().
|
||||||
|
*
|
||||||
|
* Since: 2.72
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
g_aligned_free (gpointer mem)
|
||||||
|
{
|
||||||
|
aligned_free (mem);
|
||||||
|
}
|
||||||
|
11
glib/gmem.h
11
glib/gmem.h
@ -111,6 +111,17 @@ gpointer g_try_realloc_n (gpointer mem,
|
|||||||
gsize n_blocks,
|
gsize n_blocks,
|
||||||
gsize n_block_bytes) G_GNUC_WARN_UNUSED_RESULT;
|
gsize n_block_bytes) G_GNUC_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
GLIB_AVAILABLE_IN_2_72
|
||||||
|
gpointer g_aligned_alloc (gsize n_blocks,
|
||||||
|
gsize n_block_bytes,
|
||||||
|
gsize alignment) G_GNUC_WARN_UNUSED_RESULT G_GNUC_ALLOC_SIZE2(1,2);
|
||||||
|
GLIB_AVAILABLE_IN_2_72
|
||||||
|
gpointer g_aligned_alloc0 (gsize n_blocks,
|
||||||
|
gsize n_block_bytes,
|
||||||
|
gsize alignment) G_GNUC_WARN_UNUSED_RESULT G_GNUC_ALLOC_SIZE2(1,2);
|
||||||
|
GLIB_AVAILABLE_IN_2_72
|
||||||
|
void g_aligned_free (gpointer mem);
|
||||||
|
|
||||||
#if defined(glib_typeof) && GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_58
|
#if defined(glib_typeof) && GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_58
|
||||||
#define g_clear_pointer(pp, destroy) \
|
#define g_clear_pointer(pp, destroy) \
|
||||||
G_STMT_START \
|
G_STMT_START \
|
||||||
|
@ -34,13 +34,14 @@ static gsize a = G_MAXSIZE / 10 + 10;
|
|||||||
static gsize b = 10;
|
static gsize b = 10;
|
||||||
typedef char X[10];
|
typedef char X[10];
|
||||||
|
|
||||||
#define MEM_OVERFLOW_TEST(name, code) \
|
#define MEM_OVERFLOW_TEST(name, code) MEM_OVERFLOW_TEST_FULL(name, code, g_free)
|
||||||
|
#define MEM_OVERFLOW_TEST_FULL(name, code, free_func) \
|
||||||
static void \
|
static void \
|
||||||
mem_overflow_ ## name (void) \
|
mem_overflow_ ## name (void) \
|
||||||
{ \
|
{ \
|
||||||
gpointer p; \
|
gpointer p; \
|
||||||
code; \
|
code; \
|
||||||
g_free (p); \
|
free_func (p); \
|
||||||
exit (0); \
|
exit (0); \
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,6 +69,12 @@ MEM_OVERFLOW_TEST (new0_b, p = g_new0 (X, b))
|
|||||||
MEM_OVERFLOW_TEST (renew_a, p = g_malloc (1); p = g_renew (X, p, a))
|
MEM_OVERFLOW_TEST (renew_a, p = g_malloc (1); p = g_renew (X, p, a))
|
||||||
MEM_OVERFLOW_TEST (renew_b, p = g_malloc (1); p = g_renew (X, p, b))
|
MEM_OVERFLOW_TEST (renew_b, p = g_malloc (1); p = g_renew (X, p, b))
|
||||||
|
|
||||||
|
MEM_OVERFLOW_TEST_FULL (aligned_alloc_a, p = g_aligned_alloc (sizeof(X), a, 16), g_aligned_free)
|
||||||
|
MEM_OVERFLOW_TEST_FULL (aligned_alloc_b, p = g_aligned_alloc (sizeof(X), b, 16), g_aligned_free)
|
||||||
|
|
||||||
|
MEM_OVERFLOW_TEST_FULL (aligned_alloc0_a, p = g_aligned_alloc0 (sizeof(X), a, 16), g_aligned_free)
|
||||||
|
MEM_OVERFLOW_TEST_FULL (aligned_alloc0_b, p = g_aligned_alloc0 (sizeof(X), b, 16), g_aligned_free)
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mem_overflow_malloc_0 (void)
|
mem_overflow_malloc_0 (void)
|
||||||
{
|
{
|
||||||
@ -171,6 +178,12 @@ mem_overflow (void)
|
|||||||
|
|
||||||
CHECK_SUBPROCESS_PASS (malloc_0);
|
CHECK_SUBPROCESS_PASS (malloc_0);
|
||||||
CHECK_SUBPROCESS_PASS (realloc_0);
|
CHECK_SUBPROCESS_PASS (realloc_0);
|
||||||
|
|
||||||
|
CHECK_SUBPROCESS_FAIL (aligned_alloc_a);
|
||||||
|
CHECK_SUBPROCESS_PASS (aligned_alloc_b);
|
||||||
|
|
||||||
|
CHECK_SUBPROCESS_FAIL (aligned_alloc0_a);
|
||||||
|
CHECK_SUBPROCESS_PASS (aligned_alloc0_b);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
@ -231,6 +244,10 @@ main (int argc,
|
|||||||
g_test_add_func ("/mem/overflow/subprocess/renew_b", mem_overflow_renew_b);
|
g_test_add_func ("/mem/overflow/subprocess/renew_b", mem_overflow_renew_b);
|
||||||
g_test_add_func ("/mem/overflow/subprocess/malloc_0", mem_overflow_malloc_0);
|
g_test_add_func ("/mem/overflow/subprocess/malloc_0", mem_overflow_malloc_0);
|
||||||
g_test_add_func ("/mem/overflow/subprocess/realloc_0", mem_overflow_realloc_0);
|
g_test_add_func ("/mem/overflow/subprocess/realloc_0", mem_overflow_realloc_0);
|
||||||
|
g_test_add_func ("/mem/overflow/subprocess/aligned_alloc_a", mem_overflow_aligned_alloc_a);
|
||||||
|
g_test_add_func ("/mem/overflow/subprocess/aligned_alloc_b", mem_overflow_aligned_alloc_b);
|
||||||
|
g_test_add_func ("/mem/overflow/subprocess/aligned_alloc0_a", mem_overflow_aligned_alloc0_a);
|
||||||
|
g_test_add_func ("/mem/overflow/subprocess/aligned_alloc0_b", mem_overflow_aligned_alloc0_b);
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
g_test_add_func ("/mem/empty-alloc", empty_alloc);
|
g_test_add_func ("/mem/empty-alloc", empty_alloc);
|
||||||
|
@ -915,6 +915,100 @@ test_misc_mem (void)
|
|||||||
g_assert (a == NULL);
|
g_assert (a == NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
aligned_alloc_nz (void)
|
||||||
|
{
|
||||||
|
gpointer a;
|
||||||
|
|
||||||
|
/* Test an alignment that’s zero */
|
||||||
|
a = g_aligned_alloc (16, sizeof(char), 0);
|
||||||
|
g_assert_null (a);
|
||||||
|
exit (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
aligned_alloc_npot (void)
|
||||||
|
{
|
||||||
|
gpointer a;
|
||||||
|
|
||||||
|
/* Test an alignment that’s not a power of two */
|
||||||
|
a = g_aligned_alloc (16, sizeof(char), 15);
|
||||||
|
g_assert_null (a);
|
||||||
|
exit (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
aligned_alloc_nmov (void)
|
||||||
|
{
|
||||||
|
gpointer a;
|
||||||
|
|
||||||
|
/* Test an alignment that’s not a multiple of sizeof(void*) */
|
||||||
|
a = g_aligned_alloc (16, sizeof(char), 4);
|
||||||
|
g_assert_null (a);
|
||||||
|
exit (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_aligned_mem (void)
|
||||||
|
{
|
||||||
|
gpointer a;
|
||||||
|
|
||||||
|
g_test_summary ("Aligned memory allocator");
|
||||||
|
|
||||||
|
a = g_aligned_alloc (0, sizeof(int), 8);
|
||||||
|
g_assert_null (a);
|
||||||
|
|
||||||
|
a = g_aligned_alloc0 (0, sizeof(int), 8);
|
||||||
|
g_assert_null (a);
|
||||||
|
|
||||||
|
a = g_aligned_alloc (16, 0, 8);
|
||||||
|
g_assert_null (a);
|
||||||
|
|
||||||
|
#define CHECK_SUBPROCESS_FAIL(name,msg) do { \
|
||||||
|
{ \
|
||||||
|
g_test_message (msg); \
|
||||||
|
g_test_trap_subprocess ("/utils/aligned-mem/subprocess/" #name, 0, 0); \
|
||||||
|
g_test_trap_assert_failed (); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
CHECK_SUBPROCESS_FAIL (aligned_alloc_nz, "Alignment must not be zero");
|
||||||
|
CHECK_SUBPROCESS_FAIL (aligned_alloc_npot, "Alignment must be a power of two");
|
||||||
|
CHECK_SUBPROCESS_FAIL (aligned_alloc_nmov, "Alignment must be a multiple of sizeof(void*)");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_aligned_mem_alignment (void)
|
||||||
|
{
|
||||||
|
gchar *p;
|
||||||
|
|
||||||
|
g_test_summary ("Check that g_aligned_alloc() returns a correctly aligned pointer");
|
||||||
|
|
||||||
|
p = g_aligned_alloc (5, sizeof (*p), 256);
|
||||||
|
g_assert_nonnull (p);
|
||||||
|
g_assert_cmpuint (((guintptr) p) % 256, ==, 0);
|
||||||
|
|
||||||
|
g_aligned_free (p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_aligned_mem_zeroed (void)
|
||||||
|
{
|
||||||
|
gsize n_blocks = 10;
|
||||||
|
guint *p;
|
||||||
|
gsize i;
|
||||||
|
|
||||||
|
g_test_summary ("Check that g_aligned_alloc0() zeroes out its allocation");
|
||||||
|
|
||||||
|
p = g_aligned_alloc0 (n_blocks, sizeof (*p), 16);
|
||||||
|
g_assert_nonnull (p);
|
||||||
|
|
||||||
|
for (i = 0; i < n_blocks; i++)
|
||||||
|
g_assert_cmpuint (p[i], ==, 0);
|
||||||
|
|
||||||
|
g_aligned_free (p);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_nullify (void)
|
test_nullify (void)
|
||||||
{
|
{
|
||||||
@ -1084,6 +1178,12 @@ main (int argc,
|
|||||||
g_test_add_func ("/utils/take-pointer", test_take_pointer);
|
g_test_add_func ("/utils/take-pointer", test_take_pointer);
|
||||||
g_test_add_func ("/utils/clear-source", test_clear_source);
|
g_test_add_func ("/utils/clear-source", test_clear_source);
|
||||||
g_test_add_func ("/utils/misc-mem", test_misc_mem);
|
g_test_add_func ("/utils/misc-mem", test_misc_mem);
|
||||||
|
g_test_add_func ("/utils/aligned-mem", test_aligned_mem);
|
||||||
|
g_test_add_func ("/utils/aligned-mem/subprocess/aligned_alloc_nz", aligned_alloc_nz);
|
||||||
|
g_test_add_func ("/utils/aligned-mem/subprocess/aligned_alloc_npot", aligned_alloc_npot);
|
||||||
|
g_test_add_func ("/utils/aligned-mem/subprocess/aligned_alloc_nmov", aligned_alloc_nmov);
|
||||||
|
g_test_add_func ("/utils/aligned-mem/alignment", test_aligned_mem_alignment);
|
||||||
|
g_test_add_func ("/utils/aligned-mem/zeroed", test_aligned_mem_zeroed);
|
||||||
g_test_add_func ("/utils/nullify", test_nullify);
|
g_test_add_func ("/utils/nullify", test_nullify);
|
||||||
g_test_add_func ("/utils/atexit", test_atexit);
|
g_test_add_func ("/utils/atexit", test_atexit);
|
||||||
g_test_add_func ("/utils/check-setuid", test_check_setuid);
|
g_test_add_func ("/utils/check-setuid", test_check_setuid);
|
||||||
|
13
meson.build
13
meson.build
@ -653,7 +653,18 @@ if host_system == 'android'
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
# Check that posix_memalign() is usable; must use header
|
if cc.has_function('memalign', prefix: '#include <stdlib.h>\n#include <malloc.h>')
|
||||||
|
glib_conf.set('HAVE_MEMALIGN', 1)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if cc.has_function('_aligned_malloc', prefix: '#include <malloc.h>')
|
||||||
|
glib_conf.set('HAVE__ALIGNED_MALLOC', 1)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if host_system != 'windows' and cc.has_function('aligned_alloc', prefix: '#include <stdlib.h>')
|
||||||
|
glib_conf.set('HAVE_ALIGNED_ALLOC', 1)
|
||||||
|
endif
|
||||||
|
|
||||||
if host_system != 'windows' and cc.has_function('posix_memalign', prefix: '#include <stdlib.h>')
|
if host_system != 'windows' and cc.has_function('posix_memalign', prefix: '#include <stdlib.h>')
|
||||||
glib_conf.set('HAVE_POSIX_MEMALIGN', 1)
|
glib_conf.set('HAVE_POSIX_MEMALIGN', 1)
|
||||||
endif
|
endif
|
||||||
|
Loading…
Reference in New Issue
Block a user