Bug 536289 - Generalize IPDL event queue some more. r=jduell a=blocking-fennec

This commit is contained in:
Josh Matthews 2010-10-16 01:26:14 -04:00
Родитель 31b159651d
Коммит 10b39d0f10
3 изменённых файлов: 70 добавлений и 66 удалений

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

@ -58,10 +58,14 @@ class ChannelEvent
// event loop (ex: IPDL rpc) could cause listener->OnDataAvailable (for
// instance) to be called before mListener->OnStartRequest has completed.
template<class T> class AutoEventEnqueuerBase;
template<class T>
class ChannelEventQueue
{
public:
ChannelEventQueue() : mQueuePhase(PHASE_UNQUEUED) {}
ChannelEventQueue(T* self) : mQueuePhase(PHASE_UNQUEUED)
, mSelf(self) {}
~ChannelEventQueue() {}
protected:
@ -69,10 +73,7 @@ class ChannelEventQueue
void EndEventQueueing();
void EnqueueEvent(ChannelEvent* callback);
bool ShouldEnqueue();
// Consumers must implement their own flushing routine, as there are too many
// implementation-specific details to generalize easily.
virtual void FlushEventQueue() = 0;
void FlushEventQueue();
nsTArray<nsAutoPtr<ChannelEvent> > mEventQueue;
enum {
@ -82,11 +83,16 @@ class ChannelEventQueue
PHASE_FLUSHING
} mQueuePhase;
friend class AutoEventEnqueuer;
typedef AutoEventEnqueuerBase<T> AutoEventEnqueuer;
private:
T* mSelf;
friend class AutoEventEnqueuerBase<T>;
};
inline void
ChannelEventQueue::BeginEventQueueing()
template<class T> inline void
ChannelEventQueue<T>::BeginEventQueueing()
{
if (mQueuePhase != PHASE_UNQUEUED)
return;
@ -94,8 +100,8 @@ ChannelEventQueue::BeginEventQueueing()
mQueuePhase = PHASE_QUEUEING;
}
inline void
ChannelEventQueue::EndEventQueueing()
template<class T> inline void
ChannelEventQueue<T>::EndEventQueueing()
{
if (mQueuePhase != PHASE_QUEUEING)
return;
@ -103,34 +109,72 @@ ChannelEventQueue::EndEventQueueing()
mQueuePhase = PHASE_FINISHED_QUEUEING;
}
inline bool
ChannelEventQueue::ShouldEnqueue()
template<class T> inline bool
ChannelEventQueue<T>::ShouldEnqueue()
{
return mQueuePhase != PHASE_UNQUEUED;
return mQueuePhase != PHASE_UNQUEUED || mSelf->IsSuspended();
}
inline void
ChannelEventQueue::EnqueueEvent(ChannelEvent* callback)
template<class T> inline void
ChannelEventQueue<T>::EnqueueEvent(ChannelEvent* callback)
{
mEventQueue.AppendElement(callback);
}
template<class T> void
ChannelEventQueue<T>::FlushEventQueue()
{
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;
}
// Ensures any incoming IPDL msgs are queued during its lifetime, and flushes
// the queue when it goes out of scope.
class AutoEventEnqueuer
template<class T>
class AutoEventEnqueuerBase
{
public:
AutoEventEnqueuer(ChannelEventQueue* queue) : mEventQueue(queue)
AutoEventEnqueuerBase(ChannelEventQueue<T>* queue) : mEventQueue(queue)
{
mEventQueue->BeginEventQueueing();
}
~AutoEventEnqueuer()
~AutoEventEnqueuerBase()
{
mEventQueue->EndEventQueueing();
mEventQueue->FlushEventQueue();
}
private:
ChannelEventQueue *mEventQueue;
ChannelEventQueue<T> *mEventQueue;
};
}

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

@ -59,7 +59,8 @@ namespace net {
//-----------------------------------------------------------------------------
HttpChannelChild::HttpChannelChild()
: mIsFromCache(PR_FALSE)
: ChannelEventQueue<HttpChannelChild>(this)
, mIsFromCache(PR_FALSE)
, mCacheEntryAvailable(PR_FALSE)
, mCacheExpirationTime(nsICache::NO_EXPIRATION_TIME)
, mSendResumeAt(false)
@ -141,43 +142,6 @@ HttpChannelChild::ReleaseIPDLReference()
Release();
}
void
HttpChannelChild::FlushEventQueue()
{
NS_ABORT_IF_FALSE(mQueuePhase != PHASE_UNQUEUED,
"Queue flushing should not occur if PHASE_UNQUEUED");
// Queue already being flushed, or the channel's suspended.
if (mQueuePhase != PHASE_FINISHED_QUEUEING || mSuspendCount)
return;
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;
nsRefPtr<HttpChannelChild> kungFuDeathGrip(this);
PRUint32 i;
for (i = 0; i < mEventQueue.Length(); i++) {
mEventQueue[i]->Run();
// If the callback ended up suspending us, abort all further flushing.
if (mSuspendCount)
break;
}
// We will always want to remove at least one finished callback.
if (i < mEventQueue.Length())
i++;
mEventQueue.RemoveElementsAt(0, i);
}
if (mSuspendCount)
mQueuePhase = PHASE_QUEUEING;
else
mQueuePhase = PHASE_UNQUEUED;
}
class StartRequestEvent : public ChannelEvent
{
public:

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

@ -65,8 +65,6 @@
namespace mozilla {
namespace net {
class ChildChannelEvent;
class HttpChannelChild : public PHttpChannelChild
, public HttpBaseChannel
, public nsICacheInfoChannel
@ -75,7 +73,7 @@ class HttpChannelChild : public PHttpChannelChild
, public nsIApplicationCacheChannel
, public nsIAsyncVerifyRedirectCallback
, public nsIAssociatedContentSecurity
, public ChannelEventQueue
, public ChannelEventQueue<HttpChannelChild>
{
public:
NS_DECL_ISUPPORTS_INHERITED
@ -120,6 +118,8 @@ public:
void AddIPDLReference();
void ReleaseIPDLReference();
bool IsSuspended();
protected:
bool RecvOnStartRequest(const nsHttpResponseHead& responseHead,
const PRBool& useResponseHead,
@ -164,9 +164,6 @@ private:
bool mIPCOpen;
bool mKeptAlive;
void FlushEventQueue();
bool ShouldEnqueue();
void OnStartRequest(const nsHttpResponseHead& responseHead,
const PRBool& useResponseHead,
const RequestHeaderTuples& requestHeaders,
@ -188,7 +185,6 @@ private:
void Redirect3Complete();
void DeleteSelf();
friend class AutoEventEnqueuer;
friend class StartRequestEvent;
friend class StopRequestEvent;
friend class DataAvailableEvent;
@ -205,9 +201,9 @@ private:
//-----------------------------------------------------------------------------
inline bool
HttpChannelChild::ShouldEnqueue()
HttpChannelChild::IsSuspended()
{
return ChannelEventQueue::ShouldEnqueue() || mSuspendCount;
return mSuspendCount != 0;
}
} // namespace net