monotonic_buffer_resource_test.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // (C) Copyright Ion Gaztanaga 2015-2015. Distributed under the Boost
  4. // Software License, Version 1.0. (See accompanying file
  5. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. // See http://www.boost.org/libs/container for documentation.
  8. //
  9. //////////////////////////////////////////////////////////////////////////////
  10. #include <boost/container/pmr/monotonic_buffer_resource.hpp>
  11. #include <boost/container/pmr/global_resource.hpp>
  12. #include <boost/core/lightweight_test.hpp>
  13. #include "derived_from_memory_resource.hpp"
  14. #include "memory_resource_logger.hpp"
  15. using namespace boost::container::pmr;
  16. static const std::size_t AllocCount = 32u;
  17. namespace test_block_chain{
  18. //explicit block_slist(memory_resource &upstream_rsrc)
  19. void test_constructor()
  20. {
  21. memory_resource_logger mrl;
  22. block_slist bc(mrl);
  23. //Resource stored
  24. BOOST_TEST(&bc.upstream_resource() == &mrl);
  25. //No allocation performed
  26. BOOST_TEST(mrl.m_info.size() == 0u);
  27. }
  28. //void *allocate(std::size_t size)
  29. void test_allocate()
  30. {
  31. memory_resource_logger mrl;
  32. block_slist bc(mrl);
  33. for(unsigned i = 0; i != unsigned(AllocCount); ++i){
  34. //Allocate and trace data
  35. const std::size_t alloc = i+1;
  36. char *const addr = (char*)bc.allocate(alloc);
  37. //Should have allocated a new entry
  38. BOOST_TEST(mrl.m_info.size() == (i+1));
  39. //Requested size must be bigger to include metadata
  40. BOOST_TEST(mrl.m_info[i].bytes > alloc);
  41. BOOST_TEST(mrl.m_info[i].alignment == memory_resource::max_align);
  42. //Returned address should be between the allocated buffer
  43. BOOST_TEST(mrl.m_info[i].address < addr);
  44. BOOST_TEST(addr < (mrl.m_info[i].address + mrl.m_info[i].bytes));
  45. //Allocate size should include all requested size
  46. BOOST_TEST((addr + alloc) <= (mrl.m_info[i].address + mrl.m_info[i].bytes));
  47. //Allocation must be max-aligned
  48. BOOST_TEST((std::size_t(addr) % memory_resource::max_align) == 0);
  49. }
  50. }
  51. //void release() BOOST_NOEXCEPT
  52. void test_release()
  53. {
  54. memory_resource_logger mrl;
  55. block_slist bc(mrl);
  56. //Allocate and trace data
  57. char *bufs[AllocCount];
  58. for(unsigned i = 0; i != unsigned(AllocCount); ++i){
  59. bufs[i] = (char*)bc.allocate(i+1);
  60. }
  61. (void)bufs;
  62. //Should have allocated a new entry
  63. BOOST_TEST(mrl.m_info.size() == AllocCount);
  64. //Now release and check all allocations match deallocations
  65. bc.release();
  66. BOOST_TEST(mrl.m_mismatches == 0);
  67. BOOST_TEST(mrl.m_info.size() == 0u);
  68. }
  69. //memory_resource* upstream_resource()
  70. void test_memory_resource()
  71. {
  72. derived_from_memory_resource d;
  73. block_slist bc(d);
  74. //Resource stored
  75. BOOST_TEST(&bc.upstream_resource() == &d);
  76. }
  77. //~block_slist() { this->release(); }
  78. void test_destructor()
  79. {
  80. memory_resource_logger mrl;
  81. {
  82. block_slist bc(mrl);
  83. //Allocate and trace data
  84. char *bufs[AllocCount];
  85. for(unsigned i = 0; i != unsigned(AllocCount); ++i){
  86. bufs[i] = (char*)bc.allocate(i+1);
  87. }
  88. (void)bufs;
  89. //Should have allocated a new entry
  90. BOOST_TEST(mrl.m_info.size() == AllocCount);
  91. //Destructor should release all memory
  92. }
  93. BOOST_TEST(mrl.m_mismatches == 0);
  94. BOOST_TEST(mrl.m_info.size() == 0u);
  95. }
  96. } //namespace test_block_chain {
  97. void test_resource_constructor()
  98. {
  99. //First constructor, null resource
  100. {
  101. memory_resource_logger mrl;
  102. BOOST_TEST(mrl.m_info.size() == 0u);
  103. set_default_resource(&mrl);
  104. monotonic_buffer_resource m;
  105. //test postconditions
  106. BOOST_TEST(m.upstream_resource() == get_default_resource());
  107. //test it does not allocate any memory
  108. BOOST_TEST(mrl.m_info.size() == 0u);
  109. set_default_resource(0);
  110. }
  111. //First constructor, non-null resource
  112. {
  113. derived_from_memory_resource dmr;
  114. dmr.reset();
  115. monotonic_buffer_resource m(&dmr);
  116. //test postconditions
  117. BOOST_TEST(m.upstream_resource() == &dmr);
  118. BOOST_TEST(m.next_buffer_size() == monotonic_buffer_resource::initial_next_buffer_size);
  119. BOOST_TEST(m.current_buffer() == 0);
  120. //test it does not allocate any memory
  121. BOOST_TEST(dmr.do_allocate_called == false);
  122. }
  123. }
  124. void test_initial_size_constructor()
  125. {
  126. //Second constructor, null resource
  127. const std::size_t initial_size = monotonic_buffer_resource::initial_next_buffer_size*2;
  128. {
  129. memory_resource_logger mrl;
  130. BOOST_TEST(mrl.m_info.size() == 0u);
  131. set_default_resource(&mrl);
  132. monotonic_buffer_resource m(initial_size);
  133. //test postconditions
  134. BOOST_TEST(m.upstream_resource() == get_default_resource());
  135. BOOST_TEST(m.next_buffer_size() >= initial_size);
  136. BOOST_TEST(m.current_buffer() == 0);
  137. //test it does not allocate any memory
  138. BOOST_TEST(mrl.m_info.size() == 0u);
  139. set_default_resource(0);
  140. }
  141. //Second constructor, non-null resource
  142. {
  143. derived_from_memory_resource dmr;
  144. dmr.reset();
  145. monotonic_buffer_resource m(initial_size, &dmr);
  146. //test postconditions
  147. BOOST_TEST(m.upstream_resource() == &dmr);
  148. BOOST_TEST(m.next_buffer_size() >= initial_size);
  149. BOOST_TEST(m.current_buffer() == 0);
  150. //test it does not allocate any memory
  151. BOOST_TEST(dmr.do_allocate_called == false);
  152. }
  153. }
  154. void test_buffer_constructor()
  155. {
  156. const std::size_t BufSz = monotonic_buffer_resource::initial_next_buffer_size*2;
  157. unsigned char buf[BufSz];
  158. //Third constructor, null resource
  159. {
  160. memory_resource_logger mrl;
  161. BOOST_TEST(mrl.m_info.size() == 0u);
  162. set_default_resource(&mrl);
  163. monotonic_buffer_resource m(buf, BufSz);
  164. //test postconditions
  165. BOOST_TEST(m.upstream_resource() == get_default_resource());
  166. BOOST_TEST(m.next_buffer_size() >= BufSz*2);
  167. BOOST_TEST(m.current_buffer() == buf);
  168. //test it does not allocate any memory
  169. BOOST_TEST(mrl.m_info.size() == 0u);
  170. set_default_resource(0);
  171. }
  172. //Third constructor, non-null resource
  173. {
  174. derived_from_memory_resource dmr;
  175. dmr.reset();
  176. monotonic_buffer_resource m(buf, sizeof(buf), &dmr);
  177. //test postconditions
  178. BOOST_TEST(m.upstream_resource() == &dmr);
  179. BOOST_TEST(m.next_buffer_size() >= sizeof(buf)*2);
  180. BOOST_TEST(m.current_buffer() == buf);
  181. //test it does not allocate any memory
  182. BOOST_TEST(dmr.do_allocate_called == false);
  183. }
  184. //Check for empty buffers
  185. {
  186. monotonic_buffer_resource m(buf, 0);
  187. BOOST_TEST(m.upstream_resource() == get_default_resource());
  188. BOOST_TEST(m.next_buffer_size() > 1);
  189. BOOST_TEST(m.current_buffer() == buf);
  190. }
  191. }
  192. struct derived_from_monotonic_buffer_resource
  193. : public monotonic_buffer_resource
  194. {
  195. explicit derived_from_monotonic_buffer_resource(memory_resource *p)
  196. : monotonic_buffer_resource(p)
  197. {}
  198. explicit derived_from_monotonic_buffer_resource(std::size_t initial_size, memory_resource* upstream)
  199. : monotonic_buffer_resource(initial_size, upstream)
  200. {}
  201. explicit derived_from_monotonic_buffer_resource(void* buffer, std::size_t buffer_size, memory_resource* upstream)
  202. : monotonic_buffer_resource(buffer, buffer_size, upstream)
  203. {}
  204. using monotonic_buffer_resource::do_allocate;
  205. using monotonic_buffer_resource::do_deallocate;
  206. using monotonic_buffer_resource::do_is_equal;
  207. };
  208. void test_upstream_resource()
  209. {
  210. //Allocate buffer first to avoid stack-use-after-scope in monotonic_buffer_resource's destructor
  211. const std::size_t BufSz = monotonic_buffer_resource::initial_next_buffer_size;
  212. boost::move_detail::aligned_storage<BufSz+block_slist::header_size>::type buf;
  213. //Test stores the resource and uses it to allocate memory
  214. derived_from_memory_resource dmr;
  215. dmr.reset();
  216. derived_from_monotonic_buffer_resource dmbr(&dmr);
  217. //Resource must be stored and initial values given (no current buffer)
  218. BOOST_TEST(dmbr.upstream_resource() == &dmr);
  219. BOOST_TEST(dmbr.next_buffer_size() == monotonic_buffer_resource::initial_next_buffer_size);
  220. BOOST_TEST(dmbr.current_buffer() == 0);
  221. //Test it does not allocate any memory
  222. BOOST_TEST(dmr.do_allocate_called == false);
  223. //Now stub buffer storage it as the return buffer
  224. //for "derived_from_memory_resource":
  225. dmr.do_allocate_return = &buf;
  226. //Test that allocation uses the upstream_resource()
  227. void *addr = dmbr.do_allocate(1u, 1u);
  228. //Test returns stubbed memory with the internal initial size plus metadata size
  229. BOOST_TEST(addr > (char*)&buf);
  230. BOOST_TEST(addr < (char*)(&buf+1));
  231. BOOST_TEST(dmr.do_allocate_called == true);
  232. BOOST_TEST(dmr.do_allocate_bytes > BufSz);
  233. //Alignment for the resource must be max_align
  234. BOOST_TEST(dmr.do_allocate_alignment == memory_resource::max_align);
  235. }
  236. void test_do_allocate()
  237. {
  238. memory_resource_logger mrl;
  239. {
  240. std::size_t remaining_storage = 0u;
  241. derived_from_monotonic_buffer_resource dmbr(&mrl);
  242. //First test, no buffer
  243. {
  244. dmbr.do_allocate(1, 1);
  245. //It should allocate initial size
  246. BOOST_TEST(mrl.m_info.size() == 1u);
  247. //... which requests the initial size plus the header size to the allcoator
  248. BOOST_TEST(mrl.m_info[0].bytes == monotonic_buffer_resource::initial_next_buffer_size+block_slist::header_size);
  249. std::size_t remaining = dmbr.remaining_storage(1u);
  250. //Remaining storage should be one less than initial, as we requested 1 byte with minimal alignment
  251. BOOST_TEST(remaining == monotonic_buffer_resource::initial_next_buffer_size-1u);
  252. remaining_storage = remaining;
  253. }
  254. //Now ask for more internal storage with misaligned current buffer
  255. {
  256. //Test wasted space
  257. std::size_t wasted_due_to_alignment;
  258. dmbr.remaining_storage(4u, wasted_due_to_alignment);
  259. BOOST_TEST(wasted_due_to_alignment == 3u);
  260. dmbr.do_allocate(4, 4);
  261. //It should not have allocated
  262. BOOST_TEST(mrl.m_info.size() == 1u);
  263. std::size_t remaining = dmbr.remaining_storage(1u);
  264. //We wasted some bytes due to alignment plus 4 bytes of real storage
  265. BOOST_TEST(remaining == remaining_storage - 4 - wasted_due_to_alignment);
  266. remaining_storage = remaining;
  267. }
  268. //Now request the same alignment to test no storage is wasted
  269. {
  270. std::size_t wasted_due_to_alignment;
  271. std::size_t remaining = dmbr.remaining_storage(1u, wasted_due_to_alignment);
  272. BOOST_TEST(mrl.m_info.size() == 1u);
  273. dmbr.do_allocate(4, 4);
  274. //It should not have allocated
  275. BOOST_TEST(mrl.m_info.size() == 1u);
  276. remaining = dmbr.remaining_storage(1u);
  277. //We wasted no bytes due to alignment plus 4 bytes of real storage
  278. BOOST_TEST(remaining == remaining_storage - 4u);
  279. remaining_storage = remaining;
  280. }
  281. //Now exhaust the remaining storage with 2 byte alignment (the last allocation
  282. //was 4 bytes with 4 byte alignment) so it should be already 2-byte aligned.
  283. {
  284. dmbr.do_allocate(remaining_storage, 2);
  285. std::size_t wasted_due_to_alignment;
  286. std::size_t remaining = dmbr.remaining_storage(1u, wasted_due_to_alignment);
  287. BOOST_TEST(wasted_due_to_alignment == 0u);
  288. BOOST_TEST(remaining == 0u);
  289. //It should not have allocated
  290. BOOST_TEST(mrl.m_info.size() == 1u);
  291. remaining_storage = 0u;
  292. }
  293. //The next allocation should trigger the upstream resource, even with a 1 byte
  294. //allocation.
  295. {
  296. dmbr.do_allocate(1u, 1u);
  297. BOOST_TEST(mrl.m_info.size() == 2u);
  298. //The next allocation should be geometrically bigger.
  299. BOOST_TEST(mrl.m_info[1].bytes == 2*monotonic_buffer_resource::initial_next_buffer_size+block_slist::header_size);
  300. std::size_t wasted_due_to_alignment;
  301. //For a 2 byte alignment one byte will be wasted from the previous 1 byte allocation
  302. std::size_t remaining = dmbr.remaining_storage(2u, wasted_due_to_alignment);
  303. BOOST_TEST(wasted_due_to_alignment == 1u);
  304. BOOST_TEST(remaining == (mrl.m_info[1].bytes - 1u - wasted_due_to_alignment - block_slist::header_size));
  305. //It should not have allocated
  306. remaining_storage = dmbr.remaining_storage(1u);
  307. }
  308. //Now try a bigger than next allocation and see if next_buffer_size is doubled.
  309. {
  310. std::size_t next_alloc = 5*monotonic_buffer_resource::initial_next_buffer_size;
  311. dmbr.do_allocate(next_alloc, 1u);
  312. BOOST_TEST(mrl.m_info.size() == 3u);
  313. //The next allocation should be geometrically bigger.
  314. BOOST_TEST(mrl.m_info[2].bytes == 8*monotonic_buffer_resource::initial_next_buffer_size+block_slist::header_size);
  315. remaining_storage = dmbr.remaining_storage(1u);
  316. }
  317. }
  318. //derived_from_monotonic_buffer_resource dmbr(&mrl) is destroyed
  319. BOOST_TEST(mrl.m_mismatches == 0u);
  320. BOOST_TEST(mrl.m_info.size() == 0u);
  321. //Now use a local buffer
  322. {
  323. boost::move_detail::aligned_storage
  324. <monotonic_buffer_resource::initial_next_buffer_size>::type buf;
  325. //Supply an external buffer
  326. derived_from_monotonic_buffer_resource dmbr(&buf, sizeof(buf), &mrl);
  327. BOOST_TEST(dmbr.remaining_storage(1u) == sizeof(buf));
  328. //Allocate all remaining storage
  329. dmbr.do_allocate(dmbr.remaining_storage(1u), 1u);
  330. //No new allocation should have occurred
  331. BOOST_TEST(mrl.m_info.size() == 0u);
  332. BOOST_TEST(dmbr.remaining_storage(1u) == 0u);
  333. }
  334. BOOST_TEST(mrl.m_mismatches == 0u);
  335. BOOST_TEST(mrl.m_info.size() == 0u);
  336. }
  337. void test_do_deallocate()
  338. {
  339. memory_resource_logger mrl;
  340. const std::size_t initial_size = 1u;
  341. {
  342. derived_from_monotonic_buffer_resource dmbr(initial_size, &mrl);
  343. //First test, no buffer
  344. const unsigned iterations = 8;
  345. char *bufs[iterations];
  346. std::size_t sizes[iterations];
  347. //Test each iteration allocates memory
  348. for(unsigned i = 0; i != iterations; ++i)
  349. {
  350. sizes[i] = dmbr.remaining_storage()+1;
  351. bufs[i] = (char*)dmbr.do_allocate(sizes[i], 1);
  352. BOOST_TEST(mrl.m_info.size() == (i+1));
  353. }
  354. std::size_t remaining = dmbr.remaining_storage();
  355. //Test do_deallocate does not release any storage
  356. for(unsigned i = 0; i != iterations; ++i)
  357. {
  358. dmbr.do_deallocate(bufs[i], sizes[i], 1u);
  359. BOOST_TEST(mrl.m_info.size() == iterations);
  360. BOOST_TEST(remaining == dmbr.remaining_storage());
  361. BOOST_TEST(mrl.m_mismatches == 0u);
  362. }
  363. }
  364. }
  365. void test_do_is_equal()
  366. {
  367. //! <b>Returns</b>:
  368. //! `this == dynamic_cast<const monotonic_buffer_resource*>(&other)`.
  369. memory_resource_logger mrl;
  370. derived_from_monotonic_buffer_resource dmbr(&mrl);
  371. derived_from_monotonic_buffer_resource dmbr2(&mrl);
  372. BOOST_TEST(true == dmbr.do_is_equal(dmbr));
  373. BOOST_TEST(false == dmbr.do_is_equal(dmbr2));
  374. //A different type should be always different
  375. derived_from_memory_resource dmr;
  376. BOOST_TEST(false == dmbr.do_is_equal(dmr));
  377. }
  378. void test_release()
  379. {
  380. {
  381. memory_resource_logger mrl;
  382. const std::size_t initial_size = 1u;
  383. derived_from_monotonic_buffer_resource dmbr(initial_size, &mrl);
  384. //First test, no buffer
  385. const unsigned iterations = 8;
  386. //Test each iteration allocates memory
  387. for(unsigned i = 0; i != iterations; ++i)
  388. {
  389. dmbr.do_allocate(dmbr.remaining_storage()+1, 1);
  390. BOOST_TEST(mrl.m_info.size() == (i+1));
  391. }
  392. //Release and check memory was released
  393. dmbr.release();
  394. BOOST_TEST(mrl.m_mismatches == 0u);
  395. BOOST_TEST(mrl.m_info.size() == 0u);
  396. }
  397. //Now use a local buffer
  398. {
  399. boost::move_detail::aligned_storage
  400. <monotonic_buffer_resource::initial_next_buffer_size>::type buf;
  401. //Supply an external buffer
  402. monotonic_buffer_resource monr(&buf, sizeof(buf));
  403. memory_resource &mr = monr;
  404. BOOST_TEST(monr.remaining_storage(1u) == sizeof(buf));
  405. //Allocate all remaining storage
  406. mr.allocate(monr.remaining_storage(1u), 1u);
  407. BOOST_TEST(monr.current_buffer() == ((char*)&buf + sizeof(buf)));
  408. //No new allocation should have occurred
  409. BOOST_TEST(monr.remaining_storage(1u) == 0u);
  410. //Release and check memory was released and the original buffer is back
  411. monr.release();
  412. BOOST_TEST(monr.remaining_storage(1u) == sizeof(buf));
  413. BOOST_TEST(monr.current_buffer() == &buf);
  414. }
  415. }
  416. void test_destructor()
  417. {
  418. memory_resource_logger mrl;
  419. const std::size_t initial_size = 1u;
  420. {
  421. derived_from_monotonic_buffer_resource dmbr(initial_size, &mrl);
  422. //First test, no buffer
  423. const unsigned iterations = 8;
  424. //Test each iteration allocates memory
  425. for(unsigned i = 0; i != iterations; ++i)
  426. {
  427. dmbr.do_allocate(dmbr.remaining_storage()+1, 1);
  428. BOOST_TEST(mrl.m_info.size() == (i+1));
  429. }
  430. } //dmbr is destroyed, memory should be released
  431. BOOST_TEST(mrl.m_mismatches == 0u);
  432. BOOST_TEST(mrl.m_info.size() == 0u);
  433. }
  434. int main()
  435. {
  436. test_block_chain::test_constructor();
  437. test_block_chain::test_allocate();
  438. test_block_chain::test_release();
  439. test_block_chain::test_memory_resource();
  440. test_block_chain::test_destructor();
  441. test_resource_constructor();
  442. test_initial_size_constructor();
  443. test_buffer_constructor();
  444. test_upstream_resource();
  445. test_do_allocate();
  446. test_do_deallocate();
  447. test_do_is_equal();
  448. test_release();
  449. test_destructor();
  450. return ::boost::report_errors();
  451. }