Skip to content

Commit 238d12e

Browse files
committed
The memory allocator supports runtime dynamic size memory allocation
1 parent 518958d commit 238d12e

File tree

3 files changed

+87
-19
lines changed

3 files changed

+87
-19
lines changed

include/libipc/imp/uninitialized.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ void *destroy(T *p) noexcept {
5353
return p;
5454
}
5555

56+
template <>
57+
inline void *destroy<void>(void *p) noexcept {
58+
return p;
59+
}
60+
5661
template <typename T, std::size_t N>
5762
void *destroy(T (*p)[N]) noexcept {
5863
if (p == nullptr) return nullptr;

include/libipc/mem/new.h

Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <algorithm>
1010
#include <type_traits>
1111
#include <limits>
12+
#include <tuple>
1213

1314
#include "libipc/imp/aligned.h"
1415
#include "libipc/imp/uninitialized.h"
@@ -29,13 +30,13 @@ class LIBIPC_EXPORT block_collector {
2930
};
3031

3132
#if defined(LIBIPC_CPP_17)
32-
using get_block_collector_t = block_collector *(*)() noexcept;
33+
using recycle_t = void (*)(void *p, void *o, std::size_t bytes, std::size_t alignment) noexcept;
3334
#else
34-
using get_block_collector_t = block_collector *(*)();
35+
using recycle_t = void (*)(void *p, void *o, std::size_t bytes, std::size_t alignment);
3536
#endif
3637

3738
static constexpr std::size_t regular_head_size
38-
= round_up(sizeof(get_block_collector_t), alignof(std::max_align_t));
39+
= round_up(sizeof(recycle_t), alignof(std::max_align_t));
3940

4041
/// \brief Select the incremental level based on the size.
4142
constexpr inline std::size_t regular_level(std::size_t s) noexcept {
@@ -64,6 +65,11 @@ constexpr inline std::size_t regular_sizeof() noexcept {
6465
return regular_sizeof_impl(regular_head_size + sizeof(T));
6566
}
6667

68+
template <>
69+
constexpr inline std::size_t regular_sizeof<void>() noexcept {
70+
return (std::numeric_limits<std::size_t>::max)();
71+
}
72+
6773
/// \brief Use block pools to handle memory less than 64K.
6874
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
6975
class block_resource_base : public block_pool<BlockSize, BlockPoolExpansion> {
@@ -107,20 +113,21 @@ class block_pool_resource : public block_resource_base<BlockSize, BlockPoolExpan
107113
return &instance;
108114
}
109115

116+
template <typename T>
110117
void *allocate(std::size_t bytes, std::size_t alignment = alignof(std::max_align_t)) noexcept {
111118
void *p = base_t::allocate(bytes, alignment);
112-
*static_cast<get_block_collector_t *>(p) = get;
119+
*static_cast<recycle_t *>(p)
120+
= [](void *p, void *o, std::size_t bytes, std::size_t alignment) noexcept {
121+
std::ignore = destroy(static_cast<T *>(o));
122+
get()->recycle(p, bytes, alignment);
123+
};
113124
return static_cast<byte *>(p) + regular_head_size;
114125
}
115126

116127
void deallocate(void *p, std::size_t bytes, std::size_t alignment = alignof(std::max_align_t)) noexcept {
117-
p = static_cast<byte *>(p) - regular_head_size;
118-
auto g = *static_cast<get_block_collector_t *>(p);
119-
if (g == get) {
120-
base_t::deallocate(p, bytes, alignment);
121-
return;
122-
}
123-
g()->recycle(p, bytes, alignment);
128+
void *b = static_cast<byte *>(p) - regular_head_size;
129+
auto *r = static_cast<recycle_t *>(b);
130+
(*r)(b, p, bytes, alignment);
124131
}
125132
};
126133

@@ -141,13 +148,59 @@ auto *get_regular_resource() noexcept {
141148
return dynamic_cast<block_poll_resource_t *>(block_poll_resource_t::get());
142149
}
143150

151+
namespace detail_new {
152+
153+
template <typename T>
154+
struct do_allocate {
155+
template <typename R, typename... A>
156+
static T *apply(R *res, A &&... args) noexcept {
157+
LIBIPC_TRY {
158+
return construct<T>(res->template allocate<T>(sizeof(T), alignof(T)), std::forward<A>(args)...);
159+
} LIBIPC_CATCH(...) {
160+
return nullptr;
161+
}
162+
}
163+
};
164+
165+
template <>
166+
struct do_allocate<void> {
167+
template <typename R>
168+
static void *apply(R *res, std::size_t bytes) noexcept {
169+
if (bytes == 0) return nullptr;
170+
return res->template allocate<void>(bytes);
171+
}
172+
};
173+
174+
template <typename T>
175+
struct do_deallocate {
176+
template <typename R>
177+
static void apply(R *res, T *p) noexcept {
178+
#if (LIBIPC_CC_MSVC > LIBIPC_CC_MSVC_2015)
179+
res->deallocate(p, sizeof(T), alignof(T));
180+
#else
181+
// `alignof` of vs2015 requires that type must be able to be instantiated.
182+
res->deallocate(p, sizeof(T));
183+
#endif
184+
}
185+
};
186+
187+
template <>
188+
struct do_deallocate<void> {
189+
template <typename R>
190+
static void apply(R *res, void *p) noexcept {
191+
res->deallocate(p, 0);
192+
}
193+
};
194+
195+
} // namespace detail_new
196+
144197
/// \brief Creates an object based on the specified type and parameters with block pool resource.
145198
/// \note This function is thread-safe.
146199
template <typename T, typename... A>
147200
T *$new(A &&... args) noexcept {
148201
auto *res = get_regular_resource<T>();
149202
if (res == nullptr) return nullptr;
150-
return construct<T>(res->allocate(sizeof(T), alignof(T)), std::forward<A>(args)...);
203+
return detail_new::do_allocate<T>::apply(res, std::forward<A>(args)...);
151204
}
152205

153206
/// \brief Destroys object previously allocated by the `$new` and releases obtained memory area.
@@ -156,15 +209,9 @@ T *$new(A &&... args) noexcept {
156209
template <typename T>
157210
void $delete(T *p) noexcept {
158211
if (p == nullptr) return;
159-
destroy(p);
160212
auto *res = get_regular_resource<T>();
161213
if (res == nullptr) return;
162-
#if (LIBIPC_CC_MSVC > LIBIPC_CC_MSVC_2015)
163-
res->deallocate(p, sizeof(T), alignof(T));
164-
#else
165-
// `alignof` of vs2015 requires that type must be able to be instantiated.
166-
res->deallocate(p, sizeof(T));
167-
#endif
214+
detail_new::do_deallocate<T>::apply(res, p);
168215
}
169216

170217
/// \brief The destruction policy used by std::unique_ptr.

test/mem/test_mem_new.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,22 @@ TEST(new, delete_null) {
139139
SUCCEED();
140140
}
141141

142+
TEST(new, malloc) {
143+
void *p = ipc::mem::$new<void>(0);
144+
ASSERT_EQ(p, nullptr);
145+
ipc::mem::$delete(p);
146+
p = ipc::mem::$new<void>(1024);
147+
ASSERT_NE(p, nullptr);
148+
ipc::mem::$delete(p);
149+
150+
p = ipc::mem::$new<Derived>(-1);
151+
ASSERT_NE(p, nullptr);
152+
ASSERT_EQ(((Derived *)p)->get(), -1);
153+
ASSERT_EQ(construct_count__, -1);
154+
ipc::mem::$delete(p);
155+
ASSERT_EQ(construct_count__, 0);
156+
}
157+
142158
TEST(new, multi_thread) {
143159
std::array<std::thread, 16> threads;
144160
for (auto &t : threads) {

0 commit comments

Comments
 (0)