add: [pmr] new_delete_resource & ut

This commit is contained in:
mutouyun 2022-11-13 18:54:18 +08:00
parent 15d89aed5c
commit 6bd967323d
15 changed files with 320 additions and 15 deletions

View File

@ -1,7 +1,7 @@
/**
* @file libconcur/concurrent.h
* @author mutouyun (orz@orzz.org)
* @brief Define different policies for concurrent queue
* @brief Define different policies for concurrent queue.
* @date 2022-11-07
*/
#pragma once

View File

@ -1,7 +1,7 @@
/**
* @file libconcur/def.h
* @author mutouyun (orz@orzz.org)
* @brief Define the trivial configuration information for concurrency
* @brief Define the trivial configuration information for concurrency.
* @date 2022-11-07
*/
#pragma once

View File

@ -1,7 +1,7 @@
/**
* @file libimp/byte.h
* @author mutouyun (orz@orzz.org)
* @brief Define the byte type
* @brief Define the byte type.
* @date 2022-11-12
*/
#pragma once

View File

@ -1,7 +1,7 @@
/**
* @file libimp/def.h
* @author mutouyun (orz@orzz.org)
* @brief Define the trivial configuration information
* @brief Define the trivial configuration information.
* @date 2022-04-23
*/
#pragma once
@ -15,6 +15,6 @@
LIBIMP_NAMESPACE_BEG_
// constants
/// @brief Constants.
LIBIMP_NAMESPACE_END_

View File

@ -1,12 +1,12 @@
/**
* @file libimp/detect_plat.h
* @author mutouyun (orz@orzz.org)
* @brief Define platform detection related interfaces
* @brief Define platform detection related interfaces.
* @date 2022-02-27
*/
#pragma once
// OS
/// @brief OS check.
#if defined(WINCE) || defined(_WIN32_WCE)
# define LIBIMP_OS_WINCE
@ -35,7 +35,7 @@
# define LIBIMP_OS_WIN
#endif
// Compiler
/// @brief Compiler check.
#if defined(_MSC_VER)
# define LIBIMP_CC_MSVC _MSC_VER
@ -48,8 +48,8 @@
# error "This compiler is unsupported."
#endif
// Instruction set
// @see https://sourceforge.net/p/predef/wiki/Architectures/
/// @brief Instruction set.
/// @see https://sourceforge.net/p/predef/wiki/Architectures/
#if defined(_M_X64) || defined(_M_AMD64) || \
defined(__x86_64__) || defined(__x86_64) || \
@ -74,7 +74,7 @@
# define LIBIMP_INSTR_ARM
#endif
// Byte order
/// @brief Byte order.
#if defined(__BYTE_ORDER__)
# define LIBIMP_ENDIAN_BIG (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
@ -84,7 +84,7 @@
# define LIBIMP_ENDIAN_LIT (1)
#endif
// C++ version
/// @brief C++ version.
#if (__cplusplus >= 202002L) && !defined(LIBIMP_CPP_20)
# define LIBIMP_CPP_20
@ -102,7 +102,7 @@
# error "This C++ version is unsupported."
#endif
// C++ attributes
/// @brief C++ attributes.
#if defined(__has_cpp_attribute)
# if __has_cpp_attribute(fallthrough)
@ -117,6 +117,9 @@
# if __has_cpp_attribute(unlikely)
# define LIBIMP_UNLIKELY(...) (__VA_ARGS__) [[unlikely]]
# endif
# if __has_cpp_attribute(nodiscard)
# define LIBIMP_NODISCARD [[nodiscard]]
# endif
#endif
#if !defined(LIBIMP_FALLTHROUGH)
@ -160,3 +163,28 @@
#if !defined(LIBIMP_UNLIKELY)
# define LIBIMP_UNLIKELY(...) (__VA_ARGS__)
#endif
#if !defined(LIBIMP_NODISCARD)
/// @see https://stackoverflow.com/questions/4226308/msvc-equivalent-of-attribute-warn-unused-result
# if defined(LIBIMP_CC_GNUC) && (LIBIMP_CC_GNUC >= 4)
# define LIBIMP_NODISCARD __attribute__((warn_unused_result))
# elif defined(LIBIMP_CC_MSVC) && (LIBIMP_CC_MSVC >= 1700)
# define LIBIMP_NODISCARD _Check_return_
# else
# define LIBIMP_NODISCARD
# endif
#endif
/// @see https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_exceptions.html
/// https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros
/// https://stackoverflow.com/questions/6487013/programmatically-determine-whether-exceptions-are-enabled
#if defined(__cpp_exceptions) && __cpp_exceptions || \
defined(__EXCEPTIONS) || defined(_CPPUNWIND)
# define LIBIMP_TRY try
# define LIBIMP_CATCH(...) catch(__VA_ARGS__)
# define LIBIMP_THROW(...) throw __VA_ARGS__
#else
# define LIBIMP_TRY if (true)
# define LIBIMP_CATCH(...) else if (false)
# define LIBIMP_THROW(...)
#endif

View File

@ -1,7 +1,7 @@
/**
* @file libipc/def.h
* @author mutouyun (orz@orzz.org)
* @brief Define the trivial configuration information
* @brief Define the trivial configuration information.
* @date 2022-02-27
*/
#pragma once

View File

@ -0,0 +1,28 @@
/**
* @file libpmr/allocator.h
* @author mutouyun (orz@orzz.org)
* @brief A generic polymorphic memory allocator.
* @date 2022-11-13
*/
#pragma once
#include "libpmr/def.h"
#include "libpmr/memory_resource.h"
LIBPMR_NAMESPACE_BEG_
/**
* @brief An allocator which exhibits different allocation behavior
* depending upon the memory resource from which it is constructed.
*
* @remarks Unlike std::pmr::polymorphic_allocator, it does not
* rely on a specific inheritance relationship and only restricts
* the interface behavior of the incoming memory resource object to
* conform to std::pmr::memory_resource.
*
* @see https://en.cppreference.com/w/cpp/memory/memory_resource
* https://en.cppreference.com/w/cpp/memory/polymorphic_allocator
*/
LIBPMR_NAMESPACE_END_

17
include/libpmr/def.h Normal file
View File

@ -0,0 +1,17 @@
/**
* @file libpmr/def.h
* @author mutouyun (orz@orzz.org)
* @brief Define the trivial configuration information for memory resources.
* @date 2022-11-13
*/
#pragma once
#define LIBPMR_ pmr
#define LIBPMR_NAMESPACE_BEG_ namespace LIBPMR_ {
#define LIBPMR_NAMESPACE_END_ }
LIBPMR_NAMESPACE_BEG_
/// @brief Constants.
LIBPMR_NAMESPACE_END_

View File

@ -0,0 +1,34 @@
/**
* @file libpmr/memory_resource.h
* @author mutouyun (orz@orzz.org)
* @brief Implement memory allocation strategies that can be used by pmr::allocator.
* @date 2022-11-13
*/
#pragma once
#include <cstddef> // std::size_t
#include "libimp/export.h"
#include "libpmr/def.h"
LIBPMR_NAMESPACE_BEG_
/**
* @brief A memory resource that uses the
* global operator new and operator delete to allocate memory.
*
* @see https://en.cppreference.com/w/cpp/memory/new_delete_resource
*/
class LIBIMP_EXPORT new_delete_resource {
public:
/// @brief Allocates storage with a size of at least bytes bytes, aligned to the specified alignment.
/// @remark Returns nullptr if storage of the requested size and alignment cannot be obtained.
/// @see https://en.cppreference.com/w/cpp/memory/memory_resource/do_allocate
void *do_allocate(std::size_t bytes, std::size_t alignment) noexcept;
/// @brief Deallocates the storage pointed to by p.
/// @see https://en.cppreference.com/w/cpp/memory/memory_resource/deallocate
void do_deallocate(void* p, std::size_t bytes, std::size_t alignment) noexcept;
};
LIBPMR_NAMESPACE_END_

View File

@ -1,3 +1,4 @@
add_subdirectory(libimp)
add_subdirectory(libpmr)
add_subdirectory(libipc)

32
src/libpmr/CMakeLists.txt Normal file
View File

@ -0,0 +1,32 @@
project(pmr)
if(NOT MSVC)
add_compile_options(
-Wno-attributes)
endif()
aux_source_directory(${LIBIPC_PROJECT_DIR}/src/libpmr SRC_FILES)
file(GLOB HEAD_FILES
${LIBIPC_PROJECT_DIR}/include/libpmr/*.h)
add_library(${PROJECT_NAME} STATIC ${SRC_FILES} ${HEAD_FILES})
# set output directory
set_target_properties(${PROJECT_NAME}
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
target_include_directories(${PROJECT_NAME}
PUBLIC ${LIBIPC_PROJECT_DIR}/include
PRIVATE ${LIBIPC_PROJECT_DIR}/src)
target_link_libraries(${PROJECT_NAME} PUBLIC imp)
install(
TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)

View File

@ -0,0 +1,117 @@
#include <cstdlib> // std::aligned_alloc
#include <cstddef> // std::max_align_t
#include <exception>
#include "libimp/detect_plat.h"
#include "libimp/system.h"
#include "libimp/log.h"
#include "libpmr/memory_resource.h"
LIBPMR_NAMESPACE_BEG_
namespace {
/**
* @brief Check that bytes is not 0 and that the alignment is a power of two.
*/
bool verify_args(::LIBIMP_::log::gripper &log, std::size_t bytes, std::size_t alignment) noexcept {
if (bytes == 0) {
return false;
}
if ((alignment == 0) || (alignment & (alignment - 1)) != 0) {
log.error("invalid bytes = {}, alignment = {}", bytes, alignment);
return false;
}
return true;
}
} // namespace
/**
* @brief Allocates storage with a size of at least bytes bytes, aligned to the specified alignment.
* @remark Alignment shall be a power of two.
*
* @see https://en.cppreference.com/w/cpp/memory/memory_resource/do_allocate
* https://www.cppstories.com/2019/08/newnew-align/
*
* @return void * - nullptr if storage of the requested size and alignment cannot be obtained.
*/
void *new_delete_resource::do_allocate(std::size_t bytes, std::size_t alignment) noexcept {
LIBIMP_LOG_();
if (!verify_args(log, bytes, alignment)) {
return nullptr;
}
if (alignment <= alignof(std::max_align_t)) {
/// @see https://en.cppreference.com/w/cpp/memory/c/malloc
return std::malloc(bytes);
}
#if defined(LIBIMP_CPP_17)
LIBIMP_TRY {
/// @see https://en.cppreference.com/w/cpp/memory/c/aligned_alloc
return std::aligned_alloc(alignment, bytes);
} LIBIMP_CATCH(std::exception const &e) {
log.error("std::aligned_alloc(alignment = {}, bytes = {}) fails. error = {}",
alignment, bytes, e.what());
return nullptr;
} LIBIMP_CATCH(...) {
log.error("std::aligned_alloc(alignment = {}, bytes = {}) fails. error = unknown exception",
alignment, bytes);
return nullptr;
}
#elif defined(LIBIMP_OS_WIN)
/// @see https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-malloc
return ::_aligned_malloc(bytes, alignment);
#else // try posix
/// @see https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_memalign.html
void *p = nullptr;
int ret = ::posix_memalign(&p, alignment, bytes);
if (ret != 0) {
log.error("posix_memalign(alignment = {}, bytes = {}) fails. error = {}",
alignment, bytes, sys::error(ret));
return nullptr;
}
return p;
#endif
}
/**
* @brief Deallocates the storage pointed to by p.
* @remark The storage it points to must not yet have been deallocated, otherwise the behavior is undefined.
*
* @see https://en.cppreference.com/w/cpp/memory/memory_resource/do_deallocate
*
* @param p must have been returned by a prior call to new_delete_resource::do_allocate(bytes, alignment).
*/
void new_delete_resource::do_deallocate(void* p, std::size_t bytes, std::size_t alignment) noexcept {
LIBIMP_LOG_();
if (p == nullptr) {
return;
}
if (!verify_args(log, bytes, alignment)) {
return;
}
if (alignment <= alignof(std::max_align_t)) {
/// @see https://en.cppreference.com/w/cpp/memory/c/free
std::free(p);
return;
}
#if defined(LIBIMP_CPP_17)
LIBIMP_TRY {
std::free(p);
} LIBIMP_CATCH(std::exception const &e) {
log.error("std::free(p = {}) fails, alignment = {}, bytes = {}. error = {}",
p, alignment, bytes, e.what());
} LIBIMP_CATCH(...) {
log.error("std::free(p = {}) fails, alignment = {}, bytes = {}. error = unknown exception",
p, alignment, bytes);
}
#elif defined(LIBIMP_OS_WIN)
/// @see https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-free
::_aligned_free(p);
#else // try posix
::free(p);
#endif
}
LIBPMR_NAMESPACE_END_

View File

@ -15,6 +15,7 @@ include_directories(
file(GLOB SRC_FILES
${LIBIPC_PROJECT_DIR}/test/imp/*.cpp
${LIBIPC_PROJECT_DIR}/test/pmr/*.cpp
${LIBIPC_PROJECT_DIR}/test/*.cpp)
file(GLOB HEAD_FILES
@ -23,5 +24,5 @@ file(GLOB HEAD_FILES
add_executable(${PROJECT_NAME} ${SRC_FILES} ${HEAD_FILES})
link_directories(${LIBIPC_PROJECT_DIR}/3rdparty/gperftools)
target_link_libraries(${PROJECT_NAME} gtest_main imp ipc)
target_link_libraries(${PROJECT_NAME} gtest_main imp pmr ipc)
#target_link_libraries(${PROJECT_NAME} tcmalloc_minimal)

View File

@ -0,0 +1,7 @@
#include "gtest/gtest.h"
#include "libpmr/allocator.h"
TEST(allocator, construct) {
}

View File

@ -0,0 +1,40 @@
#include <utility>
#include "gtest/gtest.h"
#include "libpmr/memory_resource.h"
namespace {
template <typename T>
void *test_mr(T &&mr, std::size_t bytes, std::size_t alignment) {
auto p = std::forward<T>(mr).do_allocate(bytes, alignment);
if (alignment == 0) {
EXPECT_EQ(p, nullptr);
} else if (p != nullptr) {
EXPECT_EQ((std::size_t)p % alignment, 0);
}
std::forward<T>(mr).do_deallocate(p, bytes, alignment);
return p;
}
} // namespace
TEST(memory_resource, new_delete_resource) {
pmr::new_delete_resource m_res;
EXPECT_EQ(test_mr(m_res, 0, 0), nullptr);
EXPECT_EQ(test_mr(m_res, 0, 1), nullptr);
EXPECT_EQ(test_mr(m_res, 0, 2), nullptr);
EXPECT_EQ(test_mr(m_res, 0, 3), nullptr);
EXPECT_EQ(test_mr(m_res, 0, 8), nullptr);
EXPECT_EQ(test_mr(m_res, 0, 64), nullptr);
EXPECT_EQ(test_mr(m_res, 1, 0), nullptr);
EXPECT_NE(test_mr(m_res, 1, 1), nullptr);
EXPECT_NE(test_mr(m_res, 1, 2), nullptr);
EXPECT_EQ(test_mr(m_res, 1, 3), nullptr);
EXPECT_NE(test_mr(m_res, 1, 8), nullptr);
EXPECT_NE(test_mr(m_res, 1, 64), nullptr);
}