Add aligned memory allocators

When working with storage (especially GInputStream or GOutputStream) it
is preferred to use page-aligned buffers so that the operating system
can do page-mapping tricks as the operation passes through the kernel.

Another use case is allocating memory used for vectorised operations,
which must be aligned to specific boundaries.

POSIX and Windows, as well as the C11 specification, provide this kind
of allocator functions, and GLib already makes use of it inside GSlice.
It would be convenient to have a public, portable wrapper that other
projects can use.

Fixes: #2574
This commit is contained in:
Emmanuele Bassi 2022-01-06 18:03:16 +00:00 committed by Philip Withnall
parent ae0ec9b753
commit 475d574440
4 changed files with 178 additions and 2 deletions

View File

@ -1393,6 +1393,11 @@ g_alloca0
g_newa
g_newa0
<SUBSECTION>
g_aligned_alloc
g_aligned_alloc0
g_aligned_free
<SUBSECTION>
g_memmove
g_memdup

View File

@ -30,6 +30,25 @@
#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 <string.h>
#include <signal.h>
@ -522,3 +541,133 @@ g_mem_profile (void)
{
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;
}
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);
}

View File

@ -111,6 +111,17 @@ gpointer g_try_realloc_n (gpointer mem,
gsize n_blocks,
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
#define g_clear_pointer(pp, destroy) \
G_STMT_START \

View File

@ -648,8 +648,19 @@ if host_system == 'android'
endif
# Check that posix_memalign() is usable; must use header
if host_system != 'windows' and cc.has_function('posix_memalign', prefix : '#include <stdlib.h>')
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>')
glib_conf.set('HAVE_POSIX_MEMALIGN', 1)
endif