Home | History | Annotate | Download | only in support
      1 //===----------------------------------------------------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is dual licensed under the MIT and the University of Illinois Open
      6 // Source Licenses. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #ifndef SUPPORT_TEST_MEMORY_RESOURCE_HPP
     11 #define SUPPORT_TEST_MEMORY_RESOURCE_HPP
     12 
     13 #include <experimental/memory_resource>
     14 #include <memory>
     15 #include <type_traits>
     16 #include <cstddef>
     17 #include <cstdlib>
     18 #include <cstring>
     19 #include <cassert>
     20 #include "test_macros.h"
     21 
     22 struct AllocController;
     23     // 'AllocController' is a concrete type that instruments and controls the
     24     // behavior of of test allocators.
     25 
     26 template <class T>
     27 class CountingAllocator;
     28     // 'CountingAllocator' is an basic implementation of the 'Allocator'
     29     // requirements that use the 'AllocController' interface.
     30 
     31 template <class T>
     32 class MinAlignAllocator;
     33     // 'MinAlignAllocator' is an instrumented test type which implements the
     34     // 'Allocator' requirements. 'MinAlignAllocator' ensures that it *never*
     35     // returns a pointer to over-aligned storage. For example
     36     // 'MinAlignPointer<char>{}.allocate(...)' will never a 2-byte aligned
     37     // pointer.
     38 
     39 template <class T>
     40 class NullAllocator;
     41     // 'NullAllocator' is an instrumented test type which implements the
     42     // 'Allocator' requirements except that 'allocator' and 'deallocate' are
     43     // nops.
     44 
     45 
     46 #define DISALLOW_COPY(Type) \
     47   Type(Type const&) = delete; \
     48   Type& operator=(Type const&) = delete
     49 
     50 constexpr std::size_t MaxAlignV = alignof(std::max_align_t);
     51 
     52 struct TestException {};
     53 
     54 struct AllocController {
     55     int copy_constructed = 0;
     56     int move_constructed = 0;
     57 
     58     int alive = 0;
     59     int alloc_count = 0;
     60     int dealloc_count = 0;
     61     int is_equal_count = 0;
     62 
     63     std::size_t alive_size;
     64     std::size_t allocated_size;
     65     std::size_t deallocated_size;
     66 
     67     std::size_t last_size = 0;
     68     std::size_t last_align = 0;
     69     void * last_pointer = 0;
     70 
     71     std::size_t last_alloc_size = 0;
     72     std::size_t last_alloc_align = 0;
     73     void * last_alloc_pointer = nullptr;
     74 
     75     std::size_t last_dealloc_size = 0;
     76     std::size_t last_dealloc_align = 0;
     77     void * last_dealloc_pointer = nullptr;
     78 
     79     bool throw_on_alloc = false;
     80 
     81     AllocController() = default;
     82 
     83     void countAlloc(void* p, size_t s, size_t a) {
     84         ++alive;
     85         ++alloc_count;
     86         alive_size += s;
     87         allocated_size += s;
     88         last_pointer = last_alloc_pointer = p;
     89         last_size = last_alloc_size = s;
     90         last_align = last_alloc_align = a;
     91     }
     92 
     93     void countDealloc(void* p, size_t s, size_t a) {
     94         --alive;
     95         ++dealloc_count;
     96         alive_size -= s;
     97         deallocated_size += s;
     98         last_pointer = last_dealloc_pointer = p;
     99         last_size = last_dealloc_size = s;
    100         last_align = last_dealloc_align = a;
    101     }
    102 
    103     void reset() { std::memset(this, 0, sizeof(*this)); }
    104 
    105 public:
    106     bool checkAlloc(void* p, size_t s, size_t a) const {
    107         return p == last_alloc_pointer &&
    108                s == last_alloc_size &&
    109                a == last_alloc_align;
    110     }
    111 
    112     bool checkAlloc(void* p, size_t s) const {
    113         return p == last_alloc_pointer &&
    114                s == last_alloc_size;
    115     }
    116 
    117     bool checkAllocAtLeast(void* p, size_t s, size_t a) const {
    118         return p == last_alloc_pointer &&
    119                s <= last_alloc_size &&
    120                a <= last_alloc_align;
    121     }
    122 
    123     bool checkAllocAtLeast(void* p, size_t s) const {
    124         return p == last_alloc_pointer &&
    125                s <= last_alloc_size;
    126     }
    127 
    128     bool checkDealloc(void* p, size_t s, size_t a) const {
    129         return p == last_dealloc_pointer &&
    130                s == last_dealloc_size &&
    131                a == last_dealloc_align;
    132     }
    133 
    134     bool checkDealloc(void* p, size_t s) const {
    135         return p == last_dealloc_pointer &&
    136                s == last_dealloc_size;
    137     }
    138 
    139     bool checkDeallocMatchesAlloc() const {
    140         return last_dealloc_pointer == last_alloc_pointer &&
    141                last_dealloc_size == last_alloc_size &&
    142                last_dealloc_align == last_alloc_align;
    143     }
    144 
    145     void countIsEqual() {
    146         ++is_equal_count;
    147     }
    148 
    149     bool checkIsEqualCalledEq(int n) const {
    150         return is_equal_count == n;
    151     }
    152 private:
    153   DISALLOW_COPY(AllocController);
    154 };
    155 
    156 template <class T>
    157 class CountingAllocator
    158 {
    159 public:
    160     typedef T value_type;
    161     typedef T* pointer;
    162     CountingAllocator() = delete;
    163     explicit CountingAllocator(AllocController& PP) : P(&PP) {}
    164 
    165     CountingAllocator(CountingAllocator const& other) : P(other.P) {
    166         P->copy_constructed += 1;
    167     }
    168 
    169     CountingAllocator(CountingAllocator&& other) : P(other.P) {
    170         P->move_constructed += 1;
    171     }
    172 
    173     template <class U>
    174     CountingAllocator(CountingAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) {
    175         P->copy_constructed += 1;
    176     }
    177 
    178     template <class U>
    179     CountingAllocator(CountingAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) {
    180         P->move_constructed += 1;
    181     }
    182 
    183     T* allocate(std::size_t n)
    184     {
    185         void* ret = ::operator new(n*sizeof(T));
    186         P->countAlloc(ret, n*sizeof(T), alignof(T));
    187         return static_cast<T*>(ret);
    188     }
    189 
    190     void deallocate(T* p, std::size_t n)
    191     {
    192         void* vp = static_cast<void*>(p);
    193         P->countDealloc(vp, n*sizeof(T), alignof(T));
    194         ::operator delete(vp);
    195     }
    196 
    197     AllocController& getController() const { return *P; }
    198 
    199 private:
    200     template <class Tp> friend class CountingAllocator;
    201     AllocController *P;
    202 };
    203 
    204 template <class T, class U>
    205 inline bool operator==(CountingAllocator<T> const& x,
    206                        CountingAllocator<U> const& y) {
    207     return &x.getController() == &y.getController();
    208 }
    209 
    210 template <class T, class U>
    211 inline bool operator!=(CountingAllocator<T> const& x,
    212                        CountingAllocator<U> const& y) {
    213     return !(x == y);
    214 }
    215 
    216 template <class T>
    217 class MinAlignedAllocator
    218 {
    219 public:
    220     typedef T value_type;
    221     typedef T* pointer;
    222 
    223     MinAlignedAllocator() = delete;
    224 
    225     explicit MinAlignedAllocator(AllocController& R) : P(&R) {}
    226 
    227     MinAlignedAllocator(MinAlignedAllocator const& other) : P(other.P) {
    228         P->copy_constructed += 1;
    229     }
    230 
    231     MinAlignedAllocator(MinAlignedAllocator&& other) : P(other.P) {
    232         P->move_constructed += 1;
    233     }
    234 
    235     template <class U>
    236     MinAlignedAllocator(MinAlignedAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) {
    237         P->copy_constructed += 1;
    238     }
    239 
    240     template <class U>
    241     MinAlignedAllocator(MinAlignedAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) {
    242         P->move_constructed += 1;
    243     }
    244 
    245     T* allocate(std::size_t n) {
    246         char* aligned_ptr = (char*)::operator new(alloc_size(n*sizeof(T)));
    247         assert(is_max_aligned(aligned_ptr));
    248 
    249         char* unaligned_ptr = aligned_ptr + alignof(T);
    250         assert(is_min_aligned(unaligned_ptr));
    251 
    252         P->countAlloc(unaligned_ptr, n * sizeof(T), alignof(T));
    253 
    254         return ((T*)unaligned_ptr);
    255     }
    256 
    257     void deallocate(T* p, std::size_t n) {
    258         assert(is_min_aligned(p));
    259 
    260         char* aligned_ptr = ((char*)p) - alignof(T);
    261         assert(is_max_aligned(aligned_ptr));
    262 
    263         P->countDealloc(p, n*sizeof(T), alignof(T));
    264 
    265         return ::operator delete(static_cast<void*>(aligned_ptr));
    266     }
    267 
    268     AllocController& getController() const { return *P; }
    269 
    270 private:
    271     static const std::size_t BlockSize = alignof(std::max_align_t);
    272 
    273     static std::size_t alloc_size(std::size_t s) {
    274         std::size_t bytes = (s + BlockSize - 1) & ~(BlockSize - 1);
    275         bytes += BlockSize;
    276         assert(bytes % BlockSize == 0);
    277         return bytes / BlockSize;
    278     }
    279 
    280     static bool is_max_aligned(void* p) {
    281         return reinterpret_cast<std::size_t>(p) % BlockSize == 0;
    282     }
    283 
    284     static bool is_min_aligned(void* p) {
    285         if (alignof(T) == BlockSize) {
    286             return is_max_aligned(p);
    287         } else {
    288             return reinterpret_cast<std::size_t>(p) % BlockSize == alignof(T);
    289         }
    290     }
    291 
    292     template <class Tp> friend class MinAlignedAllocator;
    293     mutable AllocController *P;
    294 };
    295 
    296 
    297 template <class T, class U>
    298 inline bool operator==(MinAlignedAllocator<T> const& x,
    299                        MinAlignedAllocator<U> const& y) {
    300     return &x.getController() == &y.getController();
    301 }
    302 
    303 template <class T, class U>
    304 inline bool operator!=(MinAlignedAllocator<T> const& x,
    305                        MinAlignedAllocator<U> const& y) {
    306     return !(x == y);
    307 }
    308 
    309 template <class T>
    310 class NullAllocator
    311 {
    312 public:
    313     typedef T value_type;
    314     typedef T* pointer;
    315     NullAllocator() = delete;
    316     explicit NullAllocator(AllocController& PP) : P(&PP) {}
    317 
    318     NullAllocator(NullAllocator const& other) : P(other.P) {
    319         P->copy_constructed += 1;
    320     }
    321 
    322     NullAllocator(NullAllocator&& other) : P(other.P) {
    323         P->move_constructed += 1;
    324     }
    325 
    326     template <class U>
    327     NullAllocator(NullAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) {
    328         P->copy_constructed += 1;
    329     }
    330 
    331     template <class U>
    332     NullAllocator(NullAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) {
    333         P->move_constructed += 1;
    334     }
    335 
    336     T* allocate(std::size_t n)
    337     {
    338         P->countAlloc(nullptr, n*sizeof(T), alignof(T));
    339         return nullptr;
    340     }
    341 
    342     void deallocate(T* p, std::size_t n)
    343     {
    344         void* vp = static_cast<void*>(p);
    345         P->countDealloc(vp, n*sizeof(T), alignof(T));
    346     }
    347 
    348     AllocController& getController() const { return *P; }
    349 
    350 private:
    351     template <class Tp> friend class NullAllocator;
    352     AllocController *P;
    353 };
    354 
    355 template <class T, class U>
    356 inline bool operator==(NullAllocator<T> const& x,
    357                        NullAllocator<U> const& y) {
    358     return &x.getController() == &y.getController();
    359 }
    360 
    361 template <class T, class U>
    362 inline bool operator!=(NullAllocator<T> const& x,
    363                        NullAllocator<U> const& y) {
    364     return !(x == y);
    365 }
    366 
    367 
    368 
    369 template <class ProviderT, int = 0>
    370 class TestResourceImp : public std::experimental::pmr::memory_resource
    371 {
    372 public:
    373     static int resource_alive;
    374     static int resource_constructed;
    375     static int resource_destructed;
    376 
    377     static void resetStatics() {
    378         assert(resource_alive == 0);
    379         resource_alive = 0;
    380         resource_constructed = 0;
    381         resource_destructed = 0;
    382     }
    383 
    384     using memory_resource = std::experimental::pmr::memory_resource;
    385     using Provider = ProviderT;
    386 
    387     int value;
    388 
    389     explicit TestResourceImp(int val = 0) : value(val) {
    390         ++resource_alive;
    391         ++resource_constructed;
    392     }
    393 
    394     ~TestResourceImp() noexcept {
    395         --resource_alive;
    396         ++resource_destructed;
    397     }
    398 
    399     void reset() { C.reset(); P.reset(); }
    400     AllocController& getController() { return C; }
    401 
    402     bool checkAlloc(void* p, std::size_t s, std::size_t a) const
    403       { return C.checkAlloc(p, s, a); }
    404 
    405     bool checkDealloc(void* p, std::size_t s, std::size_t a) const
    406       { return C.checkDealloc(p, s, a); }
    407 
    408     bool checkIsEqualCalledEq(int n) const { return C.checkIsEqualCalledEq(n); }
    409 
    410 protected:
    411     virtual void * do_allocate(std::size_t s, std::size_t a) {
    412         if (C.throw_on_alloc) {
    413 #ifndef TEST_HAS_NO_EXCEPTIONS
    414             throw TestException{};
    415 #else
    416             assert(false);
    417 #endif
    418         }
    419         void* ret = P.allocate(s, a);
    420         C.countAlloc(ret, s, a);
    421         return ret;
    422     }
    423 
    424     virtual void do_deallocate(void * p, std::size_t s, std::size_t a) {
    425         C.countDealloc(p, s, a);
    426         P.deallocate(p, s, a);
    427     }
    428 
    429     virtual bool do_is_equal(memory_resource const & other) const noexcept {
    430         C.countIsEqual();
    431         TestResourceImp const * o = dynamic_cast<TestResourceImp const *>(&other);
    432         return o && o->value == value;
    433     }
    434 private:
    435     mutable AllocController C;
    436     mutable Provider P;
    437     DISALLOW_COPY(TestResourceImp);
    438 };
    439 
    440 template <class Provider, int N>
    441 int TestResourceImp<Provider, N>::resource_alive = 0;
    442 
    443 template <class Provider, int N>
    444 int TestResourceImp<Provider, N>::resource_constructed = 0;
    445 
    446 template <class Provider, int N>
    447 int TestResourceImp<Provider, N>::resource_destructed = 0;
    448 
    449 
    450 struct NullProvider {
    451     NullProvider() {}
    452     void* allocate(size_t, size_t) { return nullptr; }
    453     void deallocate(void*, size_t, size_t) {}
    454     void reset() {}
    455 private:
    456     DISALLOW_COPY(NullProvider);
    457 };
    458 
    459 struct NewDeleteProvider {
    460     NewDeleteProvider() {}
    461     void* allocate(size_t s, size_t) { return ::operator new(s); }
    462     void deallocate(void* p, size_t, size_t) { ::operator delete(p); }
    463     void reset() {}
    464 private:
    465     DISALLOW_COPY(NewDeleteProvider);
    466 };
    467 
    468 template <size_t Size = 4096 * 10> // 10 pages worth of memory.
    469 struct BufferProvider {
    470     char buffer[Size];
    471     void* next = &buffer;
    472     size_t space = Size;
    473 
    474     BufferProvider() {}
    475 
    476     void* allocate(size_t s, size_t a) {
    477         void* ret = std::align(s, a, next, space);
    478         if (ret == nullptr) {
    479 #ifndef TEST_HAS_NO_EXCEPTIONS
    480             throw std::bad_alloc();
    481 #else
    482             assert(false);
    483 #endif
    484         }
    485 
    486         return ret;
    487     }
    488 
    489     void deallocate(void*, size_t, size_t) {}
    490 
    491     void reset() {
    492         next = &buffer;
    493         space = Size;
    494     }
    495 private:
    496     DISALLOW_COPY(BufferProvider);
    497 };
    498 
    499 using NullResource = TestResourceImp<NullProvider, 0>;
    500 using NewDeleteResource = TestResourceImp<NewDeleteProvider, 0>;
    501 using TestResource  = TestResourceImp<BufferProvider<>, 0>;
    502 using TestResource1 = TestResourceImp<BufferProvider<>, 1>;
    503 using TestResource2 = TestResourceImp<BufferProvider<>, 2>;
    504 
    505 
    506 #endif /* SUPPORT_TEST_MEMORY_RESOURCE_HPP */
    507