CCAllocatorStrategyFixedBlock.h 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /****************************************************************************
  2. Copyright (c) 2014-2017 Chukong Technologies Inc.
  3. Author: Justin Graham (https://github.com/mannewalis)
  4. http://www.cocos2d-x.org
  5. Permission is hereby granted, free of charge, to any person obtaining a copy
  6. of this software and associated documentation files (the "Software"), to deal
  7. in the Software without restriction, including without limitation the rights
  8. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. copies of the Software, and to permit persons to whom the Software is
  10. furnished to do so, subject to the following conditions:
  11. The above copyright notice and this permission notice shall be included in
  12. all copies or substantial portions of the Software.
  13. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. THE SOFTWARE.
  20. ****************************************************************************/
  21. #ifndef CC_ALLOCATOR_STRATEGY_FIXED_BLOCK_H
  22. #define CC_ALLOCATOR_STRATEGY_FIXED_BLOCK_H
  23. /// @cond DO_NOT_SHOW
  24. /****************************************************************************
  25. WARNING!
  26. Do not use Console::log or any other methods that use NEW inside of this
  27. allocator. Failure to do so will result in recursive memory allocation.
  28. ****************************************************************************/
  29. #include <stdint.h>
  30. #include <vector>
  31. #include <typeinfo>
  32. #include <sstream>
  33. #include "base/allocator/CCAllocatorBase.h"
  34. #include "base/allocator/CCAllocatorMacros.h"
  35. #include "base/allocator/CCAllocatorGlobal.h"
  36. #include "base/allocator/CCAllocatorMutex.h"
  37. #include "base/allocator/CCAllocatorDiagnostics.h"
  38. NS_CC_BEGIN
  39. NS_CC_ALLOCATOR_BEGIN
  40. // @brief define this to cause this allocator to fallback to the global allocator
  41. // this is just for testing purposes to see if this allocator is broken.
  42. //#define FALLBACK_TO_GLOBAL
  43. // @brief
  44. // Fixed sized block allocator strategy for allocating blocks
  45. // of memory that are the same size.
  46. // Optionally takes a page size which determines how many blocks
  47. // are added when the allocator needs more storage.
  48. // @param _block_size the size of the fixed block allocated by this allocator.
  49. // @param _page_size the number of blocks to allocate when growing the free list.
  50. // @param _alignment the alignment size in bytes of each block.
  51. // @param locking_semantics which locking strategy to use.
  52. template <size_t _block_size, size_t _alignment = 16, typename lock_traits = locking_semantics>
  53. class AllocatorStrategyFixedBlock
  54. : public AllocatorBase
  55. , public lock_traits
  56. {
  57. public:
  58. static const size_t block_size = _block_size;
  59. static const size_t alignment = _alignment;
  60. AllocatorStrategyFixedBlock(const char* tag = nullptr, size_t pageSize = 100)
  61. : _list(nullptr)
  62. , _pages(nullptr)
  63. , _pageSize(pageSize)
  64. , _allocated(0)
  65. {
  66. #if CC_ENABLE_ALLOCATOR_DIAGNOSTICS
  67. _highestCount = 0;
  68. AllocatorDiagnostics::instance()->trackAllocator(this);
  69. AllocatorBase::setTag(tag ? tag : typeid(AllocatorStrategyFixedBlock).name());
  70. #endif
  71. }
  72. virtual ~AllocatorStrategyFixedBlock()
  73. {
  74. #if CC_ENABLE_ALLOCATOR_DIAGNOSTICS
  75. AllocatorDiagnostics::instance()->untrackAllocator(this);
  76. #endif
  77. do
  78. {
  79. intptr_t* page = (intptr_t*)_pages;
  80. intptr_t* next = (intptr_t*)*page;
  81. ccAllocatorGlobal.deallocate(page);
  82. _pages = (void*)next;
  83. }
  84. while (_pages);
  85. }
  86. // @brief
  87. // allocate a block of memory by returning the first item in the list or if empty
  88. // then allocate a new page of blocks, and return the first element and store the rest.
  89. // if _block_size does not match the requested size, then we assert.
  90. CC_ALLOCATOR_INLINE void* allocate(size_t size)
  91. {
  92. CC_ASSERT(block_size == size);
  93. #ifdef FALLBACK_TO_GLOBAL
  94. return ccAllocatorGlobal.allocate(size);
  95. #else
  96. lock_traits::lock();
  97. auto r = pop_front();
  98. lock_traits::unlock();
  99. return r;
  100. #endif
  101. }
  102. // @brief Deallocate a block by pushing it on the head of a linked list of free blocks.
  103. CC_ALLOCATOR_INLINE void deallocate(void* address, size_t size = 0)
  104. {
  105. CC_ASSERT(0 == size || block_size == size);
  106. #ifdef FALLBACK_TO_GLOBAL
  107. ccAllocatorGlobal.deallocate(address);
  108. #else
  109. lock_traits::lock();
  110. push_front(address);
  111. lock_traits::unlock();
  112. #endif
  113. }
  114. // @brief Checks allocated pages to determine whether or not a block
  115. // is owned by this allocator. This should be reasonably fast
  116. // for properly configured allocators with few large pages.
  117. CC_ALLOCATOR_INLINE bool owns(const void* const address)
  118. {
  119. #ifdef FALLBACK_TO_GLOBAL
  120. return true; // since everything uses the global allocator, we can just lie and say we own this address.
  121. #else
  122. lock_traits::lock();
  123. const uint8_t* const a = (const uint8_t* const)address;
  124. const uint8_t* p = (uint8_t*)_pages;
  125. const size_t pSize = pageSize();
  126. while (p)
  127. {
  128. if (a >= p && a < (p + pSize))
  129. {
  130. lock_traits::unlock();
  131. return true;
  132. }
  133. p = (uint8_t*)(*(uintptr_t*)p);
  134. }
  135. lock_traits::unlock();
  136. return false;
  137. #endif
  138. }
  139. #if CC_ENABLE_ALLOCATOR_DIAGNOSTICS
  140. std::string diagnostics() const
  141. {
  142. std::stringstream s;
  143. s << AllocatorBase::tag() << " initial:" << _pageSize << " count:" << _allocated << " highest:" << _highestCount << "\n";
  144. return s.str();
  145. }
  146. size_t _highestCount;
  147. #endif
  148. protected:
  149. // @brief Method to push an allocated block onto the free list.
  150. // No check is made that the block hasn't been already added to this allocator.
  151. CC_ALLOCATOR_INLINE void push_front(void* block)
  152. {
  153. CC_ASSERT(block);
  154. CC_ASSERT(block_size < AllocatorBase::kDefaultAlignment || 0 == ((intptr_t)block & (AllocatorBase::kDefaultAlignment - 1)));
  155. #if COCOS2D_DEBUG
  156. // additional debug build checks
  157. CC_ASSERT(true == owns(block));
  158. #endif
  159. if (nullptr == _list)
  160. {
  161. _list = block;
  162. *(uintptr_t*)block = 0;
  163. }
  164. else
  165. {
  166. uintptr_t* p = (uintptr_t*)(block);
  167. *p = (uintptr_t)_list;
  168. _list = block;
  169. }
  170. CC_ASSERT(_allocated > 0);
  171. --_allocated;
  172. }
  173. // @brief Method to pop a block off the free list.
  174. // If no blocks are available, then the list is grown by _page_size
  175. // Tuning of the page size is critical to getting good performance.
  176. // Ideally you would use a page size that is around the high water mark
  177. // for the number of blocks of this size being allocated.
  178. CC_ALLOCATOR_INLINE void* pop_front()
  179. {
  180. if (nullptr == _list)
  181. {
  182. allocatePage();
  183. }
  184. auto next = (void*)*(uintptr_t*)_list;
  185. auto block = _list;
  186. _list = next;
  187. ++_allocated;
  188. #if CC_ENABLE_ALLOCATOR_DIAGNOSTICS
  189. if (_allocated > _highestCount)
  190. _highestCount = _allocated;
  191. #endif
  192. CC_ASSERT(block_size < AllocatorBase::kDefaultAlignment || 0 == ((intptr_t)block & (AllocatorBase::kDefaultAlignment - 1)));
  193. return block;
  194. }
  195. protected:
  196. // @brief Returns the size of a page in bytes + overhead.
  197. size_t pageSize() const
  198. {
  199. return AllocatorBase::kDefaultAlignment + AllocatorBase::nextPow2BlockSize(block_size) * _pageSize;
  200. }
  201. // @brief Allocates a new page from the global allocator,
  202. // and adds all the blocks to the free list.
  203. CC_ALLOCATOR_INLINE void allocatePage()
  204. {
  205. uint8_t* p = (uint8_t*)AllocatorBase::aligned(ccAllocatorGlobal.allocate(pageSize()));
  206. intptr_t* page = (intptr_t*)p;
  207. if (nullptr == _pages)
  208. {
  209. _pages = page;
  210. *page = 0;
  211. }
  212. else
  213. {
  214. *page = (intptr_t)_pages;
  215. _pages = page;
  216. }
  217. p += AllocatorBase::kDefaultAlignment; // step past the linked list node
  218. _allocated += _pageSize;
  219. size_t aligned_size = AllocatorBase::nextPow2BlockSize(block_size);
  220. uint8_t* block = (uint8_t*)p;
  221. for (unsigned int i = 0; i < _pageSize; ++i, block += aligned_size)
  222. {
  223. push_front(block);
  224. }
  225. }
  226. protected:
  227. // @brief Linked list of free blocks.
  228. void* _list;
  229. // @brief Linked list of allocated pages.
  230. void* _pages;
  231. // @brief number of blocks per page.
  232. size_t _pageSize;
  233. // @brief Number of blocks that are currently allocated.
  234. size_t _allocated;
  235. };
  236. NS_CC_ALLOCATOR_END
  237. NS_CC_END
  238. /// @endcond
  239. #endif//CC_ALLOCATOR_STRATEGY_FIXED_BLOCK_H