Skip to content

Commit ae9265d

Browse files
committed
Add overalignment support for new_delete_resource for newer (__cpp_aligned_new) and older standards, including some workaround for targets where __STDCPP_DEFAULT_NEW_ALIGNMENT__ value is not correctly aligned between GCC and the malloc implementation (Win32)
1 parent 52b34fb commit ae9265d

File tree

3 files changed

+204
-18
lines changed

3 files changed

+204
-18
lines changed

doc/container.qbk

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1451,8 +1451,9 @@ use [*Boost.Container]? There are several reasons for that:
14511451
* Implemented C++20's [funcref boost::container::uninitialized_construct_using_allocator uninitialized_construct_using_allocator]
14521452
and [funcref boost::container::make_obj_using_allocator make_obj_using_allocator].
14531453
* Added `[[nodiscard]]` to several allocator and PMR utilities.
1454-
* Implemented overaligned operator new/delete support for `new_allocator` and `pmr::new_delete_resource()`
1455-
when C++17's `cpp_aligned_new` is available.
1454+
* Implemented overaligned operator new/delete support for `new_allocator` and `pmr::new_delete_resource()`:
1455+
* Uses C++17's `cpp_aligned_new` if available.
1456+
* Uses alternative aligned allocation functions for Win32/Unix otherwise.
14561457
* Fixed bugs/issues:
14571458
* [@https://github.com/boostorg/container/issues/323 GitHub #323: ['"flat_tree::try_emplace UB"]].
14581459

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
//////////////////////////////////////////////////////////////////////////////
2+
//
3+
// (C) Copyright Ion Gaztanaga 2025-2025. Distributed under the Boost
4+
// Software License, Version 1.0. (See accompanying file
5+
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6+
//
7+
// See http://www.boost.org/libs/container for documentation.
8+
//
9+
//////////////////////////////////////////////////////////////////////////////
10+
#ifndef BOOST_CONTAINER_DETAIL_ALIGNED_ALLOC_HPP
11+
#define BOOST_CONTAINER_DETAIL_ALIGNED_ALLOC_HPP
12+
13+
#ifndef BOOST_CONFIG_HPP
14+
# include <boost/config.hpp>
15+
#endif
16+
17+
#if defined(BOOST_HAS_PRAGMA_ONCE)
18+
# pragma once
19+
#endif
20+
21+
// Platform detection
22+
#if defined(_WIN32) && !defined(__CYGWIN__)
23+
#define BOOST_CONTAINER_HAS_ALIGNED_MALLOC
24+
#else
25+
#include <unistd.h> //Include it to detect POSIX features
26+
#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)
27+
#define BOOST_CONTAINER_HAS_POSIX_MEMALIGN
28+
#elif defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 600)
29+
#define BOOST_CONTAINER_HAS_POSIX_MEMALIGN
30+
#elif defined(__APPLE__)
31+
#include <Availability.h>
32+
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500
33+
#define BOOST_CONTAINER_HAS_ALIGNED_ALLOC
34+
#else
35+
#define BOOST_CONTAINER_HAS_POSIX_MEMALIGN
36+
#endif
37+
#elif defined(__ANDROID__)
38+
#if (__ANDROID_API__ >= 28)
39+
#define BOOST_CONTAINER_HAS_ALIGNED_ALLOC
40+
#else
41+
#define BOOST_CONTAINER_HAS_POSIX_MEMALIGN
42+
#endif
43+
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
44+
#define BOOST_CONTAINER_HAS_ALIGNED_ALLOC
45+
#endif
46+
#endif
47+
48+
// Include
49+
#if defined(BOOST_CONTAINER_HAS_ALIGNED_MALLOC)
50+
#include <malloc.h>
51+
#elif defined(BOOST_CONTAINER_HAS_POSIX_MEMALIGN)
52+
#include <stdlib.h>
53+
#elif defined(BOOST_CONTAINER_HAS_ALIGNED_ALLOC)
54+
#include <stdlib.h>
55+
#else
56+
#include <stdlib.h> //for malloc
57+
#endif
58+
59+
namespace boost {
60+
namespace container {
61+
namespace dtl {
62+
63+
#if defined(BOOST_CONTAINER_HAS_POSIX_MEMALIGN)
64+
65+
inline void* aligned_allocate(std::size_t al, std::size_t sz)
66+
{
67+
void *ptr;
68+
// posix_memalign requires aligned multiple of void*
69+
if (al < sizeof(void*))
70+
al = sizeof(void*);
71+
int ret = posix_memalign(&ptr, al, sz);
72+
if (ret != 0)
73+
return 0;
74+
return ptr;
75+
}
76+
77+
#elif defined(BOOST_CONTAINER_HAS_ALIGNED_ALLOC)
78+
79+
inline void* aligned_allocate(std::size_t al, std::size_t sz)
80+
{
81+
// Some aligned_allocate are based on posix_memalign so require also minimal alignment
82+
if (al < sizeof(void*))
83+
al = sizeof(void*);
84+
85+
// aligned_allocate requires size to be a multiple of alignment
86+
std::size_t rounded_size = std::size_t(sz + al - 1u) & ~std::size_t(al - 1);
87+
88+
//Check for rounded size overflow
89+
return rounded_size ? aligned_allocate(al, rounded_size) : 0;
90+
}
91+
92+
#elif defined(BOOST_CONTAINER_HAS_ALIGNED_MALLOC)
93+
94+
inline void* aligned_allocate(std::size_t al, std::size_t sz)
95+
{
96+
return _aligned_malloc(sz, al);
97+
}
98+
99+
#else
100+
101+
inline void* aligned_allocate(std::size_t al, std::size_t sz)
102+
{
103+
//Make room for a back pointer metadata
104+
void* const mptr = malloc(sz + sizeof(void*) + al);
105+
if (!mptr)
106+
return 0;
107+
108+
//Now align the returned pointer (which will be aligned at least to sizeof(void*)
109+
std::size_t raw_addr = reinterpret_cast<std::size_t>(mptr);
110+
std::size_t offset = sizeof(void*);
111+
void *const ptr = reinterpret_cast<void*>((raw_addr + offset + al - 1u) & ~(al - 1u));
112+
113+
// Store the original pointer just before the aligned address
114+
void** backpointer = reinterpret_cast<void**>(ptr) - 1;
115+
*backpointer = mptr;
116+
return ptr;
117+
}
118+
119+
#endif
120+
121+
#if defined(BOOST_CONTAINER_HAS_ALIGNED_ALLOC) || defined(BOOST_CONTAINER_HAS_POSIX_MEMALIGN)
122+
123+
inline void aligned_deallocate(void* ptr)
124+
{
125+
if (!ptr)
126+
return;
127+
free(ptr);
128+
}
129+
130+
#elif defined(BOOST_CONTAINER_HAS_ALIGNED_MALLOC)
131+
132+
inline void aligned_deallocate(void* ptr)
133+
{
134+
_aligned_free(ptr); //_aligned_free supports NULL ptr
135+
}
136+
137+
#else
138+
139+
inline void aligned_deallocate(void* ptr)
140+
{
141+
// Obtain backpointer data and free it
142+
void** storage = reinterpret_cast<void**>(ptr) - 1;
143+
free(*storage);
144+
}
145+
146+
#endif//
147+
148+
} //namespace dtl {
149+
} //namespace container {
150+
} //namespace boost {
151+
152+
#endif //#ifndef BOOST_CONTAINER_DETAIL_ALIGNED_ALLOC_HPP

include/boost/container/detail/operator_new_helpers.hpp

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,42 +22,75 @@
2222
#include <boost/container/throw_exception.hpp>
2323
#include <boost/container/detail/type_traits.hpp>
2424

25+
#if !defined(__cpp_aligned_new)
26+
#include <boost/container/detail/aligned_alloc.hpp>
27+
#endif
28+
2529
namespace boost {
2630
namespace container {
2731
namespace dtl {
2832

33+
BOOST_CONTAINER_FORCEINLINE bool operator_new_raw_overaligned(std::size_t alignment)
34+
{
35+
//GCC-clang on Mingw-w64 has problems with malloc (MSVCRT / UCRT) alignment not matching
36+
//__STDCPP_DEFAULT_NEW_ALIGNMENT__, since HeapAlloc alignment is 8 for 32 bit targets
37+
#if !defined(__cpp_aligned_new) || (defined(_WIN32) && !defined(_WIN64) && !defined(_MSC_VER))
38+
return alignment > 2u*sizeof(void*);
39+
#else
40+
return alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__;
41+
#endif
42+
}
43+
44+
BOOST_CONTAINER_FORCEINLINE bool operator_new_raw_overaligned_tricky()
45+
{
46+
//GCC-clang on Mingw-w64 has problems with malloc (MSVCRT / UCRT) alignment not matching
47+
//__STDCPP_DEFAULT_NEW_ALIGNMENT__, since HeapAlloc alignment is 8 for 32 bit targets
48+
#if !defined(__cpp_aligned_new) || (defined(_WIN32) && !defined(_WIN64) && !defined(_MSC_VER))
49+
return true;
50+
#else
51+
return false;
52+
#endif
53+
}
54+
2955
BOOST_CONTAINER_FORCEINLINE void* operator_new_raw_allocate(const std::size_t size, const std::size_t alignment)
3056
{
3157
(void)alignment;
32-
#if defined(__cpp_aligned_new)
33-
if(__STDCPP_DEFAULT_NEW_ALIGNMENT__ < alignment) {
58+
if(operator_new_raw_overaligned(alignment)) {
59+
#if defined(__cpp_aligned_new)
3460
return ::operator new(size, std::align_val_t(alignment));
61+
#else
62+
//C++ requires zero-sized allocations to return a non-null pointer
63+
return aligned_allocate(alignment, !size ? 1 : size);
64+
#endif
65+
}
66+
else{
67+
return ::operator new(size);
3568
}
36-
#endif
37-
return ::operator new(size);
3869
}
3970

4071
BOOST_CONTAINER_FORCEINLINE void operator_delete_raw_deallocate
4172
(void* const ptr, const std::size_t size, const std::size_t alignment) BOOST_NOEXCEPT_OR_NOTHROW
4273
{
4374
(void)size;
4475
(void)alignment;
45-
#ifdef __cpp_aligned_new
46-
if(__STDCPP_DEFAULT_NEW_ALIGNMENT__ < alignment) {
76+
if(operator_new_raw_overaligned(alignment)) {
77+
#if defined(__cpp_aligned_new)
78+
# if defined(__cpp_sized_deallocation)
79+
::operator delete(ptr, size, std::align_val_t(alignment));
80+
#else
81+
::operator delete(ptr, std::align_val_t(alignment));
82+
# endif
83+
#else
84+
aligned_deallocate(ptr);
85+
#endif
86+
}
87+
else {
4788
# if defined(__cpp_sized_deallocation)
48-
::operator delete(ptr, size, std::align_val_t(alignment));
89+
::operator delete(ptr, size);
4990
#else
50-
::operator delete(ptr, std::align_val_t(alignment));
91+
::operator delete(ptr);
5192
# endif
52-
return;
5393
}
54-
#endif
55-
56-
# if defined(__cpp_sized_deallocation)
57-
::operator delete(ptr, size);
58-
#else
59-
::operator delete(ptr);
60-
# endif
6194
}
6295

6396
template <class T>

0 commit comments

Comments
 (0)