PageStack.h 9.7 KB

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