зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
3c8ea64fcc
Коммит
8770c8a332
|
@ -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,21 +31,23 @@ 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) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(aItem);
|
||||
Push(do_AddRef(aItem));
|
||||
nsRefPtrDeque<T>::Push(aItem);
|
||||
}
|
||||
|
||||
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,7 +57,7 @@ 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);
|
||||
|
@ -73,25 +67,22 @@ class MediaQueue : private nsDeque<T> {
|
|||
|
||||
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,60 +14,37 @@
|
|||
|
||||
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(); }
|
||||
|
||||
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);
|
||||
if (NS_WARN_IF(!transactionItem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsDeque<TransactionItem>::Push(transactionItem.forget().take());
|
||||
nsRefPtrDeque<TransactionItem>::Push(std::move(aTransactionItem));
|
||||
}
|
||||
|
||||
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 +57,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);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче