1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/ipc/ChannelEventQueue.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,181 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * vim: set sw=2 ts=8 et tw=80 : 1.6 + */ 1.7 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.10 + 1.11 +#ifndef mozilla_net_ChannelEventQueue_h 1.12 +#define mozilla_net_ChannelEventQueue_h 1.13 + 1.14 +#include <nsTArray.h> 1.15 +#include <nsAutoPtr.h> 1.16 + 1.17 +class nsISupports; 1.18 +class nsIEventTarget; 1.19 +class nsIThread; 1.20 + 1.21 +namespace mozilla { 1.22 +namespace net { 1.23 + 1.24 +class ChannelEvent 1.25 +{ 1.26 + public: 1.27 + ChannelEvent() { MOZ_COUNT_CTOR(ChannelEvent); } 1.28 + virtual ~ChannelEvent() { MOZ_COUNT_DTOR(ChannelEvent); } 1.29 + virtual void Run() = 0; 1.30 +}; 1.31 + 1.32 +// Workaround for Necko re-entrancy dangers. We buffer IPDL messages in a 1.33 +// queue if still dispatching previous one(s) to listeners/observers. 1.34 +// Otherwise synchronous XMLHttpRequests and/or other code that spins the 1.35 +// event loop (ex: IPDL rpc) could cause listener->OnDataAvailable (for 1.36 +// instance) to be dispatched and called before mListener->OnStartRequest has 1.37 +// completed. 1.38 + 1.39 +class AutoEventEnqueuerBase; 1.40 + 1.41 +class ChannelEventQueue MOZ_FINAL 1.42 +{ 1.43 + NS_INLINE_DECL_REFCOUNTING(ChannelEventQueue) 1.44 + 1.45 + public: 1.46 + ChannelEventQueue(nsISupports *owner) 1.47 + : mSuspendCount(0) 1.48 + , mSuspended(false) 1.49 + , mForced(false) 1.50 + , mFlushing(false) 1.51 + , mOwner(owner) {} 1.52 + 1.53 + // Checks to determine if an IPDL-generated channel event can be processed 1.54 + // immediately, or needs to be queued using Enqueue(). 1.55 + inline bool ShouldEnqueue(); 1.56 + 1.57 + // Puts IPDL-generated channel event into queue, to be run later 1.58 + // automatically when EndForcedQueueing and/or Resume is called. 1.59 + inline void Enqueue(ChannelEvent* callback); 1.60 + 1.61 + // After StartForcedQueueing is called, ShouldEnqueue() will return true and 1.62 + // no events will be run/flushed until EndForcedQueueing is called. 1.63 + // - Note: queueing may still be required after EndForcedQueueing() (if the 1.64 + // queue is suspended, etc): always call ShouldEnqueue() to determine 1.65 + // whether queueing is needed. 1.66 + inline void StartForcedQueueing(); 1.67 + inline void EndForcedQueueing(); 1.68 + 1.69 + // Suspend/resume event queue. ShouldEnqueue() will return true and no events 1.70 + // will be run/flushed until resume is called. These should be called when 1.71 + // the channel owning the event queue is suspended/resumed. 1.72 + inline void Suspend(); 1.73 + // Resume flushes the queue asynchronously, i.e. items in queue will be 1.74 + // dispatched in a new event on the current thread. 1.75 + void Resume(); 1.76 + 1.77 + // Retargets delivery of events to the target thread specified. 1.78 + nsresult RetargetDeliveryTo(nsIEventTarget* aTargetThread); 1.79 + 1.80 + private: 1.81 + // Private destructor, to discourage deletion outside of Release(): 1.82 + ~ChannelEventQueue() 1.83 + { 1.84 + } 1.85 + 1.86 + inline void MaybeFlushQueue(); 1.87 + void FlushQueue(); 1.88 + inline void CompleteResume(); 1.89 + 1.90 + nsTArray<nsAutoPtr<ChannelEvent> > mEventQueue; 1.91 + 1.92 + uint32_t mSuspendCount; 1.93 + bool mSuspended; 1.94 + bool mForced; 1.95 + bool mFlushing; 1.96 + 1.97 + // Keep ptr to avoid refcount cycle: only grab ref during flushing. 1.98 + nsISupports *mOwner; 1.99 + 1.100 + // Target thread for delivery of events. 1.101 + nsCOMPtr<nsIThread> mTargetThread; 1.102 + 1.103 + friend class AutoEventEnqueuer; 1.104 +}; 1.105 + 1.106 +inline bool 1.107 +ChannelEventQueue::ShouldEnqueue() 1.108 +{ 1.109 + bool answer = mForced || mSuspended || mFlushing; 1.110 + 1.111 + NS_ABORT_IF_FALSE(answer == true || mEventQueue.IsEmpty(), 1.112 + "Should always enqueue if ChannelEventQueue not empty"); 1.113 + 1.114 + return answer; 1.115 +} 1.116 + 1.117 +inline void 1.118 +ChannelEventQueue::Enqueue(ChannelEvent* callback) 1.119 +{ 1.120 + mEventQueue.AppendElement(callback); 1.121 +} 1.122 + 1.123 +inline void 1.124 +ChannelEventQueue::StartForcedQueueing() 1.125 +{ 1.126 + mForced = true; 1.127 +} 1.128 + 1.129 +inline void 1.130 +ChannelEventQueue::EndForcedQueueing() 1.131 +{ 1.132 + mForced = false; 1.133 + MaybeFlushQueue(); 1.134 +} 1.135 + 1.136 +inline void 1.137 +ChannelEventQueue::Suspend() 1.138 +{ 1.139 + mSuspended = true; 1.140 + mSuspendCount++; 1.141 +} 1.142 + 1.143 +inline void 1.144 +ChannelEventQueue::CompleteResume() 1.145 +{ 1.146 + // channel may have been suspended again since Resume fired event to call this. 1.147 + if (!mSuspendCount) { 1.148 + // we need to remain logically suspended (for purposes of queuing incoming 1.149 + // messages) until this point, else new incoming messages could run before 1.150 + // queued ones. 1.151 + mSuspended = false; 1.152 + MaybeFlushQueue(); 1.153 + } 1.154 +} 1.155 + 1.156 +inline void 1.157 +ChannelEventQueue::MaybeFlushQueue() 1.158 +{ 1.159 + // Don't flush if forced queuing on, we're already being flushed, or 1.160 + // suspended, or there's nothing to flush 1.161 + if (!mForced && !mFlushing && !mSuspended && !mEventQueue.IsEmpty()) 1.162 + FlushQueue(); 1.163 +} 1.164 + 1.165 +// Ensures that ShouldEnqueue() will be true during its lifetime (letting 1.166 +// caller know incoming IPDL msgs should be queued). Flushes the queue when it 1.167 +// goes out of scope. 1.168 +class AutoEventEnqueuer 1.169 +{ 1.170 + public: 1.171 + AutoEventEnqueuer(ChannelEventQueue *queue) : mEventQueue(queue) { 1.172 + mEventQueue->StartForcedQueueing(); 1.173 + } 1.174 + ~AutoEventEnqueuer() { 1.175 + mEventQueue->EndForcedQueueing(); 1.176 + } 1.177 + private: 1.178 + ChannelEventQueue* mEventQueue; 1.179 +}; 1.180 + 1.181 +} 1.182 +} 1.183 + 1.184 +#endif