Bug 660774 - e10s necko: refactor channelEventQueue to allow async resume/flush. r=jdm

This commit is contained in:
Jason Duell 2011-06-11 18:37:09 -07:00
parent c7f6b1cb89
commit aeeb58e404
10 changed files with 280 additions and 178 deletions

View File

@ -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 <josh@joshmatthews.net> (initial developer)
* Jason Duell <jduell.mcbugs@gmail.com>
*
* 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<nsIChannel> 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;
}
}
}

View File

@ -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 <josh@joshmatthews.net> (initial developer)
* Jason Duell <jduell.mcbugs@gmail.com>
*
* 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 <nsTArray.h>
#include <nsAutoPtr.h>
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 T> class AutoEventEnqueuerBase;
class AutoEventEnqueuerBase;
template<class T>
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<nsAutoPtr<ChannelEvent> > 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<T> 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<T>;
nsTArray<nsAutoPtr<ChannelEvent> > mEventQueue;
bool mForced;
bool mSuspended;
bool mFlushing;
// Keep ptr to avoid refcount cycle: only grab ref during flushing.
nsIChannel *mOwner;
friend class AutoEventEnqueuer;
};
template<class T> inline void
ChannelEventQueue<T>::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<class T> inline void
ChannelEventQueue<T>::EndEventQueueing()
{
if (mQueuePhase != PHASE_QUEUEING)
return;
mQueuePhase = PHASE_FINISHED_QUEUEING;
}
template<class T> inline bool
ChannelEventQueue<T>::ShouldEnqueue()
{
return mQueuePhase != PHASE_UNQUEUED || mSelf->IsSuspended();
}
template<class T> inline void
ChannelEventQueue<T>::EnqueueEvent(ChannelEvent* callback)
inline void
ChannelEventQueue::Enqueue(ChannelEvent* callback)
{
mEventQueue.AppendElement(callback);
}
template<class T> void
ChannelEventQueue<T>::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<T> 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 T>
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<T>* queue) : mEventQueue(queue)
{
mEventQueue->BeginEventQueueing();
AutoEventEnqueuer(ChannelEventQueue &queue) : mEventQueue(queue) {
mEventQueue.StartForcedQueueing();
}
~AutoEventEnqueuerBase()
{
mEventQueue->EndEventQueueing();
mEventQueue->FlushEventQueue();
~AutoEventEnqueuer() {
mEventQueue.EndForcedQueueing();
}
private:
ChannelEventQueue<T> *mEventQueue;
ChannelEventQueue &mEventQueue;
};
}

View File

@ -61,6 +61,7 @@ EXPORTS_mozilla/net = \
CPPSRCS = \
NeckoChild.cpp \
NeckoParent.cpp \
ChannelEventQueue.cpp \
$(NULL)
LOCAL_INCLUDES += \

View File

@ -56,8 +56,8 @@ namespace mozilla {
namespace net {
FTPChannelChild::FTPChannelChild(nsIURI* uri)
: ChannelEventQueue<FTPChannelChild>(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;
}

View File

@ -68,7 +68,6 @@ class FTPChannelChild : public PFTPChannelChild
, public nsIResumableChannel
, public nsIProxiedChannel
, public nsIChildChannel
, public ChannelEventQueue<FTPChannelChild>
{
public:
typedef ::nsIStreamListener nsIStreamListener;
@ -137,6 +136,7 @@ private:
nsCOMPtr<nsIInputStream> mUploadStream;
bool mIPCOpen;
ChannelEventQueue mEventQ;
bool mCanceled;
PRUint32 mSuspendCount;
PRPackedBool mIsPending;

View File

@ -60,13 +60,13 @@ namespace net {
HttpChannelChild::HttpChannelChild()
: HttpAsyncAborter<HttpChannelChild>(this)
, ChannelEventQueue<HttpChannelChild>(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;

View File

@ -76,7 +76,6 @@ class HttpChannelChild : public PHttpChannelChild
, public nsIAssociatedContentSecurity
, public nsIChildChannel
, public nsIHttpChannelChild
, public ChannelEventQueue<HttpChannelChild>
{
public:
NS_DECL_ISUPPORTS_INHERITED
@ -173,6 +172,7 @@ private:
bool mIPCOpen;
bool mKeptAlive;
ChannelEventQueue mEventQ;
void OnStartRequest(const nsHttpResponseHead& responseHead,
const PRBool& useResponseHead,

View File

@ -57,8 +57,7 @@ NS_IMPL_ISUPPORTS3(WyciwygChannelChild,
WyciwygChannelChild::WyciwygChannelChild()
: ChannelEventQueue<WyciwygChannelChild>(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);
}

View File

@ -67,7 +67,6 @@ enum WyciwygChannelChildState {
// Header file contents
class WyciwygChannelChild : public PWyciwygChannelChild
, public nsIWyciwygChannel
, public ChannelEventQueue<WyciwygChannelChild>
{
public:
NS_DECL_ISUPPORTS
@ -128,6 +127,7 @@ private:
enum WyciwygChannelChildState mState;
bool mIPCOpen;
ChannelEventQueue mEventQ;
friend class WyciwygStartRequestEvent;
friend class WyciwygDataAvailableEvent;

View File

@ -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;