PageStack.h 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. template <typename T>
  6. class PageStack
  7. {
  8. private:
  9. struct Chunk : public PagePoolPage
  10. {
  11. Chunk * nextChunk;
  12. T entries[];
  13. };
  14. static const size_t EntriesPerChunk = (AutoSystemInfo::PageSize - sizeof(Chunk)) / sizeof(T);
  15. public:
  16. PageStack(PagePool * pagePool);
  17. ~PageStack();
  18. void Init(uint reservedPageCount = 0);
  19. void Clear();
  20. bool Pop(T * item);
  21. bool Push(T item);
  22. uint Split(uint targetCount, __in_ecount(targetCount) PageStack<T> ** targetStacks);
  23. void Abort();
  24. void Release();
  25. bool IsEmpty() const;
  26. #if DBG
  27. bool HasChunk() const
  28. {
  29. return this->currentChunk != nullptr;
  30. }
  31. #endif
  32. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  33. void SetMaxPageCount(size_t maxPageCount)
  34. {
  35. this->maxPageCount = maxPageCount > 1 ? maxPageCount : 1;
  36. }
  37. #endif
  38. static const uint MaxSplitTargets = 3; // Not counting original stack, so this supports 4-way parallel
  39. private:
  40. Chunk * CreateChunk();
  41. void FreeChunk(Chunk * chunk);
  42. private:
  43. T * nextEntry;
  44. T * chunkStart;
  45. T * chunkEnd;
  46. Chunk * currentChunk;
  47. PagePool * pagePool;
  48. bool usesReservedPages;
  49. #if DBG
  50. size_t count;
  51. #endif
  52. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  53. size_t pageCount;
  54. size_t maxPageCount;
  55. #endif
  56. };
  57. template <typename T>
  58. inline
  59. bool PageStack<T>::Pop(T * item)
  60. {
  61. Assert(currentChunk != nullptr);
  62. if (nextEntry == chunkStart)
  63. {
  64. // We're at the beginning of the chunk. Move to the previous chunk, if any
  65. if (currentChunk->nextChunk == nullptr)
  66. {
  67. // All done
  68. Assert(count == 0);
  69. return false;
  70. }
  71. Chunk * temp = currentChunk;
  72. currentChunk = currentChunk->nextChunk;
  73. FreeChunk(temp);
  74. chunkStart = currentChunk->entries;
  75. chunkEnd = &currentChunk->entries[EntriesPerChunk];
  76. nextEntry = chunkEnd;
  77. }
  78. Assert(nextEntry > chunkStart && nextEntry <= chunkEnd);
  79. nextEntry--;
  80. *item = *nextEntry;
  81. #if DBG
  82. count--;
  83. Assert(count == (nextEntry - chunkStart) + (pageCount - 1) * EntriesPerChunk);
  84. #endif
  85. return true;
  86. }
  87. template <typename T>
  88. inline
  89. bool PageStack<T>::Push(T item)
  90. {
  91. if (nextEntry == chunkEnd)
  92. {
  93. Chunk * newChunk = CreateChunk();
  94. if (newChunk == nullptr)
  95. {
  96. return false;
  97. }
  98. newChunk->nextChunk = currentChunk;
  99. currentChunk = newChunk;
  100. chunkStart = currentChunk->entries;
  101. chunkEnd = &currentChunk->entries[EntriesPerChunk];
  102. nextEntry = chunkStart;
  103. }
  104. Assert(nextEntry >= chunkStart && nextEntry < chunkEnd);
  105. *nextEntry = item;
  106. nextEntry++;
  107. #if DBG
  108. count++;
  109. Assert(count == (nextEntry - chunkStart) + (pageCount - 1) * EntriesPerChunk);
  110. #endif
  111. return true;
  112. }
  113. template <typename T>
  114. PageStack<T>::PageStack(PagePool * pagePool) :
  115. pagePool(pagePool),
  116. currentChunk(nullptr),
  117. nextEntry(nullptr),
  118. chunkStart(nullptr),
  119. chunkEnd(nullptr),
  120. usesReservedPages(false)
  121. {
  122. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  123. pageCount = 0;
  124. maxPageCount = (size_t)-1; // Default to no limit
  125. #endif
  126. #if DBG
  127. count = 0;
  128. #endif
  129. }
  130. template <typename T>
  131. PageStack<T>::~PageStack()
  132. {
  133. Assert(currentChunk == nullptr);
  134. Assert(nextEntry == nullptr);
  135. Assert(count == 0);
  136. Assert(pageCount == 0);
  137. }
  138. template <typename T>
  139. void PageStack<T>::Init(uint reservedPageCount)
  140. {
  141. if (reservedPageCount > 0)
  142. {
  143. this->usesReservedPages = true;
  144. this->pagePool->ReservePages(reservedPageCount);
  145. }
  146. // Preallocate one chunk.
  147. Assert(currentChunk == nullptr);
  148. currentChunk = CreateChunk();
  149. if (currentChunk == nullptr)
  150. {
  151. Js::Throw::OutOfMemory();
  152. }
  153. currentChunk->nextChunk = nullptr;
  154. chunkStart = currentChunk->entries;
  155. chunkEnd = &currentChunk->entries[EntriesPerChunk];
  156. nextEntry = chunkStart;
  157. }
  158. template <typename T>
  159. void PageStack<T>::Clear()
  160. {
  161. currentChunk = nullptr;
  162. nextEntry = nullptr;
  163. #if DBG
  164. count = 0;
  165. pageCount = 0;
  166. #endif
  167. }
  168. template <typename T>
  169. typename PageStack<T>::Chunk * PageStack<T>::CreateChunk()
  170. {
  171. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  172. if (pageCount >= maxPageCount)
  173. {
  174. return nullptr;
  175. }
  176. #endif
  177. Chunk * newChunk = (Chunk *)this->pagePool->GetPage(usesReservedPages);
  178. if (newChunk == nullptr)
  179. {
  180. return nullptr;
  181. }
  182. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  183. pageCount++;
  184. #endif
  185. return newChunk;
  186. }
  187. template <typename T>
  188. void PageStack<T>::FreeChunk(Chunk * chunk)
  189. {
  190. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  191. pageCount--;
  192. #endif
  193. this->pagePool->FreePage(chunk);
  194. }
  195. template <typename T>
  196. uint PageStack<T>::Split(uint targetCount, __in_ecount(targetCount) PageStack<T> ** targetStacks)
  197. {
  198. // Split the current stack up to [targetCount + 1] ways.
  199. // [targetStacks] contains the target stacks and must have [targetCount] elements.
  200. Assert(targetCount > 0 && targetCount <= MaxSplitTargets);
  201. Assert(targetStacks);
  202. __analysis_assume(targetCount <= MaxSplitTargets);
  203. Chunk * mainCurrent;
  204. Chunk * targetCurrents[MaxSplitTargets];
  205. // Do the initial split of first pages for each target stack.
  206. // During this, if we run out of pages, we will return a value < maxSplit to
  207. // indicate that the split was less than the maximum possible.
  208. Chunk * chunk = this->currentChunk;
  209. Assert(chunk != nullptr);
  210. // The first chunk is assigned to the main stack, and since it's already there,
  211. // we just advance to the next chunk and start assigning to each target stack.
  212. mainCurrent = chunk;
  213. chunk = chunk->nextChunk;
  214. uint targetIndex = 0;
  215. while (targetIndex < targetCount)
  216. {
  217. if (chunk == nullptr)
  218. {
  219. // No more pages. Adjust targetCount down to what we were actually able to do.
  220. // We'll return this number below so the caller knows.
  221. targetCount = targetIndex;
  222. break;
  223. }
  224. // Target stack should be empty.
  225. // If it has a free page currently, release it.
  226. Assert(targetStacks[targetIndex]->IsEmpty());
  227. targetStacks[targetIndex]->Release();
  228. targetStacks[targetIndex]->currentChunk = chunk;
  229. targetStacks[targetIndex]->chunkStart = chunk->entries;
  230. targetStacks[targetIndex]->chunkEnd = &chunk->entries[EntriesPerChunk];
  231. targetStacks[targetIndex]->nextEntry = targetStacks[targetIndex]->chunkEnd;
  232. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  233. this->pageCount--;
  234. targetStacks[targetIndex]->pageCount = 1;
  235. #endif
  236. #if DBG
  237. this->count -= EntriesPerChunk;
  238. targetStacks[targetIndex]->count = EntriesPerChunk;
  239. #endif
  240. targetCurrents[targetIndex] = chunk;
  241. chunk = chunk->nextChunk;
  242. targetIndex++;
  243. }
  244. // Loop through the remaining chunks (if any),
  245. // assigning each chunk to the main chunk and the target chunks in turn,
  246. // and linking each chunk to the end of the respective list.
  247. while (true)
  248. {
  249. if (chunk == nullptr)
  250. {
  251. break;
  252. }
  253. mainCurrent->nextChunk = chunk;
  254. mainCurrent = chunk;
  255. chunk = chunk->nextChunk;
  256. targetIndex = 0;
  257. while (targetIndex < targetCount)
  258. {
  259. if (chunk == nullptr)
  260. {
  261. break;
  262. }
  263. targetCurrents[targetIndex]->nextChunk = chunk;
  264. targetCurrents[targetIndex] = chunk;
  265. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  266. this->pageCount--;
  267. targetStacks[targetIndex]->pageCount++;
  268. #endif
  269. #if DBG
  270. this->count -= EntriesPerChunk;
  271. targetStacks[targetIndex]->count += EntriesPerChunk;
  272. #endif
  273. chunk = chunk->nextChunk;
  274. targetIndex++;
  275. }
  276. }
  277. // Terminate all the split chunk lists with null
  278. mainCurrent->nextChunk = nullptr;
  279. targetIndex = 0;
  280. while (targetIndex < targetCount)
  281. {
  282. targetCurrents[targetIndex]->nextChunk = nullptr;
  283. targetIndex++;
  284. }
  285. // Return the actual split count we were able to do, which may have been lowered above.
  286. return targetCount;
  287. }
  288. template <typename T>
  289. void PageStack<T>::Abort()
  290. {
  291. // Abandon the current entries in the stack and reset to initialized state.
  292. if (currentChunk == nullptr)
  293. {
  294. Assert(count == 0);
  295. return;
  296. }
  297. // Free all the chunks except the first one
  298. while (currentChunk->nextChunk != nullptr)
  299. {
  300. Chunk * temp = currentChunk;
  301. currentChunk = currentChunk->nextChunk;
  302. FreeChunk(temp);
  303. }
  304. chunkStart = currentChunk->entries;
  305. chunkEnd = &currentChunk->entries[EntriesPerChunk];
  306. nextEntry = chunkStart;
  307. #if DBG
  308. count = 0;
  309. #endif
  310. }
  311. template <typename T>
  312. void PageStack<T>::Release()
  313. {
  314. Assert(IsEmpty());
  315. // We may have a preallocated chunk still held; if so release it.
  316. if (currentChunk != nullptr)
  317. {
  318. Assert(currentChunk->nextChunk == nullptr);
  319. FreeChunk(currentChunk);
  320. currentChunk = nullptr;
  321. }
  322. nextEntry = nullptr;
  323. chunkStart = nullptr;
  324. chunkEnd = nullptr;
  325. }
  326. template <typename T>
  327. bool PageStack<T>::IsEmpty() const
  328. {
  329. if (currentChunk == nullptr)
  330. {
  331. Assert(count == 0);
  332. Assert(nextEntry == nullptr);
  333. return true;
  334. }
  335. if (nextEntry == chunkStart && currentChunk->nextChunk == nullptr)
  336. {
  337. Assert(count == 0);
  338. return true;
  339. }
  340. Assert(count != 0);
  341. return false;
  342. }