diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index f791ebf429aa..9aea22e32b64 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -3958,8 +3958,7 @@ class VideoQueueMemoryFunctor : public nsDequeFunctor { MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf); virtual void operator()(VideoData* aObject) override { - const VideoData* v = aObject; - mSize += v->SizeOfIncludingThis(MallocSizeOf); + mSize += aObject->SizeOfIncludingThis(MallocSizeOf); } size_t mSize; @@ -3972,8 +3971,7 @@ class AudioQueueMemoryFunctor : public nsDequeFunctor { MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf); virtual void operator()(AudioData* aObject) override { - const AudioData* audioData = aObject; - mSize += audioData->SizeOfIncludingThis(MallocSizeOf); + mSize += aObject->SizeOfIncludingThis(MallocSizeOf); } size_t mSize; diff --git a/dom/media/MediaQueue.h b/dom/media/MediaQueue.h index 7825a2b30d2b..2edacb917f46 100644 --- a/dom/media/MediaQueue.h +++ b/dom/media/MediaQueue.h @@ -19,19 +19,11 @@ namespace mozilla { class AudioData; -// Thread and type safe wrapper around nsDeque. template -class MediaQueueDeallocator : public nsDequeFunctor { - virtual void operator()(T* aObject) override { - RefPtr releaseMe = dont_AddRef(aObject); - } -}; - -template -class MediaQueue : private nsDeque { +class MediaQueue : private nsRefPtrDeque { public: MediaQueue() - : nsDeque(new MediaQueueDeallocator()), + : nsRefPtrDeque(), mRecursiveMutex("mediaqueue"), mEndOfStream(false) {} @@ -39,7 +31,7 @@ class MediaQueue : private nsDeque { inline size_t GetSize() const { RecursiveMutexAutoLock lock(mRecursiveMutex); - return nsDeque::GetSize(); + return nsRefPtrDeque::GetSize(); } inline void Push(T* aItem) { @@ -50,10 +42,12 @@ class MediaQueue : private nsDeque { inline void Push(already_AddRefed aItem) { RecursiveMutexAutoLock lock(mRecursiveMutex); T* item = aItem.take(); + MOZ_DIAGNOSTIC_ASSERT(item); MOZ_DIAGNOSTIC_ASSERT(item->GetEndTime() >= item->mTime); - nsDeque::Push(item); + nsRefPtrDeque::Push(dont_AddRef(item)); mPushEvent.Notify(RefPtr(item)); + // Pushing new data after queue has ended means that the stream is active // again, so we should not mark it as ended. if (mEndOfStream) { @@ -63,35 +57,32 @@ class MediaQueue : private nsDeque { inline already_AddRefed PopFront() { RecursiveMutexAutoLock lock(mRecursiveMutex); - RefPtr rv = dont_AddRef(nsDeque::PopFront()); + RefPtr rv = nsRefPtrDeque::PopFront(); if (rv) { MOZ_DIAGNOSTIC_ASSERT(rv->GetEndTime() >= rv->mTime); - mPopFrontEvent.Notify(rv); + mPopFrontEvent.Notify(RefPtr(rv)); } return rv.forget(); } inline already_AddRefed PopBack() { RecursiveMutexAutoLock lock(mRecursiveMutex); - RefPtr rv = dont_AddRef(nsDeque::Pop()); - return rv.forget(); + return nsRefPtrDeque::Pop(); } inline RefPtr PeekFront() const { RecursiveMutexAutoLock lock(mRecursiveMutex); - return nsDeque::PeekFront(); + return nsRefPtrDeque::PeekFront(); } inline RefPtr PeekBack() const { RecursiveMutexAutoLock lock(mRecursiveMutex); - return nsDeque::Peek(); + return nsRefPtrDeque::Peek(); } void Reset() { RecursiveMutexAutoLock lock(mRecursiveMutex); - while (GetSize() > 0) { - RefPtr x = dont_AddRef(nsDeque::PopFront()); - } + nsRefPtrDeque::Erase(); mEndOfStream = false; } @@ -123,14 +114,14 @@ class MediaQueue : private nsDeque { if (GetSize() == 0) { return 0; } - T* last = nsDeque::Peek(); - T* first = nsDeque::PeekFront(); + T* last = nsRefPtrDeque::Peek(); + T* first = nsRefPtrDeque::PeekFront(); return (last->GetEndTime() - first->mTime).ToMicroseconds(); } void LockedForEach(nsDequeFunctor& aFunctor) const { RecursiveMutexAutoLock lock(mRecursiveMutex); - nsDeque::ForEach(aFunctor); + nsRefPtrDeque::ForEach(aFunctor); } // Extracts elements from the queue into aResult, in order. @@ -140,13 +131,13 @@ class MediaQueue : private nsDeque { if (GetSize() == 0) return; size_t i; for (i = GetSize() - 1; i > 0; --i) { - T* v = nsDeque::ObjectAt(i); + T* v = nsRefPtrDeque::ObjectAt(i); if (v->GetEndTime().ToMicroseconds() < aTime) break; } // Elements less than i have a end time before aTime. It's also possible // that the element at i has a end time before aTime, but that's OK. for (; i < GetSize(); ++i) { - RefPtr elem = nsDeque::ObjectAt(i); + RefPtr elem = nsRefPtrDeque::ObjectAt(i); aResult->AppendElement(elem); } } @@ -159,7 +150,7 @@ class MediaQueue : private nsDeque { void GetFirstElements(uint32_t aMaxElements, nsTArray>* aResult) { RecursiveMutexAutoLock lock(mRecursiveMutex); for (size_t i = 0; i < aMaxElements && i < GetSize(); ++i) { - *aResult->AppendElement() = nsDeque::ObjectAt(i); + *aResult->AppendElement() = nsRefPtrDeque::ObjectAt(i); } } @@ -169,7 +160,7 @@ class MediaQueue : private nsDeque { RecursiveMutexAutoLock lock(mRecursiveMutex); uint32_t frames = 0; for (size_t i = 0; i < GetSize(); ++i) { - T* v = nsDeque::ObjectAt(i); + T* v = nsRefPtrDeque::ObjectAt(i); frames += v->Frames(); } return frames; diff --git a/editor/txmgr/TransactionStack.cpp b/editor/txmgr/TransactionStack.cpp index f3e6d7269957..b36fc57d99a1 100644 --- a/editor/txmgr/TransactionStack.cpp +++ b/editor/txmgr/TransactionStack.cpp @@ -14,16 +14,8 @@ namespace mozilla { -class TransactionStackDeallocator final - : public nsDequeFunctor { - virtual void operator()(TransactionItem* aObject) override { - RefPtr releaseMe = dont_AddRef(aObject); - } -}; - TransactionStack::TransactionStack(Type aType) - : nsDeque(new TransactionStackDeallocator()), - mType(aType) {} + : nsRefPtrDeque(), mType(aType) {} TransactionStack::~TransactionStack() { Clear(); } @@ -31,43 +23,35 @@ void TransactionStack::Push(TransactionItem* aTransactionItem) { if (NS_WARN_IF(!aTransactionItem)) { return; } - - RefPtr item(aTransactionItem); - Push(item.forget()); + nsRefPtrDeque::Push(aTransactionItem); } void TransactionStack::Push( already_AddRefed aTransactionItem) { - RefPtr transactionItem(aTransactionItem); + TransactionItem* transactionItem = aTransactionItem.take(); if (NS_WARN_IF(!transactionItem)) { return; } - - nsDeque::Push(transactionItem.forget().take()); + nsRefPtrDeque::Push(dont_AddRef(transactionItem)); } already_AddRefed TransactionStack::Pop() { - RefPtr item = dont_AddRef(nsDeque::Pop()); - return item.forget(); + return nsRefPtrDeque::Pop(); } already_AddRefed TransactionStack::PopBottom() { - RefPtr item = - dont_AddRef(nsDeque::PopFront()); - return item.forget(); + return nsRefPtrDeque::PopFront(); } -already_AddRefed TransactionStack::Peek() { - RefPtr item = nsDeque::Peek(); +already_AddRefed TransactionStack::Peek() const { + RefPtr item = nsRefPtrDeque::Peek(); return item.forget(); } already_AddRefed TransactionStack::GetItemAt( size_t aIndex) const { - if (NS_WARN_IF(aIndex >= nsDeque::GetSize())) { - return nullptr; - } - RefPtr item = nsDeque::ObjectAt(aIndex); + RefPtr item = + nsRefPtrDeque::ObjectAt(aIndex); return item.forget(); } @@ -80,7 +64,7 @@ void TransactionStack::Clear() { void TransactionStack::DoTraverse(nsCycleCollectionTraversalCallback& cb) { size_t size = GetSize(); for (size_t i = 0; i < size; ++i) { - TransactionItem* item = nsDeque::ObjectAt(i); + auto* item = nsRefPtrDeque::ObjectAt(i); if (item) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "transaction stack mDeque[i]"); cb.NoteNativeChild(item, diff --git a/editor/txmgr/TransactionStack.h b/editor/txmgr/TransactionStack.h index 831fa3ccb1d1..74e8aebec8a4 100644 --- a/editor/txmgr/TransactionStack.h +++ b/editor/txmgr/TransactionStack.h @@ -15,7 +15,7 @@ namespace mozilla { class TransactionItem; -class TransactionStack : private nsDeque { +class TransactionStack : private nsRefPtrDeque { public: enum Type { FOR_UNDO, FOR_REDO }; @@ -26,10 +26,10 @@ class TransactionStack : private nsDeque { void Push(already_AddRefed aTransactionItem); already_AddRefed Pop(); already_AddRefed PopBottom(); - already_AddRefed Peek(); + already_AddRefed Peek() const; already_AddRefed GetItemAt(size_t aIndex) const; void Clear(); - size_t GetSize() const { return nsDeque::GetSize(); } + size_t GetSize() const { return nsRefPtrDeque::GetSize(); } bool IsEmpty() const { return GetSize() == 0; } void DoUnlink() { Clear(); } diff --git a/netwerk/base/EventTokenBucket.cpp b/netwerk/base/EventTokenBucket.cpp index 09856e9dc01f..af902801c3f2 100644 --- a/netwerk/base/EventTokenBucket.cpp +++ b/netwerk/base/EventTokenBucket.cpp @@ -104,7 +104,7 @@ EventTokenBucket::~EventTokenBucket() { // Complete any queued events to prevent hangs while (mEvents.GetSize()) { - RefPtr cancelable = dont_AddRef(mEvents.PopFront()); + RefPtr cancelable = mEvents.PopFront(); cancelable->Fire(); } } @@ -196,7 +196,7 @@ void EventTokenBucket::Stop() { // Complete any queued events to prevent hangs while (mEvents.GetSize()) { - RefPtr cancelable = dont_AddRef(mEvents.PopFront()); + RefPtr cancelable = mEvents.PopFront(); cancelable->Fire(); } } @@ -219,7 +219,7 @@ nsresult EventTokenBucket::SubmitEvent(ATokenBucketEvent* event, if (mPaused || !TryImmediateDispatch(cancelEvent.get())) { // queue it SOCKET_LOG((" queued\n")); - mEvents.Push(cancelEvent.forget().take()); + mEvents.Push(cancelEvent.forget()); UpdateTimer(); } else { SOCKET_LOG((" dispatched synchronously\n")); @@ -242,7 +242,7 @@ void EventTokenBucket::DispatchEvents() { if (mPaused || mStopped) return; while (mEvents.GetSize() && mUnitCost <= mCredit) { - RefPtr cancelable = dont_AddRef(mEvents.PopFront()); + RefPtr cancelable = mEvents.PopFront(); if (cancelable->mEvent) { SOCKET_LOG( ("EventTokenBucket::DispachEvents [%p] " diff --git a/netwerk/base/EventTokenBucket.h b/netwerk/base/EventTokenBucket.h index 79d97da38086..cab8937400f8 100644 --- a/netwerk/base/EventTokenBucket.h +++ b/netwerk/base/EventTokenBucket.h @@ -121,7 +121,7 @@ class EventTokenBucket : public nsITimerCallback, bool mPaused; bool mStopped; - nsDeque mEvents; + nsRefPtrDeque mEvents; bool mTimerArmed; TimeStamp mLastUpdate; diff --git a/netwerk/sctp/datachannel/DataChannel.cpp b/netwerk/sctp/datachannel/DataChannel.cpp index 504b410b3a03..06388d51493a 100644 --- a/netwerk/sctp/datachannel/DataChannel.cpp +++ b/netwerk/sctp/datachannel/DataChannel.cpp @@ -904,16 +904,15 @@ void DataChannelConnection::ProcessQueuedOpens() { // Can't copy nsDeque's. Move into temp array since any that fail will // go back to mPending - nsDeque temp; - DataChannel* temp_channel; // really already_AddRefed<> + nsRefPtrDeque temp; + RefPtr temp_channel; while (nullptr != (temp_channel = mPending.PopFront())) { - temp.Push(temp_channel); + temp.Push(temp_channel.forget()); } RefPtr channel; - // All these entries have an AddRef(); make that explicit now via the - // dont_AddRef() - while (nullptr != (channel = dont_AddRef(temp.PopFront()))) { + + while (nullptr != (channel = temp.PopFront())) { if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) { DC_DEBUG(("Processing queued open for %p (%u)", channel.get(), channel->mStream)); @@ -2506,10 +2505,7 @@ already_AddRefed DataChannelConnection::OpenFinish( DC_DEBUG(("Queuing channel %p (%u) to finish open", channel.get(), stream)); // Also serves to mark we told the app channel->mFlags |= DATA_CHANNEL_FLAGS_FINISH_OPEN; - // we need a ref for the nsDeQue and one to return - DataChannel* rawChannel = channel; - rawChannel->AddRef(); - mPending.Push(rawChannel); + mPending.Push(channel); return channel.forget(); } @@ -3055,7 +3051,7 @@ void DataChannelConnection::CloseAll() { // Clean up any pending opens for channels RefPtr channel; - while (nullptr != (channel = dont_AddRef(mPending.PopFront()))) { + while (nullptr != (channel = mPending.PopFront())) { DC_DEBUG(("closing pending channel %p, stream %u", channel.get(), channel->mStream)); channel->Close(); // also releases the ref on each iteration diff --git a/netwerk/sctp/datachannel/DataChannel.h b/netwerk/sctp/datachannel/DataChannel.h index 158aced1e8c6..3e27e0faea02 100644 --- a/netwerk/sctp/datachannel/DataChannel.h +++ b/netwerk/sctp/datachannel/DataChannel.h @@ -357,7 +357,7 @@ class DataChannelConnection final : public net::NeckoTargetHolder Channels mChannels; // STS only uint32_t mCurrentStream = 0; - nsDeque mPending; // Holds addref'ed DataChannel's -- careful! + nsRefPtrDeque mPending; // STS and main size_t mNegotiatedIdLimit = 0; // GUARDED_BY(mConnection->mLock) uint8_t mPendingType = PENDING_NONE; diff --git a/xpcom/ds/nsDeque.h b/xpcom/ds/nsDeque.h index 0395f37a9a45..5340d1af89d1 100644 --- a/xpcom/ds/nsDeque.h +++ b/xpcom/ds/nsDeque.h @@ -140,6 +140,81 @@ class nsDequeBase { nsDequeBase& operator=(const nsDequeBase& aOther) = delete; nsDequeBase(const nsDequeBase& aOther) = delete; }; + +// This iterator assumes that the deque itself is const, i.e., it cannot be +// modified while the iterator is used. +// Also it is a 'const' iterator in that it provides copies of the deque's +// elements, and therefore it is not possible to modify the deque's contents +// by assigning to a dereference of this iterator. +template +class ConstDequeIterator { + public: + ConstDequeIterator(const Deque& aDeque, size_t aIndex) + : mDeque(aDeque), mIndex(aIndex) {} + ConstDequeIterator& operator++() { + ++mIndex; + return *this; + } + bool operator==(const ConstDequeIterator& aOther) const { + return mIndex == aOther.mIndex; + } + bool operator!=(const ConstDequeIterator& aOther) const { + return mIndex != aOther.mIndex; + } + typename Deque::PointerType operator*() const { + // Don't allow out-of-deque dereferences. + MOZ_RELEASE_ASSERT(mIndex < mDeque.GetSize()); + return mDeque.ObjectAt(mIndex); + } + + private: + const Deque& mDeque; + size_t mIndex; +}; + +// It is a 'const' iterator in that it provides copies of the deque's +// elements, and therefore it is not possible to modify the deque's contents +// by assigning to a dereference of this iterator. +// If the deque is modified in other ways, this iterator will stay at the same +// index, and will handle past-the-end comparisons, but not dereferencing. +template +class ConstIterator { + public: + // Special index for the end iterator, to track the possibly-shifting + // deque size. + static const size_t EndIteratorIndex = size_t(-1); + + ConstIterator(const Deque& aDeque, size_t aIndex) + : mDeque(aDeque), mIndex(aIndex) {} + ConstIterator& operator++() { + // End-iterator shouldn't be modified. + MOZ_ASSERT(mIndex != EndIteratorIndex); + ++mIndex; + return *this; + } + bool operator==(const ConstIterator& aOther) const { + return EffectiveIndex() == aOther.EffectiveIndex(); + } + bool operator!=(const ConstIterator& aOther) const { + return EffectiveIndex() != aOther.EffectiveIndex(); + } + typename Deque::PointerType operator*() const { + // Don't allow out-of-deque dereferences. + MOZ_RELEASE_ASSERT(mIndex < mDeque.GetSize()); + return mDeque.ObjectAt(mIndex); + } + + private: + // 0 <= index < deque.GetSize() inside the deque, deque.GetSize() otherwise. + // Only used when comparing indices, not to actually access items. + size_t EffectiveIndex() const { + return (mIndex < mDeque.GetSize()) ? mIndex : mDeque.GetSize(); + } + + const Deque& mDeque; + size_t mIndex; // May point outside the deque! +}; + } // namespace detail } // namespace mozilla @@ -173,6 +248,10 @@ class nsDeque : public mozilla::detail::nsDequeBase { typedef mozilla::fallible_t fallible_t; public: + using PointerType = T*; + using ConstDequeIterator = mozilla::detail::ConstDequeIterator>; + using ConstIterator = mozilla::detail::ConstIterator>; + /** * Constructs an empty deque. * @@ -275,6 +354,9 @@ class nsDeque : public mozilla::detail::nsDequeBase { * @return item in list, or nullptr if index is outside the deque */ inline T* ObjectAt(size_t aIndex) const { + if (NS_WARN_IF(aIndex >= GetSize())) { + return nullptr; + } return static_cast(nsDequeBase::ObjectAt(aIndex)); } @@ -306,80 +388,10 @@ class nsDeque : public mozilla::detail::nsDequeBase { } } - // This iterator assumes that the deque itself is const, i.e., it cannot be - // modified while the iterator is used. - // Also it is a 'const' iterator in that it provides copies of the deque's - // elements, and therefore it is not possible to modify the deque's contents - // by assigning to a dereference of this iterator. - class ConstDequeIterator { - public: - ConstDequeIterator(const nsDeque& aDeque, size_t aIndex) - : mDeque(aDeque), mIndex(aIndex) {} - ConstDequeIterator& operator++() { - ++mIndex; - return *this; - } - bool operator==(const ConstDequeIterator& aOther) const { - return mIndex == aOther.mIndex; - } - bool operator!=(const ConstDequeIterator& aOther) const { - return mIndex != aOther.mIndex; - } - T* operator*() const { - // Don't allow out-of-deque dereferences. - MOZ_RELEASE_ASSERT(mIndex < mDeque.GetSize()); - return mDeque.ObjectAt(mIndex); - } - - private: - const nsDeque& mDeque; - size_t mIndex; - }; // If this deque is const, we can provide ConstDequeIterator's. ConstDequeIterator begin() const { return ConstDequeIterator(*this, 0); } ConstDequeIterator end() const { return ConstDequeIterator(*this, mSize); } - // It is a 'const' iterator in that it provides copies of the deque's - // elements, and therefore it is not possible to modify the deque's contents - // by assigning to a dereference of this iterator. - // If the deque is modified in other ways, this iterator will stay at the same - // index, and will handle past-the-end comparisons, but not dereferencing. - class ConstIterator { - public: - // Special index for the end iterator, to track the possibly-shifting - // deque size. - static const size_t EndIteratorIndex = size_t(-1); - - ConstIterator(const nsDeque& aDeque, size_t aIndex) - : mDeque(aDeque), mIndex(aIndex) {} - ConstIterator& operator++() { - // End-iterator shouldn't be modified. - MOZ_ASSERT(mIndex != EndIteratorIndex); - ++mIndex; - return *this; - } - bool operator==(const ConstIterator& aOther) const { - return EffectiveIndex() == aOther.EffectiveIndex(); - } - bool operator!=(const ConstIterator& aOther) const { - return EffectiveIndex() != aOther.EffectiveIndex(); - } - T* operator*() const { - // Don't allow out-of-deque dereferences. - MOZ_RELEASE_ASSERT(mIndex < mDeque.GetSize()); - return mDeque.ObjectAt(mIndex); - } - - private: - // 0 <= index < deque.GetSize() inside the deque, deque.GetSize() otherwise. - // Only used when comparing indices, not to actually access items. - size_t EffectiveIndex() const { - return (mIndex < mDeque.GetSize()) ? mIndex : mDeque.GetSize(); - } - - const nsDeque& mDeque; - size_t mIndex; // May point outside the deque! - }; // If this deque is *not* const, we provide ConstIterator's that can handle // deque size changes. ConstIterator begin() { return ConstIterator(*this, 0); } @@ -423,4 +435,100 @@ class nsDeque : public mozilla::detail::nsDequeBase { mDeallocator = aDeallocator; } }; + +/** + * Specialization of nsDeque for reference counted pointers. + * + * Provides builtin reference handling as part of Push/Peek/Pop + */ +template +class nsRefPtrDeque : private nsDeque { + typedef mozilla::fallible_t fallible_t; + + class RefPtrDeallocator : public nsDequeFunctor { + public: + virtual void operator()(T* aObject) override { + RefPtr releaseMe = dont_AddRef(aObject); + } + }; + + public: + using PointerType = RefPtr; + using ConstDequeIterator = + mozilla::detail::ConstDequeIterator>; + using ConstIterator = mozilla::detail::ConstIterator>; + + explicit nsRefPtrDeque() : nsDeque(new RefPtrDeallocator()) {} + + inline void PushFront(already_AddRefed aItem) { + T* item = aItem.take(); + nsDeque::PushFront(item); + } + + inline void PushFront(T* aItem) { PushFront(do_AddRef(aItem)); } + + inline void Push(T* aItem) { Push(do_AddRef(aItem)); } + + inline void Push(already_AddRefed aItem) { + T* item = aItem.take(); + nsDeque::Push(item); + } + + inline already_AddRefed PopFront() { + return dont_AddRef(nsDeque::PopFront()); + } + + inline already_AddRefed Pop() { return dont_AddRef(nsDeque::Pop()); } + + inline T* PeekFront() const { return nsDeque::PeekFront(); } + + inline T* Peek() const { return nsDeque::Peek(); } + + inline T* ObjectAt(size_t aIndex) const { + return nsDeque::ObjectAt(aIndex); + } + + inline void Erase() { nsDeque::Erase(); } + + // If this deque is const, we can provide ConstDequeIterator's. + ConstDequeIterator begin() const { return ConstDequeIterator(*this, 0); } + ConstDequeIterator end() const { + return ConstDequeIterator(*this, GetSize()); + } + + // If this deque is *not* const, we provide ConstIterator's that can handle + // deque size changes. + ConstIterator begin() { return ConstIterator(*this, 0); } + ConstIterator end() { + return ConstIterator(*this, ConstIterator::EndIteratorIndex); + } + + inline size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { + return nsDeque::SizeOfExcludingThis(aMallocSizeOf); + } + + inline size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { + return nsDeque::SizeOfIncludingThis(aMallocSizeOf); + } + + inline size_t GetSize() const { return nsDeque::GetSize(); } + + /** + * Call this method when you want to iterate through all + * items in the container, passing a functor along + * to call your code. + * If the deque is modified during ForEach, iteration will continue based on + * item indices; meaning that front operations may effectively skip over + * items or visit some items multiple times. + * + * @param aFunctor object to call for each member + */ + void ForEach(nsDequeFunctor& aFunctor) const { + size_t size = GetSize(); + for (size_t i = 0; i < size; ++i) { + aFunctor(ObjectAt(i)); + } + } +}; + #endif diff --git a/xpcom/tests/gtest/TestNsDeque.cpp b/xpcom/tests/gtest/TestNsDeque.cpp index 38d4b8495349..af3724664701 100644 --- a/xpcom/tests/gtest/TestNsDeque.cpp +++ b/xpcom/tests/gtest/TestNsDeque.cpp @@ -7,6 +7,7 @@ #include "gtest/gtest.h" #include "nsDeque.h" #include "nsCRT.h" +#include "mozilla/RefPtr.h" #include #include #include @@ -54,6 +55,53 @@ class ForEachAdder : public nsDequeFunctor { public: int GetSum() { return sum; } }; + +static uint32_t sFreeCount = 0; + +class RefCountedClass { + public: + RefCountedClass() : mRefCnt(0), mVal(0) {} + + ~RefCountedClass() { ++sFreeCount; } + + NS_METHOD_(MozExternalRefCountType) AddRef() { + mRefCnt++; + return mRefCnt; + } + NS_METHOD_(MozExternalRefCountType) Release() { + NS_ASSERTION(mRefCnt > 0, ""); + mRefCnt--; + if (mRefCnt == 0) { + delete this; + } + return 0; + } + + inline uint32_t GetRefCount() const { return mRefCnt; } + + inline void SetVal(int aVal) { mVal = aVal; } + + inline int GetVal() const { return mVal; } + + private: + uint32_t mRefCnt; + int mVal; +}; + +class ForEachRefPtr : public nsDequeFunctor { + virtual void operator()(RefCountedClass* aObject) { + if (aObject) { + aObject->SetVal(mVal); + } + } + + private: + int mVal = 0; + + public: + explicit ForEachRefPtr(int aVal) : mVal(aVal) {} +}; + } // namespace TestNsDeque using namespace TestNsDeque; @@ -470,3 +518,77 @@ TEST(NsDeque, TestRangeFor) << "'"; } } + +TEST(NsDeque, RefPtrDeque) +{ + sFreeCount = 0; + nsRefPtrDeque deque; + RefPtr ptr1 = new RefCountedClass(); + EXPECT_EQ(1u, ptr1->GetRefCount()); + + deque.Push(ptr1); + EXPECT_EQ(2u, ptr1->GetRefCount()); + + { + auto* peekPtr1 = deque.Peek(); + EXPECT_TRUE(peekPtr1); + EXPECT_EQ(2u, ptr1->GetRefCount()); + EXPECT_EQ(1u, deque.GetSize()); + } + + { + RefPtr ptr2 = new RefCountedClass(); + deque.PushFront(ptr2.forget()); + EXPECT_TRUE(deque.PeekFront()); + ptr2 = deque.PopFront(); + EXPECT_EQ(ptr1, deque.PeekFront()); + } + EXPECT_EQ(1u, sFreeCount); + + { + RefPtr popPtr1 = deque.Pop(); + EXPECT_TRUE(popPtr1); + EXPECT_EQ(2u, ptr1->GetRefCount()); + EXPECT_EQ(0u, deque.GetSize()); + } + + EXPECT_EQ(1u, ptr1->GetRefCount()); + deque.Erase(); + EXPECT_EQ(0u, deque.GetSize()); + ptr1 = nullptr; + EXPECT_EQ(2u, sFreeCount); +} + +TEST(NsDeque, RefPtrDequeTestIterator) +{ + sFreeCount = 0; + nsRefPtrDeque deque; + const uint32_t cnt = 10; + for (uint32_t i = 0; i < cnt; ++i) { + RefPtr ptr = new RefCountedClass(); + deque.Push(ptr.forget()); + EXPECT_TRUE(deque.Peek()); + } + EXPECT_EQ(cnt, deque.GetSize()); + + int val = 100; + ForEachRefPtr functor(val); + deque.ForEach(functor); + + uint32_t pos = 0; + for (nsRefPtrDeque::ConstIterator it = deque.begin(); + it != deque.end(); ++it) { + RefPtr cur = *it; + EXPECT_TRUE(cur); + EXPECT_EQ(cur, deque.ObjectAt(pos++)); + EXPECT_EQ(val, cur->GetVal()); + } + + EXPECT_EQ(deque.ObjectAt(0), deque.PeekFront()); + EXPECT_EQ(deque.ObjectAt(cnt - 1), deque.Peek()); + + deque.Erase(); + + EXPECT_EQ(0u, deque.GetSize()); + EXPECT_EQ(cnt, sFreeCount); +}