mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-06 16:56:45 +08:00
Start refactoring memory management, adding memory_resource
This commit is contained in:
parent
73ce564ab2
commit
a4361d2b97
@ -26,25 +26,26 @@ using uint_t = typename uint<N>::type;
|
|||||||
// constants
|
// constants
|
||||||
|
|
||||||
enum : std::uint32_t {
|
enum : std::uint32_t {
|
||||||
invalid_value = (std::numeric_limits<std::uint32_t>::max)(),
|
invalid_value = (std::numeric_limits<std::uint32_t>::max)(),
|
||||||
default_timeout = 100, // ms
|
default_timeout = 100, // ms
|
||||||
};
|
};
|
||||||
|
|
||||||
enum : std::size_t {
|
enum : std::size_t {
|
||||||
data_length = 64,
|
central_cache_default_size = 1024 * 1024, ///< 1MB
|
||||||
large_msg_limit = data_length,
|
data_length = 64,
|
||||||
large_msg_align = 1024,
|
large_msg_limit = data_length,
|
||||||
large_msg_cache = 32,
|
large_msg_align = 1024,
|
||||||
|
large_msg_cache = 32,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class relat { // multiplicity of the relationship
|
enum class relat { // multiplicity of the relationship
|
||||||
single,
|
single,
|
||||||
multi
|
multi
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class trans { // transmission
|
enum class trans { // transmission
|
||||||
unicast,
|
unicast,
|
||||||
broadcast
|
broadcast
|
||||||
};
|
};
|
||||||
|
|
||||||
// producer-consumer policy flag
|
// producer-consumer policy flag
|
||||||
@ -57,9 +58,9 @@ struct relat_trait;
|
|||||||
|
|
||||||
template <relat Rp, relat Rc, trans Ts>
|
template <relat Rp, relat Rc, trans Ts>
|
||||||
struct relat_trait<wr<Rp, Rc, Ts>> {
|
struct relat_trait<wr<Rp, Rc, Ts>> {
|
||||||
constexpr static bool is_multi_producer = (Rp == relat::multi);
|
constexpr static bool is_multi_producer = (Rp == relat::multi);
|
||||||
constexpr static bool is_multi_consumer = (Rc == relat::multi);
|
constexpr static bool is_multi_consumer = (Rc == relat::multi);
|
||||||
constexpr static bool is_broadcast = (Ts == trans::broadcast);
|
constexpr static bool is_broadcast = (Ts == trans::broadcast);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <template <typename> class Policy, typename Flag>
|
template <template <typename> class Policy, typename Flag>
|
||||||
@ -67,7 +68,7 @@ struct relat_trait<Policy<Flag>> : relat_trait<Flag> {};
|
|||||||
|
|
||||||
// the prefix tag of a channel
|
// the prefix tag of a channel
|
||||||
struct prefix {
|
struct prefix {
|
||||||
char const *str;
|
char const *str;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
66
include/libipc/mem/memory_resource.h
Normal file
66
include/libipc/mem/memory_resource.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* \file libipc/memory_resource.h
|
||||||
|
* \author mutouyun (orz@orzz.org)
|
||||||
|
* \brief Implement memory allocation strategies that can be used by pmr::allocator.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <cstddef> // std::size_t, std::max_align_t
|
||||||
|
|
||||||
|
#include "libipc/imp/export.h"
|
||||||
|
#include "libipc/def.h"
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
namespace mem {
|
||||||
|
|
||||||
|
/// \brief Helper trait for memory resource.
|
||||||
|
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct has_allocate : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct has_allocate<T,
|
||||||
|
typename std::enable_if<std::is_convertible<
|
||||||
|
decltype(std::declval<T &>().allocate(std::declval<std::size_t>(),
|
||||||
|
std::declval<std::size_t>())), void *
|
||||||
|
>::value>::type> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct has_deallocate : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct has_deallocate<T,
|
||||||
|
decltype(std::declval<T &>().deallocate(std::declval<void *>(),
|
||||||
|
std::declval<std::size_t>(),
|
||||||
|
std::declval<std::size_t>()))
|
||||||
|
> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using verify_memory_resource =
|
||||||
|
std::enable_if_t<has_allocate <T>::value &&
|
||||||
|
has_deallocate<T>::value, bool>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class LIBIPC_EXPORT new_delete_resource
|
||||||
|
* \brief A memory resource that uses the
|
||||||
|
* standard memory allocation and deallocation interface to allocate memory.
|
||||||
|
* \see https://en.cppreference.com/w/cpp/memory/new_delete_resource
|
||||||
|
*/
|
||||||
|
class LIBIPC_EXPORT new_delete_resource {
|
||||||
|
public:
|
||||||
|
/// \brief Returns a pointer to a `new_delete_resource`.
|
||||||
|
static new_delete_resource *get() noexcept;
|
||||||
|
|
||||||
|
/// \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 *allocate(std::size_t bytes, std::size_t alignment = alignof(std::max_align_t)) noexcept;
|
||||||
|
|
||||||
|
/// \brief Deallocates the storage pointed to by p.
|
||||||
|
/// \see https://en.cppreference.com/w/cpp/memory/memory_resource/deallocate
|
||||||
|
void deallocate(void *p, std::size_t bytes, std::size_t alignment = alignof(std::max_align_t)) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mem
|
||||||
|
} // namespace ipc
|
||||||
101
src/libipc/mem/memory_resource.cpp
Normal file
101
src/libipc/mem/memory_resource.cpp
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
|
||||||
|
#include <cstdlib> // std::aligned_alloc
|
||||||
|
|
||||||
|
#include "libipc/imp/detect_plat.h"
|
||||||
|
#include "libipc/imp/aligned.h"
|
||||||
|
#include "libipc/imp/system.h"
|
||||||
|
#include "libipc/imp/log.h"
|
||||||
|
#include "libipc/mem/memory_resource.h"
|
||||||
|
#include "libipc/mem/verify_args.h"
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
namespace mem {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Returns a pointer to a new_delete_resource.
|
||||||
|
*
|
||||||
|
* \return new_delete_resource*
|
||||||
|
*/
|
||||||
|
new_delete_resource *new_delete_resource::get() noexcept {
|
||||||
|
static new_delete_resource mem_res;
|
||||||
|
return &mem_res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Allocates storage with a size of at least bytes bytes, aligned to the specified alignment.
|
||||||
|
* 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::allocate(std::size_t bytes, std::size_t alignment) noexcept {
|
||||||
|
LIBIPC_LOG();
|
||||||
|
if (!verify_args(bytes, alignment)) {
|
||||||
|
log.error("invalid bytes = ", bytes, ", alignment = ", alignment);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
#if defined(LIBIPC_CPP_17)
|
||||||
|
/// \see https://en.cppreference.com/w/cpp/memory/c/aligned_alloc
|
||||||
|
/// \remark The size parameter must be an integral multiple of alignment.
|
||||||
|
return std::aligned_alloc(alignment, ipc::round_up(bytes, alignment));
|
||||||
|
#else
|
||||||
|
if (alignment <= alignof(std::max_align_t)) {
|
||||||
|
/// \see https://en.cppreference.com/w/cpp/memory/c/malloc
|
||||||
|
return std::malloc(bytes);
|
||||||
|
}
|
||||||
|
#if defined(LIBIPC_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("failed: posix_memalign(alignment = ", alignment,
|
||||||
|
", bytes = ", bytes,
|
||||||
|
"). error = ", sys::error(ret));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
#endif
|
||||||
|
#endif // defined(LIBIPC_CPP_17)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Deallocates the storage pointed to by p.
|
||||||
|
* 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::deallocate(void *p, std::size_t bytes, std::size_t alignment) noexcept {
|
||||||
|
LIBIPC_LOG();
|
||||||
|
if (p == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!verify_args(bytes, alignment)) {
|
||||||
|
log.error("invalid bytes = ", bytes, ", alignment = ", alignment);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#if defined(LIBIPC_CPP_17)
|
||||||
|
/// \see https://en.cppreference.com/w/cpp/memory/c/free
|
||||||
|
std::free(p);
|
||||||
|
#else
|
||||||
|
if (alignment <= alignof(std::max_align_t)) {
|
||||||
|
std::free(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#if defined(LIBIPC_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
|
||||||
|
#endif // defined(LIBIPC_CPP_17)
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mem
|
||||||
|
} // namespace ipc
|
||||||
22
src/libipc/mem/verify_args.h
Normal file
22
src/libipc/mem/verify_args.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
namespace mem {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Check that bytes is not 0 and that the alignment is a power of two.
|
||||||
|
*/
|
||||||
|
inline constexpr bool verify_args(std::size_t bytes, std::size_t alignment) noexcept {
|
||||||
|
if (bytes == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((alignment == 0) || (alignment & (alignment - 1)) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mem
|
||||||
|
} // namespace ipc
|
||||||
@ -19,6 +19,7 @@ include_directories(
|
|||||||
file(GLOB SRC_FILES
|
file(GLOB SRC_FILES
|
||||||
${LIBIPC_PROJECT_DIR}/test/test_*.cpp
|
${LIBIPC_PROJECT_DIR}/test/test_*.cpp
|
||||||
${LIBIPC_PROJECT_DIR}/test/imp/*.cpp
|
${LIBIPC_PROJECT_DIR}/test/imp/*.cpp
|
||||||
|
${LIBIPC_PROJECT_DIR}/test/mem/*.cpp
|
||||||
# ${LIBIPC_PROJECT_DIR}/test/profiler/*.cpp
|
# ${LIBIPC_PROJECT_DIR}/test/profiler/*.cpp
|
||||||
)
|
)
|
||||||
file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/test_*.h)
|
file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/test_*.h)
|
||||||
|
|||||||
63
test/mem/test_mem_memory_resource.cpp
Normal file
63
test/mem/test_mem_memory_resource.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#if defined(LIBIPC_CPP_17) && defined(__cpp_lib_memory_resource)
|
||||||
|
# include <memory_resource>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "libipc/mem/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).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).deallocate(p, bytes, alignment);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(memory_resource, traits) {
|
||||||
|
EXPECT_FALSE(ipc::mem::has_allocate<void>::value);
|
||||||
|
EXPECT_FALSE(ipc::mem::has_allocate<int>::value);
|
||||||
|
EXPECT_FALSE(ipc::mem::has_allocate<std::vector<int>>::value);
|
||||||
|
EXPECT_FALSE(ipc::mem::has_allocate<std::allocator<int>>::value);
|
||||||
|
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
|
||||||
|
EXPECT_TRUE (ipc::mem::has_allocate<std::ipc::mem::memory_resource>::value);
|
||||||
|
EXPECT_TRUE (ipc::mem::has_allocate<std::ipc::mem::polymorphic_allocator<int>>::value);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EXPECT_FALSE(ipc::mem::has_deallocate<void>::value);
|
||||||
|
EXPECT_FALSE(ipc::mem::has_deallocate<int>::value);
|
||||||
|
EXPECT_FALSE(ipc::mem::has_deallocate<std::vector<int>>::value);
|
||||||
|
EXPECT_FALSE(ipc::mem::has_deallocate<std::allocator<int>>::value);
|
||||||
|
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
|
||||||
|
EXPECT_TRUE (ipc::mem::has_deallocate<std::ipc::mem::memory_resource>::value);
|
||||||
|
EXPECT_FALSE(ipc::mem::has_deallocate<std::ipc::mem::polymorphic_allocator<int>>::value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(memory_resource, new_delete_resource) {
|
||||||
|
ipc::mem::new_delete_resource mem_res;
|
||||||
|
|
||||||
|
EXPECT_EQ(test_mr(mem_res, 0, 0), nullptr);
|
||||||
|
EXPECT_EQ(test_mr(mem_res, 0, 1), nullptr);
|
||||||
|
EXPECT_EQ(test_mr(mem_res, 0, 2), nullptr);
|
||||||
|
EXPECT_EQ(test_mr(mem_res, 0, 3), nullptr);
|
||||||
|
EXPECT_EQ(test_mr(mem_res, 0, 8), nullptr);
|
||||||
|
EXPECT_EQ(test_mr(mem_res, 0, 64), nullptr);
|
||||||
|
|
||||||
|
EXPECT_EQ(test_mr(mem_res, 1, 0), nullptr);
|
||||||
|
EXPECT_NE(test_mr(mem_res, 1, 1), nullptr);
|
||||||
|
EXPECT_NE(test_mr(mem_res, 1, 2), nullptr);
|
||||||
|
EXPECT_EQ(test_mr(mem_res, 1, 3), nullptr);
|
||||||
|
EXPECT_NE(test_mr(mem_res, 1, 8), nullptr);
|
||||||
|
EXPECT_NE(test_mr(mem_res, 1, 64), nullptr);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user