Update realloc(p,0) semantics. (#753)
* Factor out small sizeclass check * Update realloc(p,0) semantics This commit changes the behaviour of realloc(p,0) to be free(p) if p!=nullptr, and malloc(0) if p== nullptr.
This commit is contained in:
committed by
GitHub
parent
6622dc584e
commit
06df9dd9e1
@@ -52,6 +52,25 @@ namespace snmalloc::libc
|
||||
|
||||
SNMALLOC_FAST_PATH_INLINE void* realloc(void* ptr, size_t size)
|
||||
{
|
||||
// Glibc treats
|
||||
// realloc(p, 0) as free(p)
|
||||
// realloc(nullptr, s) as malloc(s)
|
||||
// and for the overlap
|
||||
// realloc(nullptr, 0) as malloc(1)
|
||||
// Without the first two we don't pass the glibc tests.
|
||||
// The last one is required by various gnu utilities such as grep, sed.
|
||||
if (SNMALLOC_UNLIKELY(!is_small_sizeclass(size)))
|
||||
{
|
||||
if (SNMALLOC_UNLIKELY(size == 0))
|
||||
{
|
||||
if (SNMALLOC_UNLIKELY(ptr == nullptr))
|
||||
return malloc(1);
|
||||
|
||||
dealloc(ptr);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
size_t sz = alloc_size(ptr);
|
||||
// Keep the current allocation if the given size is in the same sizeclass.
|
||||
if (sz == round_size(size))
|
||||
@@ -72,13 +91,10 @@ namespace snmalloc::libc
|
||||
}
|
||||
dealloc(ptr);
|
||||
}
|
||||
else if (SNMALLOC_LIKELY(size == 0))
|
||||
{
|
||||
dealloc(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return set_error();
|
||||
// Error should be set by alloc on this path already.
|
||||
SNMALLOC_ASSERT(errno == ENOMEM);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -457,6 +457,17 @@ namespace snmalloc
|
||||
|
||||
constexpr SizeClassLookup sizeclass_lookup = SizeClassLookup();
|
||||
|
||||
/**
|
||||
* @brief Returns true if the size is a small sizeclass. Note that
|
||||
* 0 is not considered a small sizeclass.
|
||||
*/
|
||||
constexpr bool is_small_sizeclass(size_t size)
|
||||
{
|
||||
// Perform the - 1 on size, so that zero wraps around and ends up on
|
||||
// slow path.
|
||||
return (size - 1) < sizeclass_to_size(NUM_SMALL_SIZECLASSES - 1);
|
||||
}
|
||||
|
||||
constexpr smallsizeclass_t size_to_sizeclass(size_t size)
|
||||
{
|
||||
auto index = sizeclass_lookup_index(size);
|
||||
@@ -482,7 +493,7 @@ namespace snmalloc
|
||||
*/
|
||||
static inline sizeclass_t size_to_sizeclass_full(size_t size)
|
||||
{
|
||||
if ((size - 1) < sizeclass_to_size(NUM_SMALL_SIZECLASSES - 1))
|
||||
if (is_small_sizeclass(size))
|
||||
{
|
||||
return sizeclass_t::from_small_class(size_to_sizeclass(size));
|
||||
}
|
||||
@@ -493,26 +504,28 @@ namespace snmalloc
|
||||
|
||||
inline SNMALLOC_FAST_PATH static size_t round_size(size_t size)
|
||||
{
|
||||
if (size > sizeclass_to_size(NUM_SMALL_SIZECLASSES - 1))
|
||||
if (is_small_sizeclass(size))
|
||||
{
|
||||
if (size > bits::one_at_bit(bits::BITS - 1))
|
||||
{
|
||||
// This size is too large, no rounding should occur as will result in a
|
||||
// failed allocation later.
|
||||
return size;
|
||||
}
|
||||
return bits::next_pow2(size);
|
||||
return sizeclass_to_size(size_to_sizeclass(size));
|
||||
}
|
||||
// If realloc(ptr, 0) returns nullptr, some consumers treat this as a
|
||||
// reallocation failure and abort. To avoid this, we round up the size of
|
||||
// requested allocations to the smallest size class. This can be changed
|
||||
// on any platform that's happy to return nullptr from realloc(ptr,0) and
|
||||
// should eventually become a configuration option.
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
// If realloc(ptr, 0) returns nullptr, some consumers treat this as a
|
||||
// reallocation failure and abort. To avoid this, we round up the size of
|
||||
// requested allocations to the smallest size class. This can be changed
|
||||
// on any platform that's happy to return nullptr from realloc(ptr,0) and
|
||||
// should eventually become a configuration option.
|
||||
return sizeclass_to_size(size_to_sizeclass(1));
|
||||
}
|
||||
return sizeclass_to_size(size_to_sizeclass(size));
|
||||
|
||||
if (size > bits::one_at_bit(bits::BITS - 1))
|
||||
{
|
||||
// This size is too large, no rounding should occur as will result in a
|
||||
// failed allocation later.
|
||||
return size;
|
||||
}
|
||||
return bits::next_pow2(size);
|
||||
}
|
||||
|
||||
/// Returns the alignment that this size naturally has, that is
|
||||
@@ -549,9 +562,7 @@ namespace snmalloc
|
||||
// sizeclass calculation. We use the same fast path constant to
|
||||
// move the case where result==0 to the slow path, and then check for which
|
||||
// case we are in.
|
||||
constexpr size_t SmallSizeClassUpperBound =
|
||||
sizeclass_to_size(NUM_SMALL_SIZECLASSES - 1) - 1;
|
||||
if (SNMALLOC_LIKELY((result - 1) < SmallSizeClassUpperBound))
|
||||
if (is_small_sizeclass(result))
|
||||
return result;
|
||||
|
||||
// We are in the slow path, so we need to check for overflow.
|
||||
|
||||
@@ -265,6 +265,9 @@ int main(int argc, char** argv)
|
||||
test_calloc(0, size, SUCCESS, false);
|
||||
}
|
||||
|
||||
// Check realloc(nullptr,0) behaves like malloc(1)
|
||||
test_realloc(nullptr, 0, SUCCESS, false);
|
||||
|
||||
for (smallsizeclass_t sc = 0; sc < NUM_SMALL_SIZECLASSES; sc++)
|
||||
{
|
||||
const size_t size = sizeclass_to_size(sc);
|
||||
@@ -277,6 +280,8 @@ int main(int argc, char** argv)
|
||||
test_realloc(our_malloc(size), size2, SUCCESS, false);
|
||||
test_realloc(our_malloc(size + 1), size2, SUCCESS, false);
|
||||
}
|
||||
// Check realloc(p,0), behaves like free(p), if p != nullptr
|
||||
test_realloc(our_malloc(size), 0, SUCCESS, true);
|
||||
}
|
||||
|
||||
for (smallsizeclass_t sc = 0; sc < (MAX_SMALL_SIZECLASS_BITS + 4); sc++)
|
||||
|
||||
Reference in New Issue
Block a user