forked from mirrors/gecko-dev
		
	 2d21eb6e9e
			
		
	
	
		2d21eb6e9e
		
	
	
	
	
		
			
			objdir/dist/include/mozilla/Queue.h:251:31: error: use of undeclared identifier 'moz_xcalloc' xpcom/base/nsCOMPtr.h:436:5: error: static_assert failed due to requirement '1 < sizeof (TestForIID<nsIEventTarget>(nullptr))' "nsCOMPtr only works for types with IIDs. Either use RefPtr; add an IID to your type with NS_DECLARE_STATIC_IID_ACCESSOR/NS_DEFINE_STATIC_IID_ACCESSOR; or make the nsCOMPtr point to a base class with an IID." xpcom/tests/gtest/TestDelayedRunnable.cpp:16:28: error: field has incomplete type '(anonymous namespace)::ReleaseDetector' xpcom/tests/gtest/TestDelayedRunnable.cpp:16:3: error: 'explicit' can only appear on non-static member functions xpcom/tests/gtest/TestDelayedRunnable.cpp:16:40: error: expected ')' xpcom/tests/gtest/TestDelayedRunnable.cpp:16:61: error: use of undeclared identifier 'aActive' xpcom/tests/gtest/TestDelayedRunnable.cpp:28:3: error: no template named 'Atomic'; did you mean 'mozilla::Atomic'? xpcom/tests/gtest/TestDelayedRunnable.cpp:34:3: error: no template named 'Atomic'; did you mean 'mozilla::Atomic'? xpcom/tests/gtest/TestDelayedRunnable.cpp:36:18: error: use of undeclared identifier 'TaskQueue'; did you mean 'taskQueue'? xpcom/tests/gtest/TestDelayedRunnable.cpp:36:18: error: variable 'taskQueue' declared with deduced type 'auto' cannot appear in its own initializer xpcom/tests/gtest/TestDelayedRunnable.cpp:36:48: error: use of undeclared identifier 'MediaThreadType' xpcom/tests/gtest/TestDelayedRunnable.cpp:36:48: error: use of undeclared identifier 'MediaThreadType'; did you mean 'mozilla::MediaThreadType'? xpcom/tests/gtest/TestDelayedRunnable.cpp:38:51: error: no matching conversion for functional-style cast from 'Atomic<bool> *' to '(anonymous namespace)::ReleaseDetector' xpcom/tests/gtest/TestDelayedRunnable.cpp:54:3: error: no template named 'Atomic'; did you mean 'mozilla::Atomic'? xpcom/tests/gtest/TestDelayedRunnable.cpp:58:51: error: no matching conversion for functional-style cast from 'Atomic<bool> *' to '(anonymous namespace)::ReleaseDetector' xpcom/tests/gtest/TestDelayedRunnable.cpp:88:3: error: use of undeclared identifier 'Unused'; did you mean 'mozilla::Unused'? xpcom/tests/gtest/TestDelayedRunnable.cpp:104:3: error: use of undeclared identifier 'Unused'; did you mean 'mozilla::Unused'? xpcom/tests/gtest/TestDelayedRunnable.cpp:109:10: error: unknown type name 'SharedThreadPool'; did you mean 'mozilla::SharedThreadPool'? xpcom/tests/gtest/TestDelayedRunnable.cpp:109:35: error: use of undeclared identifier 'SharedThreadPool' xpcom/tests/gtest/TestDelayedRunnable.cpp:109:35: error: use of undeclared identifier 'SharedThreadPool'; did you mean 'mozilla::SharedThreadPool'? xpcom/tests/gtest/TestDelayedRunnable.cpp:110:25: error: use of function template name with no prior declaration in function call with explicit template arguments is a C++20 extension [-Werror,-Wc++20-extensions] xpcom/tests/gtest/TestDelayedRunnable.cpp:110:36: error: unknown type name 'TaskQueue'; did you mean 'mozilla::TaskQueue'? xpcom/tests/gtest/TestDelayedRunnable.cpp:112:36: error: unknown type name 'TaskQueue'; did you mean 'mozilla::TaskQueue'? xpcom/tests/gtest/TestFileNTFSSpecialPaths.cpp:18:28: error: use of undeclared identifier 'do_CreateInstance' xpcom/tests/gtest/TestFileNTFSSpecialPaths.cpp:26:28: error: use of undeclared identifier 'do_CreateInstance' xpcom/tests/gtest/TestFileNTFSSpecialPaths.cpp:39:28: error: use of undeclared identifier 'do_CreateInstance' xpcom/tests/gtest/TestFileNTFSSpecialPaths.cpp:279:28: error: use of undeclared identifier 'do_CreateInstance' xpcom/tests/gtest/TestFilePreferencesWin.cpp:113:26: error: use of undeclared identifier 'do_CreateInstance' xpcom/tests/gtest/TestFilePreferencesWin.cpp:140:26: error: use of undeclared identifier 'do_CreateInstance' xpcom/tests/gtest/TestFilePreferencesWin.cpp:156:31: error: use of undeclared identifier 'NS_OS_TEMP_DIR' xpcom/tests/gtest/TestJSHolderMap.cpp:50:28: error: unknown type name 'JSHolderMap' xpcom/tests/gtest/TestJSHolderMap.cpp:52:35: error: use of undeclared identifier 'i' xpcom/tests/gtest/TestJSHolderMap.cpp:52:45: error: use of undeclared identifier 'i' xpcom/tests/gtest/TestJSHolderMap.cpp:52:8: error: use of undeclared identifier 'JSHolderMap' xpcom/tests/gtest/TestJSHolderMap.cpp:52:8: error: use of undeclared identifier 'JSHolderMap'; did you mean 'mozilla::JSHolderMap'? xpcom/tests/gtest/TestJSHolderMap.cpp:53:24: error: use of undeclared identifier 'i' xpcom/tests/gtest/TestJSHolderMap.cpp:54:24: error: use of undeclared identifier 'i' xpcom/tests/gtest/TestJSHolderMap.cpp:68:3: error: unknown type name 'JSHolderMap'; did you mean 'mozilla::JSHolderMap'? xpcom/tests/gtest/TestJSHolderMap.cpp:73:3: error: unknown type name 'JSHolderMap'; did you mean 'mozilla::JSHolderMap'? xpcom/tests/gtest/TestJSHolderMap.cpp:99:3: error: unknown type name 'JSHolderMap'; did you mean 'mozilla::JSHolderMap'? xpcom/tests/gtest/TestJSHolderMap.cpp:104:9: error: use of undeclared identifier 'JSHolderMap' xpcom/tests/gtest/TestJSHolderMap.cpp:104:9: error: use of undeclared identifier 'JSHolderMap'; did you mean 'mozilla::JSHolderMap'? xpcom/tests/gtest/TestJSHolderMap.cpp:125:41: error: use of undeclared identifier 'MakeUnique' xpcom/tests/gtest/TestJSHolderMap.cpp:147:3: error: unknown type name 'JSHolderMap'; did you mean 'mozilla::JSHolderMap'? xpcom/tests/gtest/TestJSHolderMap.cpp:151:41: error: use of undeclared identifier 'MakeUnique' xpcom/tests/gtest/TestRWLock.cpp:92:3: error: no template named 'Maybe'; did you mean 'mozilla::Maybe'? xpcom/tests/gtest/TestRWLock.cpp:95:23: error: use of undeclared identifier 'SyncRunnable'; did you mean 'mozilla::SyncRunnable'? xpcom/tests/gtest/TestRWLock.cpp:103:5: error: unknown type name 'AutoTryReadLock'; did you mean 'mozilla::AutoTryReadLock'? xpcom/tests/gtest/TestRWLock.cpp:107:5: error: unknown type name 'AutoTryReadLock'; did you mean 'mozilla::AutoTryReadLock'? xpcom/tests/gtest/TestRWLock.cpp:111:7: error: unknown type name 'AutoTryReadLock'; did you mean 'mozilla::AutoTryReadLock'? xpcom/tests/gtest/TestRWLock.cpp:120:7: error: unknown type name 'AutoTryReadLock'; did you mean 'mozilla::AutoTryReadLock'? xpcom/tests/gtest/TestRWLock.cpp:130:5: error: unknown type name 'AutoWriteLock'; did you mean 'mozilla::AutoWriteLock'? xpcom/tests/gtest/TestRWLock.cpp:133:5: error: unknown type name 'AutoTryReadLock'; did you mean 'mozilla::AutoTryReadLock'? xpcom/tests/gtest/TestRWLock.cpp:137:7: error: unknown type name 'AutoTryReadLock'; did you mean 'mozilla::AutoTryReadLock'? xpcom/tests/gtest/TestRWLock.cpp:142:3: error: unknown type name 'AutoTryReadLock'; did you mean 'mozilla::AutoTryReadLock'? xpcom/tests/gtest/TestRWLock.cpp:146:5: error: unknown type name 'AutoTryReadLock'; did you mean 'mozilla::AutoTryReadLock'? xpcom/tests/gtest/TestRWLock.cpp:155:5: error: unknown type name 'AutoTryWriteLock'; did you mean 'mozilla::AutoTryWriteLock'? xpcom/tests/gtest/TestRWLock.cpp:159:5: error: unknown type name 'AutoTryReadLock'; did you mean 'mozilla::AutoTryReadLock'? xpcom/tests/gtest/TestRWLock.cpp:163:7: error: unknown type name 'AutoTryWriteLock'; did you mean 'mozilla::AutoTryWriteLock'? xpcom/tests/gtest/TestRWLock.cpp:172:7: error: unknown type name 'AutoTryWriteLock'; did you mean 'mozilla::AutoTryWriteLock'? xpcom/tests/gtest/TestRWLock.cpp:182:5: error: unknown type name 'AutoReadLock'; did you mean 'mozilla::AutoReadLock'? xpcom/tests/gtest/TestRWLock.cpp:184:5: error: unknown type name 'AutoTryWriteLock'; did you mean 'mozilla::AutoTryWriteLock'? xpcom/tests/gtest/TestRWLock.cpp:188:7: error: unknown type name 'AutoTryWriteLock'; did you mean 'mozilla::AutoTryWriteLock'? xpcom/tests/gtest/TestRWLock.cpp:194:5: error: unknown type name 'AutoWriteLock'; did you mean 'mozilla::AutoWriteLock'? xpcom/tests/gtest/TestRWLock.cpp:197:5: error: unknown type name 'AutoTryWriteLock'; did you mean 'mozilla::AutoTryWriteLock'? xpcom/tests/gtest/TestRWLock.cpp:201:7: error: unknown type name 'AutoTryWriteLock'; did you mean 'mozilla::AutoTryWriteLock'? Differential Revision: https://phabricator.services.mozilla.com/D127042
		
			
				
	
	
		
			265 lines
		
	
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			265 lines
		
	
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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/. */
 | |
| 
 | |
| #ifndef mozilla_Queue_h
 | |
| #define mozilla_Queue_h
 | |
| 
 | |
| #include <utility>
 | |
| #include <stdint.h>
 | |
| #include "mozilla/MemoryReporting.h"
 | |
| #include "mozilla/Assertions.h"
 | |
| #include "mozalloc.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| // define to turn on additional (DEBUG) asserts
 | |
| // #define EXTRA_ASSERTS 1
 | |
| 
 | |
| // 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, bump tail and reset to 0 if at end
 | |
| //            full:
 | |
| //              Add new page, insert there and set tail to 1
 | |
| //      Pop:
 | |
| //            take entry and bump head, reset to 0 if at end
 | |
| //   b) multiple buffers:
 | |
| //      Push: if not full:
 | |
| //              Add to tail, bump tail
 | |
| //            full:
 | |
| //              Add new page, insert there and set tail to 1
 | |
| //      Pop:
 | |
| //            take entry and bump head, reset to 0 if at end
 | |
| //            if buffer is empty, free head buffer and promote next to head
 | |
| //
 | |
| template <class T, size_t RequestedItemsPerPage = 256>
 | |
| class Queue {
 | |
|  public:
 | |
|   Queue() = default;
 | |
| 
 | |
|   Queue(Queue&& aOther) noexcept
 | |
|       : mHead(std::exchange(aOther.mHead, nullptr)),
 | |
|         mTail(std::exchange(aOther.mTail, nullptr)),
 | |
|         mOffsetHead(std::exchange(aOther.mOffsetHead, 0)),
 | |
|         mHeadLength(std::exchange(aOther.mHeadLength, 0)),
 | |
|         mTailLength(std::exchange(aOther.mTailLength, 0)) {}
 | |
| 
 | |
|   Queue& operator=(Queue&& aOther) noexcept {
 | |
|     Clear();
 | |
| 
 | |
|     mHead = std::exchange(aOther.mHead, nullptr);
 | |
|     mTail = std::exchange(aOther.mTail, nullptr);
 | |
|     mOffsetHead = std::exchange(aOther.mOffsetHead, 0);
 | |
|     mHeadLength = std::exchange(aOther.mHeadLength, 0);
 | |
|     mTailLength = std::exchange(aOther.mTailLength, 0);
 | |
|     return *this;
 | |
|   }
 | |
| 
 | |
|   ~Queue() { Clear(); }
 | |
| 
 | |
|   // Discard all elements form the queue, clearing it to be empty.
 | |
|   void Clear() {
 | |
|     while (!IsEmpty()) {
 | |
|       Pop();
 | |
|     }
 | |
|     if (mHead) {
 | |
|       free(mHead);
 | |
|       mHead = nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   T& Push(T&& aElement) {
 | |
| #if defined(EXTRA_ASSERTS) && DEBUG
 | |
|     size_t original_length = Count();
 | |
| #endif
 | |
|     if (!mHead) {
 | |
|       mHead = NewPage();
 | |
|       MOZ_ASSERT(mHead);
 | |
| 
 | |
|       mTail = mHead;
 | |
|       T* eltPtr = &mTail->mEvents[0];
 | |
|       new (eltPtr) T(std::move(aElement));
 | |
|       mOffsetHead = 0;
 | |
|       mHeadLength = 1;
 | |
| #ifdef EXTRA_ASSERTS
 | |
|       MOZ_ASSERT(Count() == original_length + 1);
 | |
| #endif
 | |
|       return *eltPtr;
 | |
|     }
 | |
|     if ((mHead == mTail && mHeadLength == ItemsPerPage) ||
 | |
|         (mHead != mTail && mTailLength == ItemsPerPage)) {
 | |
|       // either we have one (circular) buffer and it's full, or
 | |
|       // we have multiple buffers and the last buffer is full
 | |
|       Page* page = NewPage();
 | |
|       MOZ_ASSERT(page);
 | |
| 
 | |
|       mTail->mNext = page;
 | |
|       mTail = page;
 | |
|       T* eltPtr = &page->mEvents[0];
 | |
|       new (eltPtr) T(std::move(aElement));
 | |
|       mTailLength = 1;
 | |
| #ifdef EXTRA_ASSERTS
 | |
|       MOZ_ASSERT(Count() == original_length + 1);
 | |
| #endif
 | |
|       return *eltPtr;
 | |
|     }
 | |
|     if (mHead == mTail) {
 | |
|       // we have space in the (single) head buffer
 | |
|       uint16_t offset = (mOffsetHead + mHeadLength++) % ItemsPerPage;
 | |
|       T* eltPtr = &mTail->mEvents[offset];
 | |
|       new (eltPtr) T(std::move(aElement));
 | |
| #ifdef EXTRA_ASSERTS
 | |
|       MOZ_ASSERT(Count() == original_length + 1);
 | |
| #endif
 | |
|       return *eltPtr;
 | |
|     }
 | |
|     // else we have space to insert into last buffer
 | |
|     T* eltPtr = &mTail->mEvents[mTailLength++];
 | |
|     new (eltPtr) T(std::move(aElement));
 | |
| #ifdef EXTRA_ASSERTS
 | |
|     MOZ_ASSERT(Count() == original_length + 1);
 | |
| #endif
 | |
|     return *eltPtr;
 | |
|   }
 | |
| 
 | |
|   bool IsEmpty() const {
 | |
|     return !mHead || (mHead == mTail && mHeadLength == 0);
 | |
|   }
 | |
| 
 | |
|   T Pop() {
 | |
| #if defined(EXTRA_ASSERTS) && DEBUG
 | |
|     size_t original_length = Count();
 | |
| #endif
 | |
|     MOZ_ASSERT(!IsEmpty());
 | |
| 
 | |
|     T result = std::move(mHead->mEvents[mOffsetHead]);
 | |
|     mHead->mEvents[mOffsetHead].~T();
 | |
|     mOffsetHead = (mOffsetHead + 1) % ItemsPerPage;
 | |
|     mHeadLength -= 1;
 | |
| 
 | |
|     // Check if mHead points to empty (circular) Page and we have more
 | |
|     // pages
 | |
|     if (mHead != mTail && mHeadLength == 0) {
 | |
|       Page* dead = mHead;
 | |
|       mHead = mHead->mNext;
 | |
|       free(dead);
 | |
|       mOffsetHead = 0;
 | |
|       // if there are still >1 pages, the new head is full.
 | |
|       if (mHead != mTail) {
 | |
|         mHeadLength = ItemsPerPage;
 | |
|       } else {
 | |
|         mHeadLength = mTailLength;
 | |
|         mTailLength = 0;
 | |
|       }
 | |
|     }
 | |
| 
 | |
| #ifdef EXTRA_ASSERTS
 | |
|     MOZ_ASSERT(Count() == original_length - 1);
 | |
| #endif
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   T& FirstElement() {
 | |
|     MOZ_ASSERT(!IsEmpty());
 | |
|     return mHead->mEvents[mOffsetHead];
 | |
|   }
 | |
| 
 | |
|   const T& FirstElement() const {
 | |
|     MOZ_ASSERT(!IsEmpty());
 | |
|     return mHead->mEvents[mOffsetHead];
 | |
|   }
 | |
| 
 | |
|   T& LastElement() {
 | |
|     MOZ_ASSERT(!IsEmpty());
 | |
|     uint16_t offset =
 | |
|         mHead == mTail ? mOffsetHead + mHeadLength - 1 : mTailLength - 1;
 | |
|     return mTail->mEvents[offset];
 | |
|   }
 | |
| 
 | |
|   const T& LastElement() const {
 | |
|     MOZ_ASSERT(!IsEmpty());
 | |
|     uint16_t offset =
 | |
|         mHead == mTail ? mOffsetHead + mHeadLength - 1 : mTailLength - 1;
 | |
|     return mTail->mEvents[offset];
 | |
|   }
 | |
| 
 | |
|   size_t Count() const {
 | |
|     // It is obvious count is 0 when the queue is empty.
 | |
|     if (!mHead) {
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     // Compute full (intermediate) pages; Doesn't count first or last page
 | |
|     int count = 0;
 | |
|     // 1 buffer will have mHead == mTail; 2 will have mHead->mNext == mTail
 | |
|     for (Page* page = mHead; page != mTail && page->mNext != mTail;
 | |
|          page = page->mNext) {
 | |
|       count += ItemsPerPage;
 | |
|     }
 | |
|     // add first and last page
 | |
|     count += mHeadLength + mTailLength;
 | |
|     MOZ_ASSERT(count >= 0);
 | |
| 
 | |
|     return count;
 | |
|   }
 | |
| 
 | |
|   size_t ShallowSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
 | |
|     size_t n = 0;
 | |
|     if (mHead) {
 | |
|       for (Page* page = mHead; page != mTail; page = page->mNext) {
 | |
|         n += aMallocSizeOf(page);
 | |
|       }
 | |
|     }
 | |
|     return n;
 | |
|   }
 | |
| 
 | |
|   size_t ShallowSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
 | |
|     return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
 | |
|   }
 | |
| 
 | |
|  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 const size_t ItemsPerPage = RequestedItemsPerPage - 1;
 | |
| 
 | |
|   // Page objects are linked together to form a simple deque.
 | |
|   struct Page {
 | |
|     struct Page* mNext;
 | |
|     T mEvents[ItemsPerPage];
 | |
|   };
 | |
| 
 | |
|   static Page* NewPage() {
 | |
|     return static_cast<Page*>(moz_xcalloc(1, sizeof(Page)));
 | |
|   }
 | |
| 
 | |
|   Page* mHead = nullptr;
 | |
|   Page* mTail = nullptr;
 | |
| 
 | |
|   uint16_t mOffsetHead = 0;  // Read position in head page
 | |
|   uint16_t mHeadLength = 0;  // Number of items in the head page
 | |
|   uint16_t mTailLength = 0;  // Number of items in the tail page
 | |
| };
 | |
| 
 | |
| }  // namespace mozilla
 | |
| 
 | |
| #endif  // mozilla_Queue_h
 |