ClickHouse/src/Common/memory.h

200 lines
5.1 KiB
C++
Raw Normal View History

#pragma once
2022-03-11 10:04:35 +00:00
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#include <new>
2021-10-02 07:13:14 +00:00
#include <base/defines.h>
2022-03-11 10:04:35 +00:00
#include <Common/Concepts.h>
#include <Common/CurrentMemoryTracker.h>
2023-01-12 14:00:26 +00:00
#include <Common/gwp_asan.h>
#include "config.h"
#if USE_JEMALLOC
# include <jemalloc/jemalloc.h>
#endif
2022-01-18 06:51:13 +00:00
#if !USE_JEMALLOC
# include <cstdlib>
#endif
namespace Memory
{
2022-03-11 10:04:35 +00:00
inline ALWAYS_INLINE size_t alignToSizeT(std::align_val_t align) noexcept
{
return static_cast<size_t>(align);
}
template <std::same_as<std::align_val_t>... TAlign>
requires DB::OptionalArgument<TAlign...>
inline ALWAYS_INLINE void * newImpl(std::size_t size, TAlign... align)
{
2023-01-12 14:00:26 +00:00
if (unlikely(GuardedAlloc.shouldSample()))
{
if constexpr (sizeof...(TAlign) == 1)
{
if (void * ptr = GuardedAlloc.allocate(size, alignToSizeT(align...)))
return ptr;
}
else
{
if (void * ptr = GuardedAlloc.allocate(size))
return ptr;
}
}
2022-03-11 10:04:35 +00:00
void * ptr = nullptr;
if constexpr (sizeof...(TAlign) == 1)
ptr = aligned_alloc(alignToSizeT(align...), size);
else
ptr = malloc(size);
if (likely(ptr != nullptr))
return ptr;
/// @note no std::get_new_handler logic implemented
throw std::bad_alloc{};
}
inline ALWAYS_INLINE void * newNoExept(std::size_t size) noexcept
{
2023-01-12 14:00:26 +00:00
if (unlikely(GuardedAlloc.shouldSample()))
{
if (void * ptr = GuardedAlloc.allocate(size))
return ptr;
}
return malloc(size);
}
2022-03-11 10:04:35 +00:00
inline ALWAYS_INLINE void * newNoExept(std::size_t size, std::align_val_t align) noexcept
{
2023-01-12 14:00:26 +00:00
if (unlikely(GuardedAlloc.shouldSample()))
{
if (void * ptr = GuardedAlloc.allocate(size, alignToSizeT(align)))
return ptr;
}
2022-03-11 10:04:35 +00:00
return aligned_alloc(static_cast<size_t>(align), size);
}
inline ALWAYS_INLINE void deleteImpl(void * ptr) noexcept
{
2023-01-12 14:00:26 +00:00
if (unlikely(GuardedAlloc.pointerIsMine(ptr)))
{
GuardedAlloc.deallocate(ptr);
return;
}
free(ptr);
}
2022-01-18 06:51:13 +00:00
#if USE_JEMALLOC
2022-03-11 10:04:35 +00:00
template <std::same_as<std::align_val_t>... TAlign>
requires DB::OptionalArgument<TAlign...>
inline ALWAYS_INLINE void deleteSized(void * ptr, std::size_t size, TAlign... align) noexcept
{
if (unlikely(ptr == nullptr))
return;
2023-01-12 14:00:26 +00:00
if (unlikely(GuardedAlloc.pointerIsMine(ptr)))
{
GuardedAlloc.deallocate(ptr);
return;
}
2022-03-11 10:04:35 +00:00
if constexpr (sizeof...(TAlign) == 1)
sdallocx(ptr, size, MALLOCX_ALIGN(alignToSizeT(align...)));
else
sdallocx(ptr, size, 0);
}
#else
2022-03-11 10:04:35 +00:00
template <std::same_as<std::align_val_t>... TAlign>
requires DB::OptionalArgument<TAlign...>
inline ALWAYS_INLINE void deleteSized(void * ptr, std::size_t size [[maybe_unused]], TAlign... /* align */) noexcept
{
free(ptr);
}
#endif
#if defined(OS_LINUX)
2022-03-11 10:04:35 +00:00
# include <malloc.h>
#elif defined(OS_DARWIN)
2022-03-11 10:04:35 +00:00
# include <malloc/malloc.h>
#endif
2022-03-11 10:04:35 +00:00
template <std::same_as<std::align_val_t>... TAlign>
requires DB::OptionalArgument<TAlign...>
2022-04-13 06:19:59 +00:00
inline ALWAYS_INLINE size_t getActualAllocationSize(size_t size, TAlign... align [[maybe_unused]])
{
size_t actual_size = size;
2022-01-18 06:51:13 +00:00
#if USE_JEMALLOC
/// The nallocx() function allocates no memory, but it performs the same size computation as the mallocx() function
/// @note je_mallocx() != je_malloc(). It's expected they don't differ much in allocation logic.
if (likely(size != 0))
2022-03-11 10:04:35 +00:00
{
if constexpr (sizeof...(TAlign) == 1)
actual_size = nallocx(size, MALLOCX_ALIGN(alignToSizeT(align...)));
else
actual_size = nallocx(size, 0);
}
#endif
return actual_size;
}
2022-03-11 10:04:35 +00:00
template <std::same_as<std::align_val_t>... TAlign>
requires DB::OptionalArgument<TAlign...>
inline ALWAYS_INLINE void trackMemory(std::size_t size, TAlign... align)
{
2022-03-11 10:04:35 +00:00
std::size_t actual_size = getActualAllocationSize(size, align...);
CurrentMemoryTracker::allocNoThrow(actual_size);
}
2022-03-11 10:04:35 +00:00
template <std::same_as<std::align_val_t>... TAlign>
requires DB::OptionalArgument<TAlign...>
inline ALWAYS_INLINE void untrackMemory(void * ptr [[maybe_unused]], std::size_t size [[maybe_unused]] = 0, TAlign... align [[maybe_unused]]) noexcept
{
2023-01-12 14:00:26 +00:00
if (unlikely(GuardedAlloc.pointerIsMine(ptr)))
{
if (!size)
size = GuardedAlloc.getSize(ptr);
CurrentMemoryTracker::free(size);
return;
}
try
{
2022-01-18 06:51:13 +00:00
#if USE_JEMALLOC
2022-03-11 10:04:35 +00:00
/// @note It's also possible to use je_malloc_usable_size() here.
if (likely(ptr != nullptr))
2022-03-11 10:04:35 +00:00
{
if constexpr (sizeof...(TAlign) == 1)
CurrentMemoryTracker::free(sallocx(ptr, MALLOCX_ALIGN(alignToSizeT(align...))));
2022-03-11 10:04:35 +00:00
else
CurrentMemoryTracker::free(sallocx(ptr, 0));
2022-03-11 10:04:35 +00:00
}
#else
if (size)
CurrentMemoryTracker::free(size);
# if defined(_GNU_SOURCE)
/// It's innaccurate resource free for sanitizers. malloc_usable_size() result is greater or equal to allocated size.
else
CurrentMemoryTracker::free(malloc_usable_size(ptr));
# endif
#endif
}
catch (...)
2022-03-11 10:04:35 +00:00
{
}
}
}
2022-03-11 10:04:35 +00:00
#pragma GCC diagnostic pop