/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// A queue implements a singly linked list of pages, each of which contains some // number of elements. Since the queue needs to store a "next" pointer, the // actual number of elements per page won't be quite as many as were requested. // // Each page consists of N entries. We use the head buffer as a circular buffer // if it's the only buffer; if we have more than one buffer when the head is // empty we release it. This avoids occasional freeing and reallocating buffers // every N entries. We'll still allocate and free every N if the normal queue // depth is greated than N. A fancier solution would be to move an empty Head // buffer to be an empty tail buffer, freeing if we have multiple empty tails, // but that probably isn't worth it. // // Cases: // a) single buffer, circular // Push: if not full: // Add to tail, increase count // full: // Add new page, insert there and increase count. // Pop: // take entry, bump head and decrease count // b) multiple buffers: // Push: if not full: // Add to tail, increase count // full: // Add new page, insert there and increase count. // Pop: // take entry, bump head and decrease count // if buffer is empty, free head buffer and promote next to head // template <class T, size_t RequestedItemsPerPage = 256> class Queue { public:
Queue() = default;
// Discard all elements form the queue, clearing it to be empty. void Clear() { while (!IsEmpty()) {
Pop();
} if (mHead) {
free(mHead);
mHead = nullptr;
}
}
MOZ_ASSERT(mHead != mTail, "can't have a non-circular single buffer");
T* eltPtr = &mTail->mEvents[offsetTail]; new (eltPtr) T(std::move(aElement));
++mCount; return *eltPtr;
}
bool IsEmpty() const { return !mCount; }
T Pop() {
MOZ_ASSERT(!IsEmpty());
T result = std::move(mHead->mEvents[mOffsetHead]);
mHead->mEvents[mOffsetHead].~T(); // Could be circular buffer, or not.
mOffsetHead = (mOffsetHead + 1) % ItemsPerPage;
mCount -= 1;
mHeadLength -= 1;
// Check if the head page is empty and we have more pages. if (mHead != mTail && mHeadLength == 0) {
Page* dead = mHead;
mHead = mHead->mNext;
free(dead); // Non-circular buffer
mOffsetHead = 0;
mHeadLength = static_cast<uint16_t>(std::min<uint32_t>(mCount, ItemsPerPage)); // if there are still >1 pages, the new head is full.
}
private:
static_assert(
(RequestedItemsPerPage & (RequestedItemsPerPage - 1)) == 0, "RequestedItemsPerPage should be a power of two to avoid heap slop.");
// Since a Page must also contain a "next" pointer, we use one of the items to // store this pointer. If sizeof(T) > sizeof(Page*), then some space will be // wasted. So be it. static constexpr size_t ItemsPerPage = RequestedItemsPerPage - 1;
// Page objects are linked together to form a simple deque. struct Page { struct Page* mNext;
T mEvents[ItemsPerPage];
};
uint32_t mCount = 0; // Number of items in the queue
uint16_t mOffsetHead = 0; // Read position in head page
uint16_t mHeadLength = 0; // Number of items in the circular head page
};
} // namespace mozilla
#endif// mozilla_Queue_h
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.