diff --git a/include/libconcur/concurrent.h b/include/libconcur/concurrent.h index d0fb83d..8d46d74 100644 --- a/include/libconcur/concurrent.h +++ b/include/libconcur/concurrent.h @@ -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 diff --git a/include/libconcur/def.h b/include/libconcur/def.h index 4faf35e..83dd1bc 100644 --- a/include/libconcur/def.h +++ b/include/libconcur/def.h @@ -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 diff --git a/include/libimp/byte.h b/include/libimp/byte.h index 89faeaa..dbf1f7c 100644 --- a/include/libimp/byte.h +++ b/include/libimp/byte.h @@ -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 diff --git a/include/libimp/def.h b/include/libimp/def.h index f95b82d..4348050 100644 --- a/include/libimp/def.h +++ b/include/libimp/def.h @@ -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_ diff --git a/include/libimp/detect_plat.h b/include/libimp/detect_plat.h index c445ed7..2e82655 100644 --- a/include/libimp/detect_plat.h +++ b/include/libimp/detect_plat.h @@ -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 \ No newline at end of file diff --git a/include/libipc/def.h b/include/libipc/def.h index f02ee8d..0ad7085 100755 --- a/include/libipc/def.h +++ b/include/libipc/def.h @@ -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 diff --git a/include/libpmr/allocator.h b/include/libpmr/allocator.h new file mode 100644 index 0000000..f323090 --- /dev/null +++ b/include/libpmr/allocator.h @@ -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_ diff --git a/include/libpmr/def.h b/include/libpmr/def.h new file mode 100644 index 0000000..adb9544 --- /dev/null +++ b/include/libpmr/def.h @@ -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_ diff --git a/include/libpmr/memory_resource.h b/include/libpmr/memory_resource.h new file mode 100644 index 0000000..328a640 --- /dev/null +++ b/include/libpmr/memory_resource.h @@ -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 // 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_ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 38f4cb3..f0c4982 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(libimp) +add_subdirectory(libpmr) add_subdirectory(libipc) diff --git a/src/libpmr/CMakeLists.txt b/src/libpmr/CMakeLists.txt new file mode 100644 index 0000000..976508a --- /dev/null +++ b/src/libpmr/CMakeLists.txt @@ -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) diff --git a/src/libpmr/memory_resource.cpp b/src/libpmr/memory_resource.cpp new file mode 100644 index 0000000..b16038d --- /dev/null +++ b/src/libpmr/memory_resource.cpp @@ -0,0 +1,117 @@ + +#include // std::aligned_alloc +#include // std::max_align_t +#include + +#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_ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3bc49f0..76cfa6a 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -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) diff --git a/test/pmr/test_pmr_allocator.cpp b/test/pmr/test_pmr_allocator.cpp new file mode 100644 index 0000000..2282cc8 --- /dev/null +++ b/test/pmr/test_pmr_allocator.cpp @@ -0,0 +1,7 @@ + +#include "gtest/gtest.h" + +#include "libpmr/allocator.h" + +TEST(allocator, construct) { +} \ No newline at end of file diff --git a/test/pmr/test_pmr_memory_resource.cpp b/test/pmr/test_pmr_memory_resource.cpp new file mode 100644 index 0000000..92a6a21 --- /dev/null +++ b/test/pmr/test_pmr_memory_resource.cpp @@ -0,0 +1,40 @@ + +#include + +#include "gtest/gtest.h" + +#include "libpmr/memory_resource.h" + +namespace { + +template +void *test_mr(T &&mr, std::size_t bytes, std::size_t alignment) { + auto p = std::forward(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(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); +} \ No newline at end of file