Bug 1647536 - Support ref counted pointers in nsDeque r=froydnj

Adds a new specialization of nsDeque, nsRefPtrDeque for use with
reference counted pointers.

Differential Revision: https://phabricator.services.mozilla.com/D84766
This commit is contained in:
Chris Fronk 2020-08-22 15:47:59 +00:00
Родитель 7aeba750f4
Коммит 3b7a1daba2
10 изменённых файлов: 348 добавлений и 149 удалений

Просмотреть файл

@ -3958,8 +3958,7 @@ class VideoQueueMemoryFunctor : public nsDequeFunctor<VideoData> {
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<AudioData> {
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;

Просмотреть файл

@ -19,19 +19,11 @@ namespace mozilla {
class AudioData;
// Thread and type safe wrapper around nsDeque.
template <class T>
class MediaQueueDeallocator : public nsDequeFunctor<T> {
virtual void operator()(T* aObject) override {
RefPtr<T> releaseMe = dont_AddRef(aObject);
}
};
template <class T>
class MediaQueue : private nsDeque<T> {
class MediaQueue : private nsRefPtrDeque<T> {
public:
MediaQueue()
: nsDeque<T>(new MediaQueueDeallocator<T>()),
: nsRefPtrDeque<T>(),
mRecursiveMutex("mediaqueue"),
mEndOfStream(false) {}
@ -39,7 +31,7 @@ class MediaQueue : private nsDeque<T> {
inline size_t GetSize() const {
RecursiveMutexAutoLock lock(mRecursiveMutex);
return nsDeque<T>::GetSize();
return nsRefPtrDeque<T>::GetSize();
}
inline void Push(T* aItem) {
@ -50,10 +42,12 @@ class MediaQueue : private nsDeque<T> {
inline void Push(already_AddRefed<T> aItem) {
RecursiveMutexAutoLock lock(mRecursiveMutex);
T* item = aItem.take();
MOZ_DIAGNOSTIC_ASSERT(item);
MOZ_DIAGNOSTIC_ASSERT(item->GetEndTime() >= item->mTime);
nsDeque<T>::Push(item);
nsRefPtrDeque<T>::Push(dont_AddRef(item));
mPushEvent.Notify(RefPtr<T>(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<T> {
inline already_AddRefed<T> PopFront() {
RecursiveMutexAutoLock lock(mRecursiveMutex);
RefPtr<T> rv = dont_AddRef(nsDeque<T>::PopFront());
RefPtr<T> rv = nsRefPtrDeque<T>::PopFront();
if (rv) {
MOZ_DIAGNOSTIC_ASSERT(rv->GetEndTime() >= rv->mTime);
mPopFrontEvent.Notify(rv);
mPopFrontEvent.Notify(RefPtr<T>(rv));
}
return rv.forget();
}
inline already_AddRefed<T> PopBack() {
RecursiveMutexAutoLock lock(mRecursiveMutex);
RefPtr<T> rv = dont_AddRef(nsDeque<T>::Pop());
return rv.forget();
return nsRefPtrDeque<T>::Pop();
}
inline RefPtr<T> PeekFront() const {
RecursiveMutexAutoLock lock(mRecursiveMutex);
return nsDeque<T>::PeekFront();
return nsRefPtrDeque<T>::PeekFront();
}
inline RefPtr<T> PeekBack() const {
RecursiveMutexAutoLock lock(mRecursiveMutex);
return nsDeque<T>::Peek();
return nsRefPtrDeque<T>::Peek();
}
void Reset() {
RecursiveMutexAutoLock lock(mRecursiveMutex);
while (GetSize() > 0) {
RefPtr<T> x = dont_AddRef(nsDeque<T>::PopFront());
}
nsRefPtrDeque<T>::Erase();
mEndOfStream = false;
}
@ -123,14 +114,14 @@ class MediaQueue : private nsDeque<T> {
if (GetSize() == 0) {
return 0;
}
T* last = nsDeque<T>::Peek();
T* first = nsDeque<T>::PeekFront();
T* last = nsRefPtrDeque<T>::Peek();
T* first = nsRefPtrDeque<T>::PeekFront();
return (last->GetEndTime() - first->mTime).ToMicroseconds();
}
void LockedForEach(nsDequeFunctor<T>& aFunctor) const {
RecursiveMutexAutoLock lock(mRecursiveMutex);
nsDeque<T>::ForEach(aFunctor);
nsRefPtrDeque<T>::ForEach(aFunctor);
}
// Extracts elements from the queue into aResult, in order.
@ -140,13 +131,13 @@ class MediaQueue : private nsDeque<T> {
if (GetSize() == 0) return;
size_t i;
for (i = GetSize() - 1; i > 0; --i) {
T* v = nsDeque<T>::ObjectAt(i);
T* v = nsRefPtrDeque<T>::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<T> elem = nsDeque<T>::ObjectAt(i);
RefPtr<T> elem = nsRefPtrDeque<T>::ObjectAt(i);
aResult->AppendElement(elem);
}
}
@ -159,7 +150,7 @@ class MediaQueue : private nsDeque<T> {
void GetFirstElements(uint32_t aMaxElements, nsTArray<RefPtr<T>>* aResult) {
RecursiveMutexAutoLock lock(mRecursiveMutex);
for (size_t i = 0; i < aMaxElements && i < GetSize(); ++i) {
*aResult->AppendElement() = nsDeque<T>::ObjectAt(i);
*aResult->AppendElement() = nsRefPtrDeque<T>::ObjectAt(i);
}
}
@ -169,7 +160,7 @@ class MediaQueue : private nsDeque<T> {
RecursiveMutexAutoLock lock(mRecursiveMutex);
uint32_t frames = 0;
for (size_t i = 0; i < GetSize(); ++i) {
T* v = nsDeque<T>::ObjectAt(i);
T* v = nsRefPtrDeque<T>::ObjectAt(i);
frames += v->Frames();
}
return frames;

Просмотреть файл

@ -14,16 +14,8 @@
namespace mozilla {
class TransactionStackDeallocator final
: public nsDequeFunctor<TransactionItem> {
virtual void operator()(TransactionItem* aObject) override {
RefPtr<TransactionItem> releaseMe = dont_AddRef(aObject);
}
};
TransactionStack::TransactionStack(Type aType)
: nsDeque<TransactionItem>(new TransactionStackDeallocator()),
mType(aType) {}
: nsRefPtrDeque<TransactionItem>(), mType(aType) {}
TransactionStack::~TransactionStack() { Clear(); }
@ -31,43 +23,35 @@ void TransactionStack::Push(TransactionItem* aTransactionItem) {
if (NS_WARN_IF(!aTransactionItem)) {
return;
}
RefPtr<TransactionItem> item(aTransactionItem);
Push(item.forget());
nsRefPtrDeque<TransactionItem>::Push(aTransactionItem);
}
void TransactionStack::Push(
already_AddRefed<TransactionItem> aTransactionItem) {
RefPtr<TransactionItem> transactionItem(aTransactionItem);
TransactionItem* transactionItem = aTransactionItem.take();
if (NS_WARN_IF(!transactionItem)) {
return;
}
nsDeque<TransactionItem>::Push(transactionItem.forget().take());
nsRefPtrDeque<TransactionItem>::Push(dont_AddRef(transactionItem));
}
already_AddRefed<TransactionItem> TransactionStack::Pop() {
RefPtr<TransactionItem> item = dont_AddRef(nsDeque<TransactionItem>::Pop());
return item.forget();
return nsRefPtrDeque<TransactionItem>::Pop();
}
already_AddRefed<TransactionItem> TransactionStack::PopBottom() {
RefPtr<TransactionItem> item =
dont_AddRef(nsDeque<TransactionItem>::PopFront());
return item.forget();
return nsRefPtrDeque<TransactionItem>::PopFront();
}
already_AddRefed<TransactionItem> TransactionStack::Peek() {
RefPtr<TransactionItem> item = nsDeque<TransactionItem>::Peek();
already_AddRefed<TransactionItem> TransactionStack::Peek() const {
RefPtr<TransactionItem> item = nsRefPtrDeque<TransactionItem>::Peek();
return item.forget();
}
already_AddRefed<TransactionItem> TransactionStack::GetItemAt(
size_t aIndex) const {
if (NS_WARN_IF(aIndex >= nsDeque<TransactionItem>::GetSize())) {
return nullptr;
}
RefPtr<TransactionItem> item = nsDeque<TransactionItem>::ObjectAt(aIndex);
RefPtr<TransactionItem> item =
nsRefPtrDeque<TransactionItem>::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<TransactionItem>::ObjectAt(i);
auto* item = nsRefPtrDeque<TransactionItem>::ObjectAt(i);
if (item) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "transaction stack mDeque[i]");
cb.NoteNativeChild(item,

Просмотреть файл

@ -15,7 +15,7 @@ namespace mozilla {
class TransactionItem;
class TransactionStack : private nsDeque<TransactionItem> {
class TransactionStack : private nsRefPtrDeque<TransactionItem> {
public:
enum Type { FOR_UNDO, FOR_REDO };
@ -26,10 +26,10 @@ class TransactionStack : private nsDeque<TransactionItem> {
void Push(already_AddRefed<TransactionItem> aTransactionItem);
already_AddRefed<TransactionItem> Pop();
already_AddRefed<TransactionItem> PopBottom();
already_AddRefed<TransactionItem> Peek();
already_AddRefed<TransactionItem> Peek() const;
already_AddRefed<TransactionItem> GetItemAt(size_t aIndex) const;
void Clear();
size_t GetSize() const { return nsDeque::GetSize(); }
size_t GetSize() const { return nsRefPtrDeque<TransactionItem>::GetSize(); }
bool IsEmpty() const { return GetSize() == 0; }
void DoUnlink() { Clear(); }

Просмотреть файл

@ -104,7 +104,7 @@ EventTokenBucket::~EventTokenBucket() {
// Complete any queued events to prevent hangs
while (mEvents.GetSize()) {
RefPtr<TokenBucketCancelable> cancelable = dont_AddRef(mEvents.PopFront());
RefPtr<TokenBucketCancelable> cancelable = mEvents.PopFront();
cancelable->Fire();
}
}
@ -196,7 +196,7 @@ void EventTokenBucket::Stop() {
// Complete any queued events to prevent hangs
while (mEvents.GetSize()) {
RefPtr<TokenBucketCancelable> cancelable = dont_AddRef(mEvents.PopFront());
RefPtr<TokenBucketCancelable> 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<TokenBucketCancelable> cancelable = dont_AddRef(mEvents.PopFront());
RefPtr<TokenBucketCancelable> cancelable = mEvents.PopFront();
if (cancelable->mEvent) {
SOCKET_LOG(
("EventTokenBucket::DispachEvents [%p] "

Просмотреть файл

@ -121,7 +121,7 @@ class EventTokenBucket : public nsITimerCallback,
bool mPaused;
bool mStopped;
nsDeque<TokenBucketCancelable> mEvents;
nsRefPtrDeque<TokenBucketCancelable> mEvents;
bool mTimerArmed;
TimeStamp mLastUpdate;

Просмотреть файл

@ -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<DataChannel> temp;
DataChannel* temp_channel; // really already_AddRefed<>
nsRefPtrDeque<DataChannel> temp;
RefPtr<DataChannel> temp_channel;
while (nullptr != (temp_channel = mPending.PopFront())) {
temp.Push(temp_channel);
temp.Push(temp_channel.forget());
}
RefPtr<DataChannel> 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<DataChannel> 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<DataChannel> 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

Просмотреть файл

@ -357,7 +357,7 @@ class DataChannelConnection final : public net::NeckoTargetHolder
Channels mChannels;
// STS only
uint32_t mCurrentStream = 0;
nsDeque<DataChannel> mPending; // Holds addref'ed DataChannel's -- careful!
nsRefPtrDeque<DataChannel> mPending;
// STS and main
size_t mNegotiatedIdLimit = 0; // GUARDED_BY(mConnection->mLock)
uint8_t mPendingType = PENDING_NONE;

Просмотреть файл

@ -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 <typename Deque>
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 <typename Deque>
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<nsDeque<T>>;
using ConstIterator = mozilla::detail::ConstIterator<nsDeque<T>>;
/**
* 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<T*>(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 <typename T>
class nsRefPtrDeque : private nsDeque<T> {
typedef mozilla::fallible_t fallible_t;
class RefPtrDeallocator : public nsDequeFunctor<T> {
public:
virtual void operator()(T* aObject) override {
RefPtr<T> releaseMe = dont_AddRef(aObject);
}
};
public:
using PointerType = RefPtr<T>;
using ConstDequeIterator =
mozilla::detail::ConstDequeIterator<nsRefPtrDeque<T>>;
using ConstIterator = mozilla::detail::ConstIterator<nsRefPtrDeque<T>>;
explicit nsRefPtrDeque() : nsDeque<T>(new RefPtrDeallocator()) {}
inline void PushFront(already_AddRefed<T> aItem) {
T* item = aItem.take();
nsDeque<T>::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<T> aItem) {
T* item = aItem.take();
nsDeque<T>::Push(item);
}
inline already_AddRefed<T> PopFront() {
return dont_AddRef(nsDeque<T>::PopFront());
}
inline already_AddRefed<T> Pop() { return dont_AddRef(nsDeque<T>::Pop()); }
inline T* PeekFront() const { return nsDeque<T>::PeekFront(); }
inline T* Peek() const { return nsDeque<T>::Peek(); }
inline T* ObjectAt(size_t aIndex) const {
return nsDeque<T>::ObjectAt(aIndex);
}
inline void Erase() { nsDeque<T>::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<T>::SizeOfExcludingThis(aMallocSizeOf);
}
inline size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
return nsDeque<T>::SizeOfIncludingThis(aMallocSizeOf);
}
inline size_t GetSize() const { return nsDeque<T>::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<T>& aFunctor) const {
size_t size = GetSize();
for (size_t i = 0; i < size; ++i) {
aFunctor(ObjectAt(i));
}
}
};
#endif

Просмотреть файл

@ -7,6 +7,7 @@
#include "gtest/gtest.h"
#include "nsDeque.h"
#include "nsCRT.h"
#include "mozilla/RefPtr.h"
#include <stdio.h>
#include <functional>
#include <type_traits>
@ -54,6 +55,53 @@ class ForEachAdder : public nsDequeFunctor<int> {
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<RefCountedClass> {
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<RefCountedClass> deque;
RefPtr<RefCountedClass> 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<RefCountedClass> ptr2 = new RefCountedClass();
deque.PushFront(ptr2.forget());
EXPECT_TRUE(deque.PeekFront());
ptr2 = deque.PopFront();
EXPECT_EQ(ptr1, deque.PeekFront());
}
EXPECT_EQ(1u, sFreeCount);
{
RefPtr<RefCountedClass> 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<RefCountedClass> deque;
const uint32_t cnt = 10;
for (uint32_t i = 0; i < cnt; ++i) {
RefPtr<RefCountedClass> 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<RefCountedClass>::ConstIterator it = deque.begin();
it != deque.end(); ++it) {
RefPtr<RefCountedClass> 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);
}