| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft Corporation and contributors. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- template <typename T>
- class PageStack
- {
- private:
- struct Chunk : public PagePoolPage
- {
- Chunk * nextChunk;
- T entries[];
- };
- static const size_t EntriesPerChunk = (AutoSystemInfo::PageSize - sizeof(Chunk)) / sizeof(T);
- public:
- PageStack(PagePool * pagePool);
- ~PageStack();
- void Init(uint reservedPageCount = 0);
- void Clear();
- bool Pop(T * item);
- bool Push(T item);
- uint Split(uint targetCount, __in_ecount(targetCount) PageStack<T> ** targetStacks);
- void Abort();
- void Release();
- bool IsEmpty() const;
- #if DBG
- bool HasChunk() const
- {
- return this->currentChunk != nullptr;
- }
- #endif
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- void SetMaxPageCount(size_t maxPageCount)
- {
- this->maxPageCount = maxPageCount > 1 ? maxPageCount : 1;
- }
- #endif
- static const uint MaxSplitTargets = 3; // Not counting original stack, so this supports 4-way parallel
- private:
- Chunk * CreateChunk();
- void FreeChunk(Chunk * chunk);
- private:
- T * nextEntry;
- T * chunkStart;
- T * chunkEnd;
- Chunk * currentChunk;
- PagePool * pagePool;
- bool usesReservedPages;
- #if DBG
- size_t count;
- #endif
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- size_t pageCount;
- size_t maxPageCount;
- #endif
- };
- template <typename T>
- inline
- bool PageStack<T>::Pop(T * item)
- {
- Assert(currentChunk != nullptr);
- if (nextEntry == chunkStart)
- {
- // We're at the beginning of the chunk. Move to the previous chunk, if any
- if (currentChunk->nextChunk == nullptr)
- {
- // All done
- Assert(count == 0);
- return false;
- }
- Chunk * temp = currentChunk;
- currentChunk = currentChunk->nextChunk;
- FreeChunk(temp);
- chunkStart = currentChunk->entries;
- chunkEnd = ¤tChunk->entries[EntriesPerChunk];
- nextEntry = chunkEnd;
- }
- Assert(nextEntry > chunkStart && nextEntry <= chunkEnd);
- nextEntry--;
- #if defined(_M_IX86) || defined(_M_X64)
- _mm_prefetch((char*) (nextEntry - 1), _MM_HINT_T0);
- #endif
- *item = *nextEntry;
- #if DBG
- count--;
- Assert(count == (nextEntry - chunkStart) + (pageCount - 1) * EntriesPerChunk);
- #endif
- return true;
- }
- template <typename T>
- inline
- bool PageStack<T>::Push(T item)
- {
- if (nextEntry == chunkEnd)
- {
- Chunk * newChunk = CreateChunk();
- if (newChunk == nullptr)
- {
- return false;
- }
- newChunk->nextChunk = currentChunk;
- currentChunk = newChunk;
- chunkStart = currentChunk->entries;
- chunkEnd = ¤tChunk->entries[EntriesPerChunk];
- nextEntry = chunkStart;
- }
- Assert(nextEntry >= chunkStart && nextEntry < chunkEnd);
- *nextEntry = item;
- nextEntry++;
- #if DBG
- count++;
- Assert(count == (nextEntry - chunkStart) + (pageCount - 1) * EntriesPerChunk);
- #endif
- return true;
- }
- template <typename T>
- PageStack<T>::PageStack(PagePool * pagePool) :
- pagePool(pagePool),
- currentChunk(nullptr),
- nextEntry(nullptr),
- chunkStart(nullptr),
- chunkEnd(nullptr),
- usesReservedPages(false)
- {
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- pageCount = 0;
- maxPageCount = (size_t)-1; // Default to no limit
- #endif
- #if DBG
- count = 0;
- #endif
- }
- template <typename T>
- PageStack<T>::~PageStack()
- {
- Assert(currentChunk == nullptr);
- Assert(nextEntry == nullptr);
- Assert(count == 0);
- Assert(pageCount == 0);
- }
- template <typename T>
- void PageStack<T>::Init(uint reservedPageCount)
- {
- if (reservedPageCount > 0)
- {
- this->usesReservedPages = true;
- this->pagePool->ReservePages(reservedPageCount);
- }
- // Preallocate one chunk.
- Assert(currentChunk == nullptr);
- currentChunk = CreateChunk();
- if (currentChunk == nullptr)
- {
- Js::Throw::OutOfMemory();
- }
- currentChunk->nextChunk = nullptr;
- chunkStart = currentChunk->entries;
- chunkEnd = ¤tChunk->entries[EntriesPerChunk];
- nextEntry = chunkStart;
- }
- template <typename T>
- void PageStack<T>::Clear()
- {
- currentChunk = nullptr;
- nextEntry = nullptr;
- #if DBG
- count = 0;
- pageCount = 0;
- #endif
- }
- template <typename T>
- typename PageStack<T>::Chunk * PageStack<T>::CreateChunk()
- {
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (pageCount >= maxPageCount)
- {
- return nullptr;
- }
- #endif
- Chunk * newChunk = (Chunk *)this->pagePool->GetPage(usesReservedPages);
- if (newChunk == nullptr)
- {
- return nullptr;
- }
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- pageCount++;
- #endif
- return newChunk;
- }
- template <typename T>
- void PageStack<T>::FreeChunk(Chunk * chunk)
- {
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- pageCount--;
- #endif
- this->pagePool->FreePage(chunk);
- }
- template <typename T>
- uint PageStack<T>::Split(uint targetCount, __in_ecount(targetCount) PageStack<T> ** targetStacks)
- {
- // Split the current stack up to [targetCount + 1] ways.
- // [targetStacks] contains the target stacks and must have [targetCount] elements.
- Assert(targetCount > 0 && targetCount <= MaxSplitTargets);
- Assert(targetStacks);
- __analysis_assume(targetCount <= MaxSplitTargets);
- Chunk * mainCurrent;
- Chunk * targetCurrents[MaxSplitTargets];
- // Do the initial split of first pages for each target stack.
- // During this, if we run out of pages, we will return a value < maxSplit to
- // indicate that the split was less than the maximum possible.
- Chunk * chunk = this->currentChunk;
- Assert(chunk != nullptr);
- // The first chunk is assigned to the main stack, and since it's already there,
- // we just advance to the next chunk and start assigning to each target stack.
- mainCurrent = chunk;
- chunk = chunk->nextChunk;
- uint targetIndex = 0;
- while (targetIndex < targetCount)
- {
- if (chunk == nullptr)
- {
- // No more pages. Adjust targetCount down to what we were actually able to do.
- // We'll return this number below so the caller knows.
- targetCount = targetIndex;
- break;
- }
- // Target stack should be empty.
- // If it has a free page currently, release it.
- Assert(targetStacks[targetIndex]->IsEmpty());
- targetStacks[targetIndex]->Release();
- targetStacks[targetIndex]->currentChunk = chunk;
- targetStacks[targetIndex]->chunkStart = chunk->entries;
- targetStacks[targetIndex]->chunkEnd = &chunk->entries[EntriesPerChunk];
- targetStacks[targetIndex]->nextEntry = targetStacks[targetIndex]->chunkEnd;
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- this->pageCount--;
- targetStacks[targetIndex]->pageCount = 1;
- #endif
- #if DBG
- this->count -= EntriesPerChunk;
- targetStacks[targetIndex]->count = EntriesPerChunk;
- #endif
- targetCurrents[targetIndex] = chunk;
- chunk = chunk->nextChunk;
- targetIndex++;
- }
- // Loop through the remaining chunks (if any),
- // assigning each chunk to the main chunk and the target chunks in turn,
- // and linking each chunk to the end of the respective list.
- while (true)
- {
- if (chunk == nullptr)
- {
- break;
- }
- mainCurrent->nextChunk = chunk;
- mainCurrent = chunk;
- chunk = chunk->nextChunk;
- targetIndex = 0;
- while (targetIndex < targetCount)
- {
- if (chunk == nullptr)
- {
- break;
- }
- targetCurrents[targetIndex]->nextChunk = chunk;
- targetCurrents[targetIndex] = chunk;
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- this->pageCount--;
- targetStacks[targetIndex]->pageCount++;
- #endif
- #if DBG
- this->count -= EntriesPerChunk;
- targetStacks[targetIndex]->count += EntriesPerChunk;
- #endif
- chunk = chunk->nextChunk;
- targetIndex++;
- }
- }
- // Terminate all the split chunk lists with null
- mainCurrent->nextChunk = nullptr;
- targetIndex = 0;
- while (targetIndex < targetCount)
- {
- targetCurrents[targetIndex]->nextChunk = nullptr;
- targetIndex++;
- }
- // Return the actual split count we were able to do, which may have been lowered above.
- return targetCount;
- }
- template <typename T>
- void PageStack<T>::Abort()
- {
- // Abandon the current entries in the stack and reset to initialized state.
- if (currentChunk == nullptr)
- {
- Assert(count == 0);
- return;
- }
- // Free all the chunks except the first one
- while (currentChunk->nextChunk != nullptr)
- {
- Chunk * temp = currentChunk;
- currentChunk = currentChunk->nextChunk;
- FreeChunk(temp);
- }
- chunkStart = currentChunk->entries;
- chunkEnd = ¤tChunk->entries[EntriesPerChunk];
- nextEntry = chunkStart;
- #if DBG
- count = 0;
- #endif
- }
- template <typename T>
- void PageStack<T>::Release()
- {
- Assert(IsEmpty());
- // We may have a preallocated chunk still held; if so release it.
- if (currentChunk != nullptr)
- {
- Assert(currentChunk->nextChunk == nullptr);
- FreeChunk(currentChunk);
- currentChunk = nullptr;
- }
- nextEntry = nullptr;
- chunkStart = nullptr;
- chunkEnd = nullptr;
- }
- template <typename T>
- bool PageStack<T>::IsEmpty() const
- {
- if (currentChunk == nullptr)
- {
- Assert(count == 0);
- Assert(nextEntry == nullptr);
- return true;
- }
- if (nextEntry == chunkStart && currentChunk->nextChunk == nullptr)
- {
- Assert(count == 0);
- return true;
- }
- Assert(count != 0);
- return false;
- }
|