1/*
2 * Copyright 2013-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <folly/Memory.h>
18
19#include <limits>
20#include <memory>
21#include <type_traits>
22#include <utility>
23
24#include <glog/logging.h>
25
26#include <folly/ConstexprMath.h>
27#include <folly/String.h>
28#include <folly/memory/Arena.h>
29#include <folly/portability/GMock.h>
30#include <folly/portability/GTest.h>
31
32using namespace folly;
33
34static constexpr std::size_t kTooBig = folly::constexpr_max(
35 std::size_t{std::numeric_limits<uint32_t>::max()},
36 std::size_t{1} << (8 * sizeof(std::size_t) - 14));
37
38TEST(allocateBytes, simple) {
39 auto p = allocateBytes(10);
40 EXPECT_TRUE(p != nullptr);
41 deallocateBytes(p, 10);
42}
43
44TEST(aligned_malloc, examples) {
45 auto trial = [](size_t align) {
46 auto const ptr = aligned_malloc(1, align);
47 return (aligned_free(ptr), uintptr_t(ptr));
48 };
49
50 if (!kIsSanitize) { // asan allocator raises SIGABRT instead
51 EXPECT_EQ(EINVAL, (trial(2), errno)) << "too small";
52 EXPECT_EQ(EINVAL, (trial(513), errno)) << "not power of two";
53 }
54
55 EXPECT_EQ(0, trial(512) % 512);
56 EXPECT_EQ(0, trial(8192) % 8192);
57}
58
59TEST(make_unique, compatible_with_std_make_unique) {
60 // HACK: To enforce that `folly::` is imported here.
61 to_shared_ptr(std::unique_ptr<std::string>());
62
63 using namespace std;
64 make_unique<string>("hello, world");
65}
66
67TEST(to_weak_ptr, example) {
68 auto s = std::make_shared<int>(17);
69 EXPECT_EQ(1, s.use_count());
70 EXPECT_EQ(2, (to_weak_ptr(s).lock(), s.use_count())) << "lvalue";
71 EXPECT_EQ(3, (to_weak_ptr(decltype(s)(s)).lock(), s.use_count())) << "rvalue";
72}
73
74TEST(SysAllocator, equality) {
75 using Alloc = SysAllocator<float>;
76 Alloc const a, b;
77 EXPECT_TRUE(a == b);
78 EXPECT_FALSE(a != b);
79}
80
81TEST(SysAllocator, allocate_unique) {
82 using Alloc = SysAllocator<float>;
83 Alloc const alloc;
84 auto ptr = allocate_unique<float>(alloc, 3.);
85 EXPECT_EQ(3., *ptr);
86}
87
88TEST(SysAllocator, vector) {
89 using Alloc = SysAllocator<float>;
90 Alloc const alloc;
91 std::vector<float, Alloc> nums(alloc);
92 nums.push_back(3.);
93 nums.push_back(5.);
94 EXPECT_THAT(nums, testing::ElementsAreArray({3., 5.}));
95}
96
97TEST(SysAllocator, bad_alloc) {
98 using Alloc = SysAllocator<float>;
99 Alloc const alloc;
100 std::vector<float, Alloc> nums(alloc);
101 if (!kIsSanitize) {
102 EXPECT_THROW(nums.reserve(kTooBig), std::bad_alloc);
103 }
104}
105
106TEST(AlignedSysAllocator, equality_fixed) {
107 using Alloc = AlignedSysAllocator<float, FixedAlign<1024>>;
108 Alloc const a, b;
109 EXPECT_TRUE(a == b);
110 EXPECT_FALSE(a != b);
111}
112
113TEST(AlignedSysAllocator, allocate_unique_fixed) {
114 using Alloc = AlignedSysAllocator<float, FixedAlign<1024>>;
115 Alloc const alloc;
116 auto ptr = allocate_unique<float>(alloc, 3.);
117 EXPECT_EQ(3., *ptr);
118 EXPECT_EQ(0, std::uintptr_t(ptr.get()) % 1024);
119}
120
121TEST(AlignedSysAllocator, vector_fixed) {
122 using Alloc = AlignedSysAllocator<float, FixedAlign<1024>>;
123 Alloc const alloc;
124 std::vector<float, Alloc> nums(alloc);
125 nums.push_back(3.);
126 nums.push_back(5.);
127 EXPECT_THAT(nums, testing::ElementsAreArray({3., 5.}));
128 EXPECT_EQ(0, std::uintptr_t(nums.data()) % 1024);
129}
130
131TEST(AlignedSysAllocator, bad_alloc_fixed) {
132 using Alloc = AlignedSysAllocator<float, FixedAlign<1024>>;
133 Alloc const alloc;
134 std::vector<float, Alloc> nums(alloc);
135 if (!kIsSanitize) {
136 EXPECT_THROW(nums.reserve(kTooBig), std::bad_alloc);
137 }
138}
139
140TEST(AlignedSysAllocator, equality_default) {
141 using Alloc = AlignedSysAllocator<float>;
142 Alloc const a(1024), b(1024), c(512);
143 EXPECT_TRUE(a == b);
144 EXPECT_FALSE(a != b);
145 EXPECT_FALSE(a == c);
146 EXPECT_TRUE(a != c);
147}
148
149TEST(AlignedSysAllocator, allocate_unique_default) {
150 using Alloc = AlignedSysAllocator<float>;
151 Alloc const alloc(1024);
152 auto ptr = allocate_unique<float>(alloc, 3.);
153 EXPECT_EQ(3., *ptr);
154 EXPECT_EQ(0, std::uintptr_t(ptr.get()) % 1024);
155}
156
157TEST(AlignedSysAllocator, vector_default) {
158 using Alloc = AlignedSysAllocator<float>;
159 Alloc const alloc(1024);
160 std::vector<float, Alloc> nums(alloc);
161 nums.push_back(3.);
162 nums.push_back(5.);
163 EXPECT_THAT(nums, testing::ElementsAreArray({3., 5.}));
164 EXPECT_EQ(0, std::uintptr_t(nums.data()) % 1024);
165}
166
167TEST(AlignedSysAllocator, bad_alloc_default) {
168 using Alloc = AlignedSysAllocator<float>;
169 Alloc const alloc(1024);
170 std::vector<float, Alloc> nums(alloc);
171 if (!kIsSanitize) {
172 EXPECT_THROW(nums.reserve(kTooBig), std::bad_alloc);
173 }
174}
175
176TEST(AlignedSysAllocator, converting_constructor) {
177 using Alloc1 = AlignedSysAllocator<float>;
178 using Alloc2 = AlignedSysAllocator<double>;
179 Alloc1 const alloc1(1024);
180 Alloc2 const alloc2(alloc1);
181}
182
183TEST(allocate_sys_buffer, compiles) {
184 auto buf = allocate_sys_buffer(256);
185 // Freed at the end of the scope.
186}
187
188struct CountedAllocatorStats {
189 size_t deallocates = 0;
190};
191
192template <typename T>
193class CountedAllocator : public std::allocator<T> {
194 private:
195 CountedAllocatorStats* stats_;
196
197 public:
198 explicit CountedAllocator(CountedAllocatorStats& stats) noexcept
199 : stats_(&stats) {}
200 void deallocate(T* p, size_t n) {
201 std::allocator<T>::deallocate(p, n);
202 ++stats_->deallocates;
203 }
204};
205
206TEST(allocate_unique, ctor_failure) {
207 struct CtorThrows {
208 explicit CtorThrows(bool cond) {
209 if (cond) {
210 throw std::runtime_error("nope");
211 }
212 }
213 };
214 using Alloc = CountedAllocator<CtorThrows>;
215 using Deleter = allocator_delete<CountedAllocator<CtorThrows>>;
216 {
217 CountedAllocatorStats stats;
218 Alloc const alloc(stats);
219 EXPECT_EQ(0, stats.deallocates);
220 std::unique_ptr<CtorThrows, Deleter> ptr{nullptr, Deleter{alloc}};
221 ptr = allocate_unique<CtorThrows>(alloc, false);
222 EXPECT_NE(nullptr, ptr);
223 EXPECT_EQ(0, stats.deallocates);
224 ptr = nullptr;
225 EXPECT_EQ(nullptr, ptr);
226 EXPECT_EQ(1, stats.deallocates);
227 }
228 {
229 CountedAllocatorStats stats;
230 Alloc const alloc(stats);
231 EXPECT_EQ(0, stats.deallocates);
232 std::unique_ptr<CtorThrows, Deleter> ptr{nullptr, Deleter{alloc}};
233 EXPECT_THROW(
234 ptr = allocate_unique<CtorThrows>(alloc, true), std::runtime_error);
235 EXPECT_EQ(nullptr, ptr);
236 EXPECT_EQ(1, stats.deallocates);
237 }
238}
239
240namespace {
241template <typename T>
242struct TestAlloc1 : SysAllocator<T> {
243 template <typename U, typename... Args>
244 void construct(U* p, Args&&... args) {
245 ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
246 }
247};
248
249template <typename T>
250struct TestAlloc2 : TestAlloc1<T> {
251 template <typename U>
252 void destroy(U* p) {
253 p->~U();
254 }
255};
256
257template <typename T>
258struct TestAlloc3 : TestAlloc2<T> {
259 using folly_has_default_object_construct = std::true_type;
260};
261
262template <typename T>
263struct TestAlloc4 : TestAlloc3<T> {
264 using folly_has_default_object_destroy = std::true_type;
265};
266
267template <typename T>
268struct TestAlloc5 : SysAllocator<T> {
269 using folly_has_default_object_construct = std::true_type;
270 using folly_has_default_object_destroy = std::false_type;
271};
272} // namespace
273
274TEST(AllocatorObjectLifecycleTraits, compiles) {
275 using A = std::allocator<int>;
276 using S = std::string;
277
278 static_assert(
279 folly::AllocatorHasDefaultObjectConstruct<A, int, int>::value, "");
280 static_assert(folly::AllocatorHasDefaultObjectConstruct<A, S, S>::value, "");
281
282 static_assert(folly::AllocatorHasDefaultObjectDestroy<A, int>::value, "");
283 static_assert(folly::AllocatorHasDefaultObjectDestroy<A, S>::value, "");
284
285 static_assert(
286 folly::AllocatorHasDefaultObjectConstruct<
287 folly::AlignedSysAllocator<int>,
288 int,
289 int>::value,
290 "");
291 static_assert(
292 folly::AllocatorHasDefaultObjectConstruct<
293 folly::AlignedSysAllocator<int>,
294 S,
295 S>::value,
296 "");
297
298 static_assert(
299 folly::AllocatorHasDefaultObjectDestroy<
300 folly::AlignedSysAllocator<int>,
301 int>::value,
302 "");
303 static_assert(
304 folly::AllocatorHasDefaultObjectDestroy<
305 folly::AlignedSysAllocator<int>,
306 S>::value,
307 "");
308
309 static_assert(
310 !folly::AllocatorHasDefaultObjectConstruct<TestAlloc1<S>, S, S>::value,
311 "");
312 static_assert(
313 folly::AllocatorHasDefaultObjectDestroy<TestAlloc1<S>, S>::value, "");
314
315 static_assert(
316 !folly::AllocatorHasDefaultObjectConstruct<TestAlloc2<S>, S, S>::value,
317 "");
318 static_assert(
319 !folly::AllocatorHasDefaultObjectDestroy<TestAlloc2<S>, S>::value, "");
320
321 static_assert(
322 folly::AllocatorHasDefaultObjectConstruct<TestAlloc3<S>, S, S>::value,
323 "");
324 static_assert(
325 !folly::AllocatorHasDefaultObjectDestroy<TestAlloc3<S>, S>::value, "");
326
327 static_assert(
328 folly::AllocatorHasDefaultObjectConstruct<TestAlloc4<S>, S, S>::value,
329 "");
330 static_assert(
331 folly::AllocatorHasDefaultObjectDestroy<TestAlloc4<S>, S>::value, "");
332
333 static_assert(
334 folly::AllocatorHasDefaultObjectConstruct<TestAlloc5<S>, S, S>::value,
335 "");
336 static_assert(
337 !folly::AllocatorHasDefaultObjectDestroy<TestAlloc5<S>, S>::value, "");
338}
339
340template <typename T>
341struct ExpectingAlloc {
342 using value_type = T;
343
344 ExpectingAlloc(std::size_t expectedSize, std::size_t expectedCount)
345 : expectedSize_{expectedSize}, expectedCount_{expectedCount} {}
346
347 template <class U>
348 explicit ExpectingAlloc(ExpectingAlloc<U> const& other) noexcept
349 : expectedSize_{other.expectedSize_},
350 expectedCount_{other.expectedCount_} {}
351
352 T* allocate(std::size_t n) {
353 EXPECT_EQ(expectedSize_, sizeof(T));
354 EXPECT_EQ(expectedSize_, alignof(T));
355 EXPECT_EQ(expectedCount_, n);
356 return std::allocator<T>{}.allocate(n);
357 }
358
359 void deallocate(T* p, std::size_t n) {
360 std::allocator<T>{}.deallocate(p, n);
361 }
362
363 std::size_t expectedSize_;
364 std::size_t expectedCount_;
365};
366
367struct alignas(64) OverAlignedType {
368 std::array<char, 64> raw_;
369};
370
371TEST(allocateOverAligned, notActuallyOver) {
372 // allocates 6 bytes with alignment 4, should get turned into an
373 // allocation of 2 elements of something of size and alignment 4
374 ExpectingAlloc<char> a(4, 2);
375 auto p = folly::allocateOverAligned<decltype(a), 4>(a, 6);
376 EXPECT_EQ((reinterpret_cast<uintptr_t>(p) % 4), 0);
377 folly::deallocateOverAligned<decltype(a), 4>(a, p, 6);
378 EXPECT_EQ((folly::allocationBytesForOverAligned<decltype(a), 4>(6)), 8);
379}
380
381TEST(allocateOverAligned, manualOverStdAlloc) {
382 // allocates 6 bytes with alignment 64 using std::allocator, which will
383 // result in a call to aligned_malloc underneath. We free one directly
384 // to check that this is not the padding path
385 std::allocator<short> a;
386 auto p = folly::allocateOverAligned<decltype(a), 64>(a, 3);
387 auto p2 = folly::allocateOverAligned<decltype(a), 64>(a, 3);
388 EXPECT_EQ((reinterpret_cast<uintptr_t>(p) % 64), 0);
389 folly::deallocateOverAligned<decltype(a), 64>(a, p, 3);
390 aligned_free(p2);
391 EXPECT_EQ((folly::allocationBytesForOverAligned<decltype(a), 64>(3)), 6);
392}
393
394TEST(allocateOverAligned, manualOverCustomAlloc) {
395 // allocates 6 byte with alignment 64 using non-standard allocator, which
396 // will result in an allocation of 64 + alignof(max_align_t) underneath.
397 ExpectingAlloc<short> a(
398 alignof(folly::max_align_t), 64 / alignof(folly::max_align_t) + 1);
399 auto p = folly::allocateOverAligned<decltype(a), 64>(a, 3);
400 EXPECT_EQ((reinterpret_cast<uintptr_t>(p) % 64), 0);
401 folly::deallocateOverAligned<decltype(a), 64>(a, p, 3);
402 EXPECT_EQ(
403 (folly::allocationBytesForOverAligned<decltype(a), 64>(3)),
404 64 + alignof(folly::max_align_t));
405}
406
407TEST(allocateOverAligned, defaultOverCustomAlloc) {
408 ExpectingAlloc<OverAlignedType> a(
409 alignof(folly::max_align_t), 128 / alignof(folly::max_align_t));
410 auto p = folly::allocateOverAligned(a, 1);
411 EXPECT_EQ((reinterpret_cast<uintptr_t>(p) % 64), 0);
412 folly::deallocateOverAligned(a, p, 1);
413 EXPECT_EQ(folly::allocationBytesForOverAligned<decltype(a)>(1), 128);
414}
415