1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
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 #pragma once
18 
19 #include <cstddef>
20 #include <limits>
21 #include <memory>
22 #include <ostream>
23 
24 #include <folly/Function.h>
25 #include <folly/hash/Hash.h>
26 #include <folly/lang/SafeAssert.h>
27 #include <folly/portability/Asm.h>
28 
29 namespace folly {
30 namespace test {
31 
32 struct MoveOnlyTestInt {
33   int x;
34   bool destroyed{false};
35 
MoveOnlyTestIntMoveOnlyTestInt36   MoveOnlyTestInt() noexcept : x(0) {}
MoveOnlyTestIntMoveOnlyTestInt37   /* implicit */ MoveOnlyTestInt(int x0) : x(x0) {}
MoveOnlyTestIntMoveOnlyTestInt38   MoveOnlyTestInt(MoveOnlyTestInt&& rhs) noexcept : x(rhs.x) {}
39   MoveOnlyTestInt(MoveOnlyTestInt const&) = delete;
40   MoveOnlyTestInt& operator=(MoveOnlyTestInt&& rhs) noexcept {
41     FOLLY_SAFE_CHECK(!rhs.destroyed, "");
42     x = rhs.x;
43     return *this;
44   }
45   MoveOnlyTestInt& operator=(MoveOnlyTestInt const&) = delete;
46 
~MoveOnlyTestIntMoveOnlyTestInt47   ~MoveOnlyTestInt() {
48     FOLLY_SAFE_CHECK(!destroyed, "");
49     destroyed = true;
50     asm_volatile_memory(); // try to keep compiler from eliding the store
51   }
52 
53   bool operator==(MoveOnlyTestInt const& rhs) const {
54     FOLLY_SAFE_CHECK(!destroyed, "");
55     FOLLY_SAFE_CHECK(!rhs.destroyed, "");
56     return x == rhs.x && destroyed == rhs.destroyed;
57   }
58   bool operator!=(MoveOnlyTestInt const& rhs) const { return !(*this == rhs); }
59 };
60 
61 struct ThrowOnCopyTestInt {
62   int x{0};
63 
ThrowOnCopyTestIntThrowOnCopyTestInt64   ThrowOnCopyTestInt() {}
65 
ThrowOnCopyTestIntThrowOnCopyTestInt66   [[noreturn]] ThrowOnCopyTestInt(const ThrowOnCopyTestInt& other)
67       : x(other.x) {
68     throw std::exception{};
69   }
70 
71   ThrowOnCopyTestInt& operator=(const ThrowOnCopyTestInt&) {
72     throw std::exception{};
73   }
74 
75   bool operator==(const ThrowOnCopyTestInt& other) const {
76     return x == other.x;
77   }
78 
79   bool operator!=(const ThrowOnCopyTestInt& other) const {
80     return !(x == other.x);
81   }
82 };
83 
84 struct PermissiveConstructorTestInt {
85   int x;
86 
PermissiveConstructorTestIntPermissiveConstructorTestInt87   PermissiveConstructorTestInt() noexcept : x(0) {}
PermissiveConstructorTestIntPermissiveConstructorTestInt88   /* implicit */ PermissiveConstructorTestInt(int x0) : x(x0) {}
89 
90   template <typename T>
PermissiveConstructorTestIntPermissiveConstructorTestInt91   /* implicit */ PermissiveConstructorTestInt(T&& src)
92       : x(std::forward<T>(src)) {}
93 
PermissiveConstructorTestIntPermissiveConstructorTestInt94   PermissiveConstructorTestInt(PermissiveConstructorTestInt&& rhs) noexcept
95       : x(rhs.x) {}
96   PermissiveConstructorTestInt(PermissiveConstructorTestInt const&) = delete;
97   PermissiveConstructorTestInt& operator=(
98       PermissiveConstructorTestInt&& rhs) noexcept {
99     x = rhs.x;
100     return *this;
101   }
102   PermissiveConstructorTestInt& operator=(PermissiveConstructorTestInt const&) =
103       delete;
104 
105   bool operator==(PermissiveConstructorTestInt const& rhs) const {
106     return x == rhs.x;
107   }
108   bool operator!=(PermissiveConstructorTestInt const& rhs) const {
109     return !(*this == rhs);
110   }
111 };
112 
113 // Tracked is implicitly constructible across tags
114 struct Counts {
115   uint64_t copyConstruct{0};
116   uint64_t moveConstruct{0};
117   uint64_t copyConvert{0};
118   uint64_t moveConvert{0};
119   uint64_t copyAssign{0};
120   uint64_t moveAssign{0};
121   uint64_t defaultConstruct{0};
122   uint64_t destroyed{0};
123 
124   explicit Counts(
125       uint64_t copConstr = 0,
126       uint64_t movConstr = 0,
127       uint64_t copConv = 0,
128       uint64_t movConv = 0,
129       uint64_t copAssign = 0,
130       uint64_t movAssign = 0,
131       uint64_t def = 0,
132       uint64_t destr = 0)
133       : copyConstruct{copConstr},
134         moveConstruct{movConstr},
135         copyConvert{copConv},
136         moveConvert{movConv},
137         copyAssign{copAssign},
138         moveAssign{movAssign},
139         defaultConstruct{def},
140         destroyed{destr} {}
141 
liveCountCounts142   int64_t liveCount() const {
143     return copyConstruct + moveConstruct + copyConvert + moveConvert +
144         defaultConstruct - destroyed;
145   }
146 
147   // dist ignores destroyed count
distCounts148   uint64_t dist(Counts const& rhs) const {
149     auto d = [](uint64_t x, uint64_t y) { return (x - y) * (x - y); };
150     return d(copyConstruct, rhs.copyConstruct) +
151         d(moveConstruct, rhs.moveConstruct) + d(copyConvert, rhs.copyConvert) +
152         d(moveConvert, rhs.moveConvert) + d(copyAssign, rhs.copyAssign) +
153         d(moveAssign, rhs.moveAssign) +
154         d(defaultConstruct, rhs.defaultConstruct);
155   }
156 
157   bool operator==(Counts const& rhs) const {
158     return dist(rhs) == 0 && destroyed == rhs.destroyed;
159   }
160   bool operator!=(Counts const& rhs) const { return !(*this == rhs); }
161 };
162 
163 inline std::ostream& operator<<(std::ostream& xo, Counts const& counts) {
164   xo << "[";
165   std::string glue = "";
166   if (counts.copyConstruct > 0) {
167     xo << glue << counts.copyConstruct << " copy";
168     glue = ", ";
169   }
170   if (counts.moveConstruct > 0) {
171     xo << glue << counts.moveConstruct << " move";
172     glue = ", ";
173   }
174   if (counts.copyConvert > 0) {
175     xo << glue << counts.copyConvert << " copy convert";
176     glue = ", ";
177   }
178   if (counts.moveConvert > 0) {
179     xo << glue << counts.moveConvert << " move convert";
180     glue = ", ";
181   }
182   if (counts.copyAssign > 0) {
183     xo << glue << counts.copyAssign << " copy assign";
184     glue = ", ";
185   }
186   if (counts.moveAssign > 0) {
187     xo << glue << counts.moveAssign << " move assign";
188     glue = ", ";
189   }
190   if (counts.defaultConstruct > 0) {
191     xo << glue << counts.defaultConstruct << " default construct";
192     glue = ", ";
193   }
194   if (counts.destroyed > 0) {
195     xo << glue << counts.destroyed << " destroyed";
196     glue = ", ";
197   }
198   xo << "]";
199   return xo;
200 }
201 
sumCounts()202 inline Counts& sumCounts() {
203   static thread_local Counts value{};
204   return value;
205 }
206 
207 template <int Tag>
208 struct Tracked {
209   static_assert(Tag <= 5, "Need to extend Tracked<Tag> in TestUtil.cpp");
210 
countsTracked211   static Counts& counts() {
212     static thread_local Counts value{};
213     return value;
214   }
215 
216   uint64_t val_;
217 
TrackedTracked218   Tracked() : val_{0} {
219     sumCounts().defaultConstruct++;
220     counts().defaultConstruct++;
221   }
TrackedTracked222   /* implicit */ Tracked(uint64_t const& val) : val_{val} {
223     sumCounts().copyConvert++;
224     counts().copyConvert++;
225   }
TrackedTracked226   /* implicit */ Tracked(uint64_t&& val) : val_{val} {
227     sumCounts().moveConvert++;
228     counts().moveConvert++;
229   }
TrackedTracked230   Tracked(Tracked const& rhs) : val_{rhs.val_} {
231     sumCounts().copyConstruct++;
232     counts().copyConstruct++;
233   }
TrackedTracked234   Tracked(Tracked&& rhs) noexcept : val_{rhs.val_} {
235     sumCounts().moveConstruct++;
236     counts().moveConstruct++;
237   }
238   Tracked& operator=(Tracked const& rhs) {
239     val_ = rhs.val_;
240     sumCounts().copyAssign++;
241     counts().copyAssign++;
242     return *this;
243   }
244   Tracked& operator=(Tracked&& rhs) noexcept {
245     val_ = rhs.val_;
246     sumCounts().moveAssign++;
247     counts().moveAssign++;
248     return *this;
249   }
250 
251   template <int T>
TrackedTracked252   /* implicit */ Tracked(Tracked<T> const& rhs) : val_{rhs.val_} {
253     sumCounts().copyConvert++;
254     counts().copyConvert++;
255   }
256 
257   template <int T>
TrackedTracked258   /* implicit */ Tracked(Tracked<T>&& rhs) : val_{rhs.val_} {
259     sumCounts().moveConvert++;
260     counts().moveConvert++;
261   }
262 
~TrackedTracked263   ~Tracked() {
264     sumCounts().destroyed++;
265     counts().destroyed++;
266   }
267 
268   bool operator==(Tracked const& rhs) const { return val_ == rhs.val_; }
269   bool operator!=(Tracked const& rhs) const { return !(*this == rhs); }
270 };
271 
272 template <int Tag>
273 struct TransparentTrackedHash {
274   using is_transparent = void;
275 
operatorTransparentTrackedHash276   size_t operator()(Tracked<Tag> const& tracked) const {
277     return tracked.val_ ^ Tag;
278   }
operatorTransparentTrackedHash279   size_t operator()(uint64_t v) const { return v ^ Tag; }
280 };
281 
282 template <int Tag>
283 struct TransparentTrackedEqual {
284   using is_transparent = void;
285 
unwrapTransparentTrackedEqual286   uint64_t unwrap(Tracked<Tag> const& v) const { return v.val_; }
unwrapTransparentTrackedEqual287   uint64_t unwrap(uint64_t v) const { return v; }
288 
289   template <typename A, typename B>
operatorTransparentTrackedEqual290   bool operator()(A const& lhs, B const& rhs) const {
291     return unwrap(lhs) == unwrap(rhs);
292   }
293 };
294 
testAllocatedMemorySize()295 inline size_t& testAllocatedMemorySize() {
296   static thread_local size_t value{0};
297   return value;
298 }
299 
testAllocatedBlockCount()300 inline size_t& testAllocatedBlockCount() {
301   static thread_local size_t value{0};
302   return value;
303 }
304 
testAllocationCount()305 inline size_t& testAllocationCount() {
306   static thread_local size_t value{0};
307   return value;
308 }
309 
testAllocationMaxCount()310 inline size_t& testAllocationMaxCount() {
311   static thread_local size_t value{std::numeric_limits<std::size_t>::max()};
312   return value;
313 }
314 
315 inline void limitTestAllocations(std::size_t allocationsBeforeException = 0) {
316   testAllocationMaxCount() = testAllocationCount() + allocationsBeforeException;
317 }
318 
unlimitTestAllocations()319 inline void unlimitTestAllocations() {
320   testAllocationMaxCount() = std::numeric_limits<std::size_t>::max();
321 }
322 
resetTracking()323 inline void resetTracking() {
324   sumCounts() = Counts{};
325   Tracked<0>::counts() = Counts{};
326   Tracked<1>::counts() = Counts{};
327   Tracked<2>::counts() = Counts{};
328   Tracked<3>::counts() = Counts{};
329   Tracked<4>::counts() = Counts{};
330   Tracked<5>::counts() = Counts{};
331   testAllocatedMemorySize() = 0;
332   testAllocatedBlockCount() = 0;
333   testAllocationCount() = 0;
334   testAllocationMaxCount() = std::numeric_limits<std::size_t>::max();
335 }
336 
337 template <class T>
338 class SwapTrackingAlloc {
339  public:
340   using Alloc = std::allocator<T>;
341   using AllocTraits = std::allocator_traits<Alloc>;
342   using value_type = typename AllocTraits::value_type;
343 
344   using pointer = typename AllocTraits::pointer;
345   using const_pointer = typename AllocTraits::const_pointer;
346   using reference = value_type&;
347   using const_reference = value_type const&;
348   using size_type = typename AllocTraits::size_type;
349 
350   using propagate_on_container_swap = std::true_type;
351   using propagate_on_container_copy_assignment = std::true_type;
352   using propagate_on_container_move_assignment = std::true_type;
353 
SwapTrackingAlloc()354   SwapTrackingAlloc() {}
355 
356   template <class U>
SwapTrackingAlloc(SwapTrackingAlloc<U> const & other)357   /* implicit */ SwapTrackingAlloc(SwapTrackingAlloc<U> const& other) noexcept
358       : a_(other.a_), t_(other.t_) {}
359 
360   template <class U>
361   SwapTrackingAlloc& operator=(SwapTrackingAlloc<U> const& other) noexcept {
362     a_ = other.a_;
363     t_ = other.t_;
364     return *this;
365   }
366 
367   template <class U>
SwapTrackingAlloc(SwapTrackingAlloc<U> && other)368   /* implicit */ SwapTrackingAlloc(SwapTrackingAlloc<U>&& other) noexcept
369       : a_(std::move(other.a_)), t_(std::move(other.t_)) {}
370 
371   template <class U>
372   SwapTrackingAlloc& operator=(SwapTrackingAlloc<U>&& other) noexcept {
373     a_ = std::move(other.a_);
374     t_ = std::move(other.t_);
375     return *this;
376   }
377 
allocate(size_t n)378   T* allocate(size_t n) {
379     if (testAllocationCount() >= testAllocationMaxCount()) {
380       throw std::bad_alloc();
381     }
382     ++testAllocationCount();
383     testAllocatedMemorySize() += n * sizeof(T);
384     ++testAllocatedBlockCount();
385     std::size_t extra =
386         std::max<std::size_t>(1, sizeof(std::size_t) / sizeof(T));
387     T* p = a_.allocate(extra + n);
388     void* raw = static_cast<void*>(p);
389     *static_cast<std::size_t*>(raw) = n;
390     return p + extra;
391   }
deallocate(T * p,size_t n)392   void deallocate(T* p, size_t n) {
393     testAllocatedMemorySize() -= n * sizeof(T);
394     --testAllocatedBlockCount();
395     std::size_t extra =
396         std::max<std::size_t>(1, sizeof(std::size_t) / sizeof(T));
397     std::size_t check;
398     void* raw = static_cast<void*>(p - extra);
399     check = *static_cast<std::size_t*>(raw);
400     FOLLY_SAFE_CHECK(check == n, "");
401     a_.deallocate(p - extra, n + extra);
402   }
403 
404  private:
405   std::allocator<T> a_;
406   Tracked<0> t_;
407 
408   template <class U>
409   friend class SwapTrackingAlloc;
410 };
411 
412 template <class T>
swap(SwapTrackingAlloc<T> &,SwapTrackingAlloc<T> &)413 void swap(SwapTrackingAlloc<T>&, SwapTrackingAlloc<T>&) noexcept {
414   // For argument dependent lookup:
415   // This function will be called if the custom swap functions of a container
416   // is used. Otherwise, std::swap() will do 1 move construct and 2 move
417   // assigns which will get tracked by t_.
418 }
419 
420 template <class T1, class T2>
421 bool operator==(SwapTrackingAlloc<T1> const&, SwapTrackingAlloc<T2> const&) {
422   return true;
423 }
424 
425 template <class T1, class T2>
426 bool operator!=(SwapTrackingAlloc<T1> const&, SwapTrackingAlloc<T2> const&) {
427   return false;
428 }
429 
430 template <class T>
431 class GenericAlloc {
432  public:
433   using value_type = T;
434 
435   using pointer = T*;
436   using const_pointer = T const*;
437   using reference = T&;
438   using const_reference = T const&;
439   using size_type = std::size_t;
440 
441   using propagate_on_container_swap = std::true_type;
442   using propagate_on_container_copy_assignment = std::true_type;
443   using propagate_on_container_move_assignment = std::true_type;
444 
445   using AllocBytesFunc = folly::Function<void*(std::size_t)>;
446   using DeallocBytesFunc = folly::Function<void(void*, std::size_t)>;
447 
448   GenericAlloc() = delete;
449 
450   template <typename A, typename D>
GenericAlloc(A && alloc,D && dealloc)451   GenericAlloc(A&& alloc, D&& dealloc)
452       : alloc_{std::make_shared<AllocBytesFunc>(std::forward<A>(alloc))},
453         dealloc_{std::make_shared<DeallocBytesFunc>(std::forward<D>(dealloc))} {
454   }
455 
456   template <class U>
GenericAlloc(GenericAlloc<U> const & other)457   /* implicit */ GenericAlloc(GenericAlloc<U> const& other) noexcept
458       : alloc_{other.alloc_}, dealloc_{other.dealloc_} {}
459 
460   template <class U>
461   GenericAlloc& operator=(GenericAlloc<U> const& other) noexcept {
462     alloc_ = other.alloc_;
463     dealloc_ = other.dealloc_;
464     return *this;
465   }
466 
467   template <class U>
GenericAlloc(GenericAlloc<U> && other)468   /* implicit */ GenericAlloc(GenericAlloc<U>&& other) noexcept
469       : alloc_(std::move(other.alloc_)), dealloc_(std::move(other.dealloc_)) {}
470 
471   template <class U>
472   GenericAlloc& operator=(GenericAlloc<U>&& other) noexcept {
473     alloc_ = std::move(other.alloc_);
474     dealloc_ = std::move(other.dealloc_);
475     return *this;
476   }
477 
allocate(size_t n)478   T* allocate(size_t n) { return static_cast<T*>((*alloc_)(n * sizeof(T))); }
deallocate(T * p,size_t n)479   void deallocate(T* p, size_t n) {
480     (*dealloc_)(static_cast<void*>(p), n * sizeof(T));
481   }
482 
483   template <typename U>
484   bool operator==(GenericAlloc<U> const& rhs) const {
485     return alloc_ == rhs.alloc_;
486   }
487 
488   template <typename U>
489   bool operator!=(GenericAlloc<U> const& rhs) const {
490     return !(*this == rhs);
491   }
492 
493  private:
494   std::shared_ptr<AllocBytesFunc> alloc_;
495   std::shared_ptr<DeallocBytesFunc> dealloc_;
496 
497   template <class U>
498   friend class GenericAlloc;
499 };
500 
501 template <typename T>
502 class GenericEqual {
503  public:
504   using EqualFunc = folly::Function<bool(T const&, T const&)>;
505 
506   GenericEqual() = delete;
507 
508   template <typename E>
GenericEqual(E && equal)509   /* implicit */ GenericEqual(E&& equal)
510       : equal_{std::make_shared<EqualFunc>(std::forward<E>(equal))} {}
511 
operator()512   bool operator()(T const& lhs, T const& rhs) const {
513     return (*equal_)(lhs, rhs);
514   }
515 
516  private:
517   std::shared_ptr<EqualFunc> equal_;
518 };
519 
520 template <typename T>
521 class GenericHasher {
522  public:
523   using HasherFunc = folly::Function<std::size_t(T const&)>;
524 
525   GenericHasher() = delete;
526 
527   template <typename H>
GenericHasher(H && hasher)528   /* implicit */ GenericHasher(H&& hasher)
529       : hasher_{std::make_shared<HasherFunc>(std::forward<H>(hasher))} {}
530 
operator()531   std::size_t operator()(T const& val) const { return (*hasher_)(val); }
532 
533  private:
534   std::shared_ptr<HasherFunc> hasher_;
535 };
536 
537 struct HashFirst {
538   template <typename P>
operatorHashFirst539   std::size_t operator()(P const& p) const {
540     return folly::Hash{}(p.first);
541   }
542 };
543 
544 struct EqualFirst {
545   template <typename P>
operatorEqualFirst546   bool operator()(P const& lhs, P const& rhs) const {
547     return lhs.first == rhs.first;
548   }
549 };
550 
551 } // namespace test
552 } // namespace folly
553 
554 namespace std {
555 template <>
556 struct hash<folly::test::MoveOnlyTestInt> {
557   std::size_t operator()(folly::test::MoveOnlyTestInt const& val) const {
558     FOLLY_SAFE_CHECK(!val.destroyed, "");
559     return val.x;
560   }
561 };
562 
563 template <>
564 struct hash<folly::test::ThrowOnCopyTestInt> {
565   std::size_t operator()(folly::test::ThrowOnCopyTestInt const& val) const {
566     return val.x;
567   }
568 };
569 
570 template <>
571 struct hash<folly::test::PermissiveConstructorTestInt> {
572   std::size_t operator()(
573       folly::test::PermissiveConstructorTestInt const& val) const {
574     return val.x;
575   }
576 };
577 
578 template <int Tag>
579 struct hash<folly::test::Tracked<Tag>> {
580   size_t operator()(folly::test::Tracked<Tag> const& tracked) const {
581     return tracked.val_ ^ Tag;
582   }
583 };
584 } // namespace std
585