test_allocator.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. // Copyright 2018 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #ifndef ABSL_CONTAINER_INTERNAL_TEST_ALLOCATOR_H_
  15. #define ABSL_CONTAINER_INTERNAL_TEST_ALLOCATOR_H_
  16. #include <cassert>
  17. #include <cstddef>
  18. #include <cstdint>
  19. #include <memory>
  20. #include <type_traits>
  21. #include "gtest/gtest.h"
  22. #include "absl/base/config.h"
  23. namespace absl {
  24. ABSL_NAMESPACE_BEGIN
  25. namespace container_internal {
  26. // This is a stateful allocator, but the state lives outside of the
  27. // allocator (in whatever test is using the allocator). This is odd
  28. // but helps in tests where the allocator is propagated into nested
  29. // containers - that chain of allocators uses the same state and is
  30. // thus easier to query for aggregate allocation information.
  31. template <typename T>
  32. class CountingAllocator {
  33. public:
  34. using Allocator = std::allocator<T>;
  35. using AllocatorTraits = std::allocator_traits<Allocator>;
  36. using value_type = typename AllocatorTraits::value_type;
  37. using pointer = typename AllocatorTraits::pointer;
  38. using const_pointer = typename AllocatorTraits::const_pointer;
  39. using size_type = typename AllocatorTraits::size_type;
  40. using difference_type = typename AllocatorTraits::difference_type;
  41. CountingAllocator() = default;
  42. explicit CountingAllocator(int64_t* bytes_used) : bytes_used_(bytes_used) {}
  43. CountingAllocator(int64_t* bytes_used, int64_t* instance_count)
  44. : bytes_used_(bytes_used), instance_count_(instance_count) {}
  45. template <typename U>
  46. CountingAllocator(const CountingAllocator<U>& x)
  47. : bytes_used_(x.bytes_used_), instance_count_(x.instance_count_) {}
  48. pointer allocate(
  49. size_type n,
  50. typename AllocatorTraits::const_void_pointer hint = nullptr) {
  51. Allocator allocator;
  52. pointer ptr = AllocatorTraits::allocate(allocator, n, hint);
  53. if (bytes_used_ != nullptr) {
  54. *bytes_used_ += n * sizeof(T);
  55. }
  56. return ptr;
  57. }
  58. void deallocate(pointer p, size_type n) {
  59. Allocator allocator;
  60. AllocatorTraits::deallocate(allocator, p, n);
  61. if (bytes_used_ != nullptr) {
  62. *bytes_used_ -= n * sizeof(T);
  63. }
  64. }
  65. template <typename U, typename... Args>
  66. void construct(U* p, Args&&... args) {
  67. Allocator allocator;
  68. AllocatorTraits::construct(allocator, p, std::forward<Args>(args)...);
  69. if (instance_count_ != nullptr) {
  70. *instance_count_ += 1;
  71. }
  72. }
  73. template <typename U>
  74. void destroy(U* p) {
  75. Allocator allocator;
  76. // Ignore GCC warning bug.
  77. #if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
  78. #pragma GCC diagnostic push
  79. #pragma GCC diagnostic ignored "-Wuse-after-free"
  80. #endif
  81. AllocatorTraits::destroy(allocator, p);
  82. #if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
  83. #pragma GCC diagnostic pop
  84. #endif
  85. if (instance_count_ != nullptr) {
  86. *instance_count_ -= 1;
  87. }
  88. }
  89. template <typename U>
  90. class rebind {
  91. public:
  92. using other = CountingAllocator<U>;
  93. };
  94. friend bool operator==(const CountingAllocator& a,
  95. const CountingAllocator& b) {
  96. return a.bytes_used_ == b.bytes_used_ &&
  97. a.instance_count_ == b.instance_count_;
  98. }
  99. friend bool operator!=(const CountingAllocator& a,
  100. const CountingAllocator& b) {
  101. return !(a == b);
  102. }
  103. int64_t* bytes_used_ = nullptr;
  104. int64_t* instance_count_ = nullptr;
  105. };
  106. template <typename T>
  107. struct CopyAssignPropagatingCountingAlloc : public CountingAllocator<T> {
  108. using propagate_on_container_copy_assignment = std::true_type;
  109. using Base = CountingAllocator<T>;
  110. using Base::Base;
  111. template <typename U>
  112. explicit CopyAssignPropagatingCountingAlloc(
  113. const CopyAssignPropagatingCountingAlloc<U>& other)
  114. : Base(other.bytes_used_, other.instance_count_) {}
  115. template <typename U>
  116. struct rebind {
  117. using other = CopyAssignPropagatingCountingAlloc<U>;
  118. };
  119. };
  120. template <typename T>
  121. struct MoveAssignPropagatingCountingAlloc : public CountingAllocator<T> {
  122. using propagate_on_container_move_assignment = std::true_type;
  123. using Base = CountingAllocator<T>;
  124. using Base::Base;
  125. template <typename U>
  126. explicit MoveAssignPropagatingCountingAlloc(
  127. const MoveAssignPropagatingCountingAlloc<U>& other)
  128. : Base(other.bytes_used_, other.instance_count_) {}
  129. template <typename U>
  130. struct rebind {
  131. using other = MoveAssignPropagatingCountingAlloc<U>;
  132. };
  133. };
  134. template <typename T>
  135. struct SwapPropagatingCountingAlloc : public CountingAllocator<T> {
  136. using propagate_on_container_swap = std::true_type;
  137. using Base = CountingAllocator<T>;
  138. using Base::Base;
  139. template <typename U>
  140. explicit SwapPropagatingCountingAlloc(
  141. const SwapPropagatingCountingAlloc<U>& other)
  142. : Base(other.bytes_used_, other.instance_count_) {}
  143. template <typename U>
  144. struct rebind {
  145. using other = SwapPropagatingCountingAlloc<U>;
  146. };
  147. };
  148. // Tries to allocate memory at the minimum alignment even when the default
  149. // allocator uses a higher alignment.
  150. template <typename T>
  151. struct MinimumAlignmentAlloc : std::allocator<T> {
  152. MinimumAlignmentAlloc() = default;
  153. template <typename U>
  154. explicit MinimumAlignmentAlloc(const MinimumAlignmentAlloc<U>& /*other*/) {}
  155. template <class U>
  156. struct rebind {
  157. using other = MinimumAlignmentAlloc<U>;
  158. };
  159. T* allocate(size_t n) {
  160. T* ptr = std::allocator<T>::allocate(n + 1);
  161. char* cptr = reinterpret_cast<char*>(ptr);
  162. cptr += alignof(T);
  163. return reinterpret_cast<T*>(cptr);
  164. }
  165. void deallocate(T* ptr, size_t n) {
  166. char* cptr = reinterpret_cast<char*>(ptr);
  167. cptr -= alignof(T);
  168. std::allocator<T>::deallocate(reinterpret_cast<T*>(cptr), n + 1);
  169. }
  170. };
  171. inline bool IsAssertEnabled() {
  172. // Use an assert with side-effects to figure out if they are actually enabled.
  173. bool assert_enabled = false;
  174. assert([&]() { // NOLINT
  175. assert_enabled = true;
  176. return true;
  177. }());
  178. return assert_enabled;
  179. }
  180. template <template <class Alloc> class Container>
  181. void TestCopyAssignAllocPropagation() {
  182. int64_t bytes1 = 0, instances1 = 0, bytes2 = 0, instances2 = 0;
  183. CopyAssignPropagatingCountingAlloc<int> allocator1(&bytes1, &instances1);
  184. CopyAssignPropagatingCountingAlloc<int> allocator2(&bytes2, &instances2);
  185. // Test propagating allocator_type.
  186. {
  187. Container<CopyAssignPropagatingCountingAlloc<int>> c1(allocator1);
  188. Container<CopyAssignPropagatingCountingAlloc<int>> c2(allocator2);
  189. for (int i = 0; i < 100; ++i) c1.insert(i);
  190. EXPECT_NE(c2.get_allocator(), allocator1);
  191. EXPECT_EQ(instances1, 100);
  192. EXPECT_EQ(instances2, 0);
  193. c2 = c1;
  194. EXPECT_EQ(c2.get_allocator(), allocator1);
  195. EXPECT_EQ(instances1, 200);
  196. EXPECT_EQ(instances2, 0);
  197. }
  198. // Test non-propagating allocator_type with different allocators.
  199. {
  200. Container<CountingAllocator<int>> c1(allocator1), c2(allocator2);
  201. for (int i = 0; i < 100; ++i) c1.insert(i);
  202. EXPECT_EQ(c2.get_allocator(), allocator2);
  203. EXPECT_EQ(instances1, 100);
  204. EXPECT_EQ(instances2, 0);
  205. c2 = c1;
  206. EXPECT_EQ(c2.get_allocator(), allocator2);
  207. EXPECT_EQ(instances1, 100);
  208. EXPECT_EQ(instances2, 100);
  209. }
  210. EXPECT_EQ(bytes1, 0);
  211. EXPECT_EQ(instances1, 0);
  212. EXPECT_EQ(bytes2, 0);
  213. EXPECT_EQ(instances2, 0);
  214. }
  215. template <template <class Alloc> class Container>
  216. void TestMoveAssignAllocPropagation() {
  217. int64_t bytes1 = 0, instances1 = 0, bytes2 = 0, instances2 = 0;
  218. MoveAssignPropagatingCountingAlloc<int> allocator1(&bytes1, &instances1);
  219. MoveAssignPropagatingCountingAlloc<int> allocator2(&bytes2, &instances2);
  220. // Test propagating allocator_type.
  221. {
  222. Container<MoveAssignPropagatingCountingAlloc<int>> c1(allocator1);
  223. Container<MoveAssignPropagatingCountingAlloc<int>> c2(allocator2);
  224. for (int i = 0; i < 100; ++i) c1.insert(i);
  225. EXPECT_NE(c2.get_allocator(), allocator1);
  226. EXPECT_EQ(instances1, 100);
  227. EXPECT_EQ(instances2, 0);
  228. c2 = std::move(c1);
  229. EXPECT_EQ(c2.get_allocator(), allocator1);
  230. EXPECT_EQ(instances1, 100);
  231. EXPECT_EQ(instances2, 0);
  232. }
  233. // Test non-propagating allocator_type with equal allocators.
  234. {
  235. Container<CountingAllocator<int>> c1(allocator1), c2(allocator1);
  236. for (int i = 0; i < 100; ++i) c1.insert(i);
  237. EXPECT_EQ(c2.get_allocator(), allocator1);
  238. EXPECT_EQ(instances1, 100);
  239. EXPECT_EQ(instances2, 0);
  240. c2 = std::move(c1);
  241. EXPECT_EQ(c2.get_allocator(), allocator1);
  242. EXPECT_EQ(instances1, 100);
  243. EXPECT_EQ(instances2, 0);
  244. }
  245. // Test non-propagating allocator_type with different allocators.
  246. {
  247. Container<CountingAllocator<int>> c1(allocator1), c2(allocator2);
  248. for (int i = 0; i < 100; ++i) c1.insert(i);
  249. EXPECT_NE(c2.get_allocator(), allocator1);
  250. EXPECT_EQ(instances1, 100);
  251. EXPECT_EQ(instances2, 0);
  252. c2 = std::move(c1);
  253. EXPECT_EQ(c2.get_allocator(), allocator2);
  254. EXPECT_LE(instances1, 100); // The values in c1 may or may not have been
  255. // destroyed at this point.
  256. EXPECT_EQ(instances2, 100);
  257. }
  258. EXPECT_EQ(bytes1, 0);
  259. EXPECT_EQ(instances1, 0);
  260. EXPECT_EQ(bytes2, 0);
  261. EXPECT_EQ(instances2, 0);
  262. }
  263. template <template <class Alloc> class Container>
  264. void TestSwapAllocPropagation() {
  265. int64_t bytes1 = 0, instances1 = 0, bytes2 = 0, instances2 = 0;
  266. SwapPropagatingCountingAlloc<int> allocator1(&bytes1, &instances1);
  267. SwapPropagatingCountingAlloc<int> allocator2(&bytes2, &instances2);
  268. // Test propagating allocator_type.
  269. {
  270. Container<SwapPropagatingCountingAlloc<int>> c1(allocator1), c2(allocator2);
  271. for (int i = 0; i < 100; ++i) c1.insert(i);
  272. EXPECT_NE(c2.get_allocator(), allocator1);
  273. EXPECT_EQ(instances1, 100);
  274. EXPECT_EQ(instances2, 0);
  275. c2.swap(c1);
  276. EXPECT_EQ(c2.get_allocator(), allocator1);
  277. EXPECT_EQ(instances1, 100);
  278. EXPECT_EQ(instances2, 0);
  279. }
  280. // Test non-propagating allocator_type with equal allocators.
  281. {
  282. Container<CountingAllocator<int>> c1(allocator1), c2(allocator1);
  283. for (int i = 0; i < 100; ++i) c1.insert(i);
  284. EXPECT_EQ(c2.get_allocator(), allocator1);
  285. EXPECT_EQ(instances1, 100);
  286. EXPECT_EQ(instances2, 0);
  287. c2.swap(c1);
  288. EXPECT_EQ(c2.get_allocator(), allocator1);
  289. EXPECT_EQ(instances1, 100);
  290. EXPECT_EQ(instances2, 0);
  291. }
  292. // Test non-propagating allocator_type with different allocators.
  293. {
  294. Container<CountingAllocator<int>> c1(allocator1), c2(allocator2);
  295. for (int i = 0; i < 100; ++i) c1.insert(i);
  296. EXPECT_NE(c1.get_allocator(), c2.get_allocator());
  297. if (IsAssertEnabled()) {
  298. EXPECT_DEATH_IF_SUPPORTED(c2.swap(c1), "");
  299. }
  300. }
  301. EXPECT_EQ(bytes1, 0);
  302. EXPECT_EQ(instances1, 0);
  303. EXPECT_EQ(bytes2, 0);
  304. EXPECT_EQ(instances2, 0);
  305. }
  306. template <template <class Alloc> class Container>
  307. void TestAllocPropagation() {
  308. TestCopyAssignAllocPropagation<Container>();
  309. TestMoveAssignAllocPropagation<Container>();
  310. TestSwapAllocPropagation<Container>();
  311. }
  312. } // namespace container_internal
  313. ABSL_NAMESPACE_END
  314. } // namespace absl
  315. #endif // ABSL_CONTAINER_INTERNAL_TEST_ALLOCATOR_H_