diff --git a/netwerk/ipc/ChannelEventQueue.cpp b/netwerk/ipc/ChannelEventQueue.cpp new file mode 100644 index 000000000000..80a5b4b5c308 --- /dev/null +++ b/netwerk/ipc/ChannelEventQueue.cpp @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set sw=2 ts=8 et tw=80 : + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Josh Matthews (initial developer) + * Jason Duell + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIChannel.h" +#include "mozilla/net/ChannelEventQueue.h" + +namespace mozilla { +namespace net { + +void +ChannelEventQueue::FlushQueue() +{ + // Events flushed could include destruction of channel (and our own + // destructor) unless we make sure its refcount doesn't drop to 0 while this + // method is running. + nsCOMPtr kungFuDeathGrip(mOwner); + + // Prevent flushed events from flushing the queue recursively + mFlushing = true; + + PRUint32 i; + for (i = 0; i < mEventQueue.Length(); i++) { + mEventQueue[i]->Run(); + if (mSuspended) + break; + } + + // We will always want to remove at least one finished callback. + if (i < mEventQueue.Length()) + i++; + + // It is possible for new callbacks to be enqueued as we are + // flushing the queue, so the queue must not be cleared until + // all callbacks have run. + mEventQueue.RemoveElementsAt(0, i); + + mFlushing = false; +} + + +} +} diff --git a/netwerk/ipc/ChannelEventQueue.h b/netwerk/ipc/ChannelEventQueue.h index 8e8eb7af5d43..7339216a11a6 100644 --- a/netwerk/ipc/ChannelEventQueue.h +++ b/netwerk/ipc/ChannelEventQueue.h @@ -1,5 +1,5 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: sw=2 ts=8 et : + * vim: set sw=2 ts=8 et tw=80 : */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -23,6 +23,7 @@ * * Contributor(s): * Josh Matthews (initial developer) + * Jason Duell * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -41,6 +42,11 @@ #ifndef mozilla_net_ChannelEventQueue_h #define mozilla_net_ChannelEventQueue_h +#include +#include + +class nsIChannel; + namespace mozilla { namespace net { @@ -56,125 +62,137 @@ class ChannelEvent // queue if still dispatching previous one(s) to listeners/observers. // Otherwise synchronous XMLHttpRequests and/or other code that spins the // event loop (ex: IPDL rpc) could cause listener->OnDataAvailable (for -// instance) to be called before mListener->OnStartRequest has completed. +// instance) to be dispatched and called before mListener->OnStartRequest has +// completed. -template class AutoEventEnqueuerBase; +class AutoEventEnqueuerBase; -template class ChannelEventQueue { public: - ChannelEventQueue(T* self) : mQueuePhase(PHASE_UNQUEUED) - , mSelf(self) {} + ChannelEventQueue(nsIChannel *owner) + : mForced(false) + , mSuspended(false) + , mFlushing(false) + , mOwner(owner) {} + ~ChannelEventQueue() {} - - protected: - void BeginEventQueueing(); - void EndEventQueueing(); - void EnqueueEvent(ChannelEvent* callback); - bool ShouldEnqueue(); - void FlushEventQueue(); - nsTArray > mEventQueue; - enum { - PHASE_UNQUEUED, - PHASE_QUEUEING, - PHASE_FINISHED_QUEUEING, - PHASE_FLUSHING - } mQueuePhase; + // Checks to determine if an IPDL-generated channel event can be processed + // immediately, or needs to be queued using Enqueue(). + inline bool ShouldEnqueue(); - typedef AutoEventEnqueuerBase AutoEventEnqueuer; + // Puts IPDL-generated channel event into queue, to be run later + // automatically when EndForcedQueueing and/or Resume is called. + inline void Enqueue(ChannelEvent* callback); + + // After StartForcedQueueing is called, ShouldEnqueue() will return true and + // no events will be run/flushed until EndForcedQueueing is called. + // - Note: queueing may still be required after EndForcedQueueing() (if the + // queue is suspended, etc): always call ShouldEnqueue() to determine + // whether queueing is needed. + inline void StartForcedQueueing(); + inline void EndForcedQueueing(); + + // Suspend/resume event queue. ShouldEnqueue() will return true and no events + // will be run/flushed until resume is called. These should be called when + // the channel owning the event queue is suspended/resumed. + // - Note: these suspend/resume functions are NOT meant to be called + // recursively: call them only at initial suspend, and actual resume). + // - Note: Resume flushes the queue and invokes any pending callbacks + // immediately--caller must arrange any needed asynchronicity vis a vis + // the channel's own Resume() method. + inline void Suspend(); + inline void Resume(); private: - T* mSelf; + inline void MaybeFlushQueue(); + void FlushQueue(); - friend class AutoEventEnqueuerBase; + nsTArray > mEventQueue; + + bool mForced; + bool mSuspended; + bool mFlushing; + + // Keep ptr to avoid refcount cycle: only grab ref during flushing. + nsIChannel *mOwner; + + friend class AutoEventEnqueuer; }; -template inline void -ChannelEventQueue::BeginEventQueueing() +inline bool +ChannelEventQueue::ShouldEnqueue() { - if (mQueuePhase != PHASE_UNQUEUED) - return; - // Store incoming IPDL messages for later. - mQueuePhase = PHASE_QUEUEING; + bool answer = mForced || mSuspended || mFlushing; + + NS_ABORT_IF_FALSE(answer == true || mEventQueue.IsEmpty(), + "Should always enqueue if ChannelEventQueue not empty"); + + return answer; } -template inline void -ChannelEventQueue::EndEventQueueing() -{ - if (mQueuePhase != PHASE_QUEUEING) - return; - - mQueuePhase = PHASE_FINISHED_QUEUEING; -} - -template inline bool -ChannelEventQueue::ShouldEnqueue() -{ - return mQueuePhase != PHASE_UNQUEUED || mSelf->IsSuspended(); -} - -template inline void -ChannelEventQueue::EnqueueEvent(ChannelEvent* callback) +inline void +ChannelEventQueue::Enqueue(ChannelEvent* callback) { mEventQueue.AppendElement(callback); } -template void -ChannelEventQueue::FlushEventQueue() +inline void +ChannelEventQueue::StartForcedQueueing() { - NS_ABORT_IF_FALSE(mQueuePhase != PHASE_UNQUEUED, - "Queue flushing should not occur if PHASE_UNQUEUED"); - - // Queue already being flushed - if (mQueuePhase != PHASE_FINISHED_QUEUEING || mSelf->IsSuspended()) - return; - - nsRefPtr kungFuDeathGrip(mSelf); - if (mEventQueue.Length() > 0) { - // It is possible for new callbacks to be enqueued as we are - // flushing the queue, so the queue must not be cleared until - // all callbacks have run. - mQueuePhase = PHASE_FLUSHING; - - PRUint32 i; - for (i = 0; i < mEventQueue.Length(); i++) { - mEventQueue[i]->Run(); - if (mSelf->IsSuspended()) - break; - } - - // We will always want to remove at least one finished callback. - if (i < mEventQueue.Length()) - i++; - - mEventQueue.RemoveElementsAt(0, i); - } - - if (mSelf->IsSuspended()) - mQueuePhase = PHASE_QUEUEING; - else - mQueuePhase = PHASE_UNQUEUED; + mForced = true; } -// Ensures any incoming IPDL msgs are queued during its lifetime, and flushes -// the queue when it goes out of scope. -template -class AutoEventEnqueuerBase +inline void +ChannelEventQueue::EndForcedQueueing() +{ + mForced = false; + MaybeFlushQueue(); +} + +inline void +ChannelEventQueue::Suspend() +{ + NS_ABORT_IF_FALSE(!mSuspended, + "ChannelEventQueue::Suspend called recursively"); + + mSuspended = true; +} + +inline void +ChannelEventQueue::Resume() +{ + NS_ABORT_IF_FALSE(mSuspended, + "ChannelEventQueue::Resume called when not suspended!"); + + mSuspended = false; + MaybeFlushQueue(); +} + +inline void +ChannelEventQueue::MaybeFlushQueue() +{ + // Don't flush if forced queuing on, we're already being flushed, or + // suspended, or there's nothing to flush + if (!mForced && !mFlushing && !mSuspended && !mEventQueue.IsEmpty()) + FlushQueue(); +} + +// Ensures that ShouldEnqueue() will be true during its lifetime (letting +// caller know incoming IPDL msgs should be queued). Flushes the queue when it +// goes out of scope. +class AutoEventEnqueuer { public: - AutoEventEnqueuerBase(ChannelEventQueue* queue) : mEventQueue(queue) - { - mEventQueue->BeginEventQueueing(); + AutoEventEnqueuer(ChannelEventQueue &queue) : mEventQueue(queue) { + mEventQueue.StartForcedQueueing(); } - ~AutoEventEnqueuerBase() - { - mEventQueue->EndEventQueueing(); - mEventQueue->FlushEventQueue(); + ~AutoEventEnqueuer() { + mEventQueue.EndForcedQueueing(); } private: - ChannelEventQueue *mEventQueue; + ChannelEventQueue &mEventQueue; }; } diff --git a/netwerk/ipc/Makefile.in b/netwerk/ipc/Makefile.in index 2e1b3f113ba2..89fa94c81288 100644 --- a/netwerk/ipc/Makefile.in +++ b/netwerk/ipc/Makefile.in @@ -61,6 +61,7 @@ EXPORTS_mozilla/net = \ CPPSRCS = \ NeckoChild.cpp \ NeckoParent.cpp \ + ChannelEventQueue.cpp \ $(NULL) LOCAL_INCLUDES += \ diff --git a/netwerk/protocol/ftp/FTPChannelChild.cpp b/netwerk/protocol/ftp/FTPChannelChild.cpp index 3124e2a64354..dede284d8774 100644 --- a/netwerk/protocol/ftp/FTPChannelChild.cpp +++ b/netwerk/protocol/ftp/FTPChannelChild.cpp @@ -56,8 +56,8 @@ namespace mozilla { namespace net { FTPChannelChild::FTPChannelChild(nsIURI* uri) -: ChannelEventQueue(this) -, mIPCOpen(false) +: mIPCOpen(false) +, mEventQ(this) , mCanceled(false) , mSuspendCount(0) , mIsPending(PR_FALSE) @@ -251,9 +251,9 @@ FTPChannelChild::RecvOnStartRequest(const PRInt32& aContentLength, const nsCString& aEntityID, const IPC::URI& aURI) { - if (ShouldEnqueue()) { - EnqueueEvent(new FTPStartRequestEvent(this, aContentLength, aContentType, - aLastModified, aEntityID, aURI)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new FTPStartRequestEvent(this, aContentLength, aContentType, + aLastModified, aEntityID, aURI)); } else { DoOnStartRequest(aContentLength, aContentType, aLastModified, aEntityID, aURI); @@ -280,7 +280,7 @@ FTPChannelChild::DoOnStartRequest(const PRInt32& aContentLength, uri->GetSpec(spec); nsBaseChannel::URI()->SetSpec(spec); - AutoEventEnqueuer ensureSerialDispatch(this); + AutoEventEnqueuer ensureSerialDispatch(mEventQ); nsresult rv = mListener->OnStartRequest(this, mListenerContext); if (NS_FAILED(rv)) Cancel(rv); @@ -304,8 +304,8 @@ FTPChannelChild::RecvOnDataAvailable(const nsCString& data, const PRUint32& offset, const PRUint32& count) { - if (ShouldEnqueue()) { - EnqueueEvent(new FTPDataAvailableEvent(this, data, offset, count)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new FTPDataAvailableEvent(this, data, offset, count)); } else { DoOnDataAvailable(data, offset, count); } @@ -337,7 +337,7 @@ FTPChannelChild::DoOnDataAvailable(const nsCString& data, return; } - AutoEventEnqueuer ensureSerialDispatch(this); + AutoEventEnqueuer ensureSerialDispatch(mEventQ); rv = mListener->OnDataAvailable(this, mListenerContext, stringStream, offset, count); if (NS_FAILED(rv)) @@ -359,8 +359,8 @@ class FTPStopRequestEvent : public ChannelEvent bool FTPChannelChild::RecvOnStopRequest(const nsresult& statusCode) { - if (ShouldEnqueue()) { - EnqueueEvent(new FTPStopRequestEvent(this, statusCode)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new FTPStopRequestEvent(this, statusCode)); } else { DoOnStopRequest(statusCode); } @@ -379,7 +379,7 @@ FTPChannelChild::DoOnStopRequest(const nsresult& statusCode) { // Ensure that all queued ipdl events are dispatched before // we initiate protocol deletion below. mIsPending = PR_FALSE; - AutoEventEnqueuer ensureSerialDispatch(this); + AutoEventEnqueuer ensureSerialDispatch(mEventQ); (void)mListener->OnStopRequest(this, mListenerContext, statusCode); mListener = nsnull; mListenerContext = nsnull; @@ -407,8 +407,8 @@ class FTPCancelEarlyEvent : public ChannelEvent bool FTPChannelChild::RecvCancelEarly(const nsresult& statusCode) { - if (ShouldEnqueue()) { - EnqueueEvent(new FTPCancelEarlyEvent(this, statusCode)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new FTPCancelEarlyEvent(this, statusCode)); } else { DoCancelEarly(statusCode); } @@ -453,8 +453,8 @@ class FTPDeleteSelfEvent : public ChannelEvent bool FTPChannelChild::RecvDeleteSelf() { - if (ShouldEnqueue()) { - EnqueueEvent(new FTPDeleteSelfEvent(this)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new FTPDeleteSelfEvent(this)); } else { DoDeleteSelf(); } @@ -485,8 +485,10 @@ NS_IMETHODIMP FTPChannelChild::Suspend() { NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE); - mSuspendCount++; - SendSuspend(); + if (!mSuspendCount++) { + SendSuspend(); + mEventQ.Suspend(); + } return NS_OK; } @@ -494,12 +496,10 @@ NS_IMETHODIMP FTPChannelChild::Resume() { NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE); - SendResume(); - --mSuspendCount; - if (!mSuspendCount) { - if (mQueuePhase == PHASE_UNQUEUED) - mQueuePhase = PHASE_FINISHED_QUEUEING; - FlushEventQueue(); + + if (!--mSuspendCount) { + SendResume(); + mEventQ.Resume(); // TODO: make this async: see HttpChannelChild::Resume } return NS_OK; } diff --git a/netwerk/protocol/ftp/FTPChannelChild.h b/netwerk/protocol/ftp/FTPChannelChild.h index b7fef9b82df0..500c9b08fd42 100644 --- a/netwerk/protocol/ftp/FTPChannelChild.h +++ b/netwerk/protocol/ftp/FTPChannelChild.h @@ -68,7 +68,6 @@ class FTPChannelChild : public PFTPChannelChild , public nsIResumableChannel , public nsIProxiedChannel , public nsIChildChannel - , public ChannelEventQueue { public: typedef ::nsIStreamListener nsIStreamListener; @@ -137,6 +136,7 @@ private: nsCOMPtr mUploadStream; bool mIPCOpen; + ChannelEventQueue mEventQ; bool mCanceled; PRUint32 mSuspendCount; PRPackedBool mIsPending; diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index b669a829f6ba..43a599b25b05 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -60,13 +60,13 @@ namespace net { HttpChannelChild::HttpChannelChild() : HttpAsyncAborter(this) - , ChannelEventQueue(this) , mIsFromCache(PR_FALSE) , mCacheEntryAvailable(PR_FALSE) , mCacheExpirationTime(nsICache::NO_EXPIRATION_TIME) , mSendResumeAt(false) , mIPCOpen(false) , mKeptAlive(false) + , mEventQ(this) { LOG(("Creating HttpChannelChild @%x\n", this)); } @@ -219,13 +219,13 @@ HttpChannelChild::RecvOnStartRequest(const nsHttpResponseHead& responseHead, const PRNetAddr& selfAddr, const PRNetAddr& peerAddr) { - if (ShouldEnqueue()) { - EnqueueEvent(new StartRequestEvent(this, responseHead, useResponseHead, - requestHeaders, - isFromCache, cacheEntryAvailable, - cacheExpirationTime, cachedCharset, - securityInfoSerialization, selfAddr, - peerAddr)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new StartRequestEvent(this, responseHead, useResponseHead, + requestHeaders, isFromCache, + cacheEntryAvailable, + cacheExpirationTime, cachedCharset, + securityInfoSerialization, selfAddr, + peerAddr)); } else { OnStartRequest(responseHead, useResponseHead, requestHeaders, isFromCache, cacheEntryAvailable, cacheExpirationTime, cachedCharset, @@ -255,13 +255,13 @@ HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead, NS_DeserializeObject(securityInfoSerialization, getter_AddRefs(mSecurityInfo)); } - + mIsFromCache = isFromCache; mCacheEntryAvailable = cacheEntryAvailable; mCacheExpirationTime = cacheExpirationTime; mCachedCharset = cachedCharset; - AutoEventEnqueuer ensureSerialDispatch(this); + AutoEventEnqueuer ensureSerialDispatch(mEventQ); // replace our request headers with what actually got sent in the parent mRequestHead.ClearHeaders(); @@ -329,9 +329,10 @@ HttpChannelChild::RecvOnTransportAndData(const nsresult& status, const PRUint32& offset, const PRUint32& count) { - if (ShouldEnqueue()) { - EnqueueEvent(new TransportAndDataEvent(this, status, progress, progressMax, - data, offset, count)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new TransportAndDataEvent(this, status, progress, + progressMax, data, offset, + count)); } else { OnTransportAndData(status, progress, progressMax, data, offset, count); } @@ -357,7 +358,7 @@ HttpChannelChild::OnTransportAndData(const nsresult& status, // Hold queue lock throughout all three calls, else we might process a later // necko msg in between them. - AutoEventEnqueuer ensureSerialDispatch(this); + AutoEventEnqueuer ensureSerialDispatch(mEventQ); // block status/progress after Cancel or OnStopRequest has been called, // or if channel has LOAD_BACKGROUND set. @@ -425,8 +426,8 @@ class StopRequestEvent : public ChannelEvent bool HttpChannelChild::RecvOnStopRequest(const nsresult& statusCode) { - if (ShouldEnqueue()) { - EnqueueEvent(new StopRequestEvent(this, statusCode)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new StopRequestEvent(this, statusCode)); } else { OnStopRequest(statusCode); } @@ -447,7 +448,7 @@ HttpChannelChild::OnStopRequest(const nsresult& statusCode) { // We must flush the queue before we Send__delete__ // (although we really shouldn't receive any msgs after OnStop), // so make sure this goes out of scope before then. - AutoEventEnqueuer ensureSerialDispatch(this); + AutoEventEnqueuer ensureSerialDispatch(mEventQ); mListener->OnStopRequest(this, mListenerContext, mStatus); @@ -490,8 +491,8 @@ bool HttpChannelChild::RecvOnProgress(const PRUint64& progress, const PRUint64& progressMax) { - if (ShouldEnqueue()) { - EnqueueEvent(new ProgressEvent(this, progress, progressMax)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new ProgressEvent(this, progress, progressMax)); } else { OnProgress(progress, progressMax); } @@ -512,7 +513,7 @@ HttpChannelChild::OnProgress(const PRUint64& progress, if (!mProgressSink) GetCallback(mProgressSink); - AutoEventEnqueuer ensureSerialDispatch(this); + AutoEventEnqueuer ensureSerialDispatch(mEventQ); // block socket status event after Cancel or OnStopRequest has been called, // or if channel has LOAD_BACKGROUND set @@ -543,8 +544,8 @@ class StatusEvent : public ChannelEvent bool HttpChannelChild::RecvOnStatus(const nsresult& status) { - if (ShouldEnqueue()) { - EnqueueEvent(new StatusEvent(this, status)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new StatusEvent(this, status)); } else { OnStatus(status); } @@ -563,7 +564,7 @@ HttpChannelChild::OnStatus(const nsresult& status) if (!mProgressSink) GetCallback(mProgressSink); - AutoEventEnqueuer ensureSerialDispatch(this); + AutoEventEnqueuer ensureSerialDispatch(mEventQ); // block socket status event after Cancel or OnStopRequest has been called, // or if channel has LOAD_BACKGROUND set @@ -593,8 +594,8 @@ class FailedAsyncOpenEvent : public ChannelEvent bool HttpChannelChild::RecvFailedAsyncOpen(const nsresult& status) { - if (ShouldEnqueue()) { - EnqueueEvent(new FailedAsyncOpenEvent(this, status)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new FailedAsyncOpenEvent(this, status)); } else { FailedAsyncOpen(status); } @@ -640,8 +641,8 @@ class DeleteSelfEvent : public ChannelEvent bool HttpChannelChild::RecvDeleteSelf() { - if (ShouldEnqueue()) { - EnqueueEvent(new DeleteSelfEvent(this)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new DeleteSelfEvent(this)); } else { DeleteSelf(); } @@ -687,9 +688,9 @@ HttpChannelChild::RecvRedirect1Begin(const PRUint32& newChannelId, const PRUint32& redirectFlags, const nsHttpResponseHead& responseHead) { - if (ShouldEnqueue()) { - EnqueueEvent(new Redirect1Event(this, newChannelId, newUri, redirectFlags, - responseHead)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new Redirect1Event(this, newChannelId, newUri, + redirectFlags, responseHead)); } else { Redirect1Begin(newChannelId, newUri, redirectFlags, responseHead); } @@ -760,8 +761,8 @@ class Redirect3Event : public ChannelEvent bool HttpChannelChild::RecvRedirect3Complete() { - if (ShouldEnqueue()) { - EnqueueEvent(new Redirect3Event(this)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new Redirect3Event(this)); } else { Redirect3Complete(); } @@ -905,8 +906,10 @@ NS_IMETHODIMP HttpChannelChild::Suspend() { NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE); - SendSuspend(); - mSuspendCount++; + if (!mSuspendCount++) { + SendSuspend(); + mEventQ.Suspend(); + } return NS_OK; } @@ -918,7 +921,9 @@ HttpChannelChild::CompleteResume() mCallOnResume = 0; } - FlushEventQueue(); + // Don't resume event queue until now, else queued events could get + // flushed/called before mCallOnResume, which needs to run first. + mEventQ.Resume(); } NS_IMETHODIMP @@ -929,15 +934,8 @@ HttpChannelChild::Resume() nsresult rv = NS_OK; - SendResume(); - mSuspendCount--; - if (!mSuspendCount) { - // If we were suspended outside of an event handler (bug 595972) we'll - // consider ourselves unqueued. This is a legal state of affairs but - // FlushEventQueue() can't easily ensure this fact, so we'll do some - // fudging to set the invariants correctly. - if (mQueuePhase == PHASE_UNQUEUED) - mQueuePhase = PHASE_FINISHED_QUEUEING; + if (!--mSuspendCount) { + SendResume(); rv = AsyncCall(&HttpChannelChild::CompleteResume); } return rv; diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index 2d8e0b47d434..e5937aa5c797 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -76,7 +76,6 @@ class HttpChannelChild : public PHttpChannelChild , public nsIAssociatedContentSecurity , public nsIChildChannel , public nsIHttpChannelChild - , public ChannelEventQueue { public: NS_DECL_ISUPPORTS_INHERITED @@ -173,6 +172,7 @@ private: bool mIPCOpen; bool mKeptAlive; + ChannelEventQueue mEventQ; void OnStartRequest(const nsHttpResponseHead& responseHead, const PRBool& useResponseHead, diff --git a/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp b/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp index 1ca78209d8ec..1d2e3ef056a4 100644 --- a/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp +++ b/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp @@ -57,8 +57,7 @@ NS_IMPL_ISUPPORTS3(WyciwygChannelChild, WyciwygChannelChild::WyciwygChannelChild() - : ChannelEventQueue(this) - , mStatus(NS_OK) + : mStatus(NS_OK) , mIsPending(PR_FALSE) , mCanceled(false) , mLoadFlags(LOAD_NORMAL) @@ -66,6 +65,7 @@ WyciwygChannelChild::WyciwygChannelChild() , mCharsetSource(kCharsetUninitialized) , mState(WCC_NEW) , mIPCOpen(false) + , mEventQ(this) { LOG(("Creating WyciwygChannelChild @%x\n", this)); } @@ -138,9 +138,10 @@ WyciwygChannelChild::RecvOnStartRequest(const nsresult& statusCode, const nsCString& charset, const nsCString& securityInfo) { - if (ShouldEnqueue()) { - EnqueueEvent(new WyciwygStartRequestEvent(this, statusCode, contentLength, - source, charset, securityInfo)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new WyciwygStartRequestEvent(this, statusCode, + contentLength, source, + charset, securityInfo)); } else { OnStartRequest(statusCode, contentLength, source, charset, securityInfo); } @@ -167,7 +168,7 @@ WyciwygChannelChild::OnStartRequest(const nsresult& statusCode, NS_DeserializeObject(securityInfo, getter_AddRefs(mSecurityInfo)); } - AutoEventEnqueuer ensureSerialDispatch(this); + AutoEventEnqueuer ensureSerialDispatch(mEventQ); nsresult rv = mListener->OnStartRequest(this, mListenerContext); if (NS_FAILED(rv)) @@ -192,8 +193,8 @@ bool WyciwygChannelChild::RecvOnDataAvailable(const nsCString& data, const PRUint32& offset) { - if (ShouldEnqueue()) { - EnqueueEvent(new WyciwygDataAvailableEvent(this, data, offset)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new WyciwygDataAvailableEvent(this, data, offset)); } else { OnDataAvailable(data, offset); } @@ -226,7 +227,7 @@ WyciwygChannelChild::OnDataAvailable(const nsCString& data, return; } - AutoEventEnqueuer ensureSerialDispatch(this); + AutoEventEnqueuer ensureSerialDispatch(mEventQ); rv = mListener->OnDataAvailable(this, mListenerContext, stringStream, offset, data.Length()); @@ -253,8 +254,8 @@ private: bool WyciwygChannelChild::RecvOnStopRequest(const nsresult& statusCode) { - if (ShouldEnqueue()) { - EnqueueEvent(new WyciwygStopRequestEvent(this, statusCode)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new WyciwygStopRequestEvent(this, statusCode)); } else { OnStopRequest(statusCode); } @@ -269,8 +270,8 @@ WyciwygChannelChild::OnStopRequest(const nsresult& statusCode) { // We need to ensure that all IPDL message dispatching occurs // before we delete the protocol below - AutoEventEnqueuer ensureSerialDispatch(this); - + AutoEventEnqueuer ensureSerialDispatch(mEventQ); + mState = WCC_ONSTOP; mIsPending = PR_FALSE; @@ -310,8 +311,8 @@ class WyciwygCancelEvent : public ChannelEvent bool WyciwygChannelChild::RecvCancelEarly(const nsresult& statusCode) { - if (ShouldEnqueue()) { - EnqueueEvent(new WyciwygCancelEvent(this, statusCode)); + if (mEventQ.ShouldEnqueue()) { + mEventQ.Enqueue(new WyciwygCancelEvent(this, statusCode)); } else { CancelEarly(statusCode); } diff --git a/netwerk/protocol/wyciwyg/WyciwygChannelChild.h b/netwerk/protocol/wyciwyg/WyciwygChannelChild.h index e44a69f0daa6..da5682239b1a 100644 --- a/netwerk/protocol/wyciwyg/WyciwygChannelChild.h +++ b/netwerk/protocol/wyciwyg/WyciwygChannelChild.h @@ -67,7 +67,6 @@ enum WyciwygChannelChildState { // Header file contents class WyciwygChannelChild : public PWyciwygChannelChild , public nsIWyciwygChannel - , public ChannelEventQueue { public: NS_DECL_ISUPPORTS @@ -128,6 +127,7 @@ private: enum WyciwygChannelChildState mState; bool mIPCOpen; + ChannelEventQueue mEventQ; friend class WyciwygStartRequestEvent; friend class WyciwygDataAvailableEvent; diff --git a/netwerk/test/unit/test_httpsuspend.js b/netwerk/test/unit/test_httpsuspend.js index 0459c637aaeb..f9d5145f4df7 100644 --- a/netwerk/test/unit/test_httpsuspend.js +++ b/netwerk/test/unit/test_httpsuspend.js @@ -25,7 +25,9 @@ var listener = { // Insert a delay between this and the next callback to ensure message buffering // works correctly request.suspend(); + request.suspend(); do_timeout(RESUME_DELAY, function() request.resume()); + do_timeout(RESUME_DELAY + 1000, function() request.resume()); }, onDataAvailable: function(request, context, stream, offset, count) { @@ -34,6 +36,8 @@ var listener = { // Ensure that suspending and resuming inside a callback works correctly request.suspend(); + request.suspend(); + request.resume(); request.resume(); this._gotData = true;