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 | |
32 | using namespace folly; |
33 | |
34 | static 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 | |
38 | TEST(allocateBytes, simple) { |
39 | auto p = allocateBytes(10); |
40 | EXPECT_TRUE(p != nullptr); |
41 | deallocateBytes(p, 10); |
42 | } |
43 | |
44 | TEST(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 | |
59 | TEST(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 | |
67 | TEST(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 | |
74 | TEST(SysAllocator, equality) { |
75 | using Alloc = SysAllocator<float>; |
76 | Alloc const a, b; |
77 | EXPECT_TRUE(a == b); |
78 | EXPECT_FALSE(a != b); |
79 | } |
80 | |
81 | TEST(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 | |
88 | TEST(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 | |
97 | TEST(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 | |
106 | TEST(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 | |
113 | TEST(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 | |
121 | TEST(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 | |
131 | TEST(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 | |
140 | TEST(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 | |
149 | TEST(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 | |
157 | TEST(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 | |
167 | TEST(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 | |
176 | TEST(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 | |
183 | TEST(allocate_sys_buffer, compiles) { |
184 | auto buf = allocate_sys_buffer(256); |
185 | // Freed at the end of the scope. |
186 | } |
187 | |
188 | struct CountedAllocatorStats { |
189 | size_t deallocates = 0; |
190 | }; |
191 | |
192 | template <typename T> |
193 | class 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 | |
206 | TEST(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 | |
240 | namespace { |
241 | template <typename T> |
242 | struct 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 | |
249 | template <typename T> |
250 | struct TestAlloc2 : TestAlloc1<T> { |
251 | template <typename U> |
252 | void destroy(U* p) { |
253 | p->~U(); |
254 | } |
255 | }; |
256 | |
257 | template <typename T> |
258 | struct TestAlloc3 : TestAlloc2<T> { |
259 | using folly_has_default_object_construct = std::true_type; |
260 | }; |
261 | |
262 | template <typename T> |
263 | struct TestAlloc4 : TestAlloc3<T> { |
264 | using folly_has_default_object_destroy = std::true_type; |
265 | }; |
266 | |
267 | template <typename T> |
268 | struct 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 | |
274 | TEST(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 | |
340 | template <typename T> |
341 | struct 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 | |
367 | struct alignas(64) OverAlignedType { |
368 | std::array<char, 64> raw_; |
369 | }; |
370 | |
371 | TEST(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 | |
381 | TEST(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 | |
394 | TEST(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 | |
407 | TEST(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 | |