Bumping manifests a=b2g-bump
[gecko.git] / netwerk / ipc / ChannelEventQueue.h
blob3b654a4b566007a8e75c1ef5c36bfc928781705d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set sw=2 ts=8 et tw=80 :
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #ifndef mozilla_net_ChannelEventQueue_h
9 #define mozilla_net_ChannelEventQueue_h
11 #include <nsTArray.h>
12 #include <nsAutoPtr.h>
14 class nsISupports;
15 class nsIEventTarget;
16 class nsIThread;
18 namespace mozilla {
19 namespace net {
21 class ChannelEvent
23 public:
24 ChannelEvent() { MOZ_COUNT_CTOR(ChannelEvent); }
25 virtual ~ChannelEvent() { MOZ_COUNT_DTOR(ChannelEvent); }
26 virtual void Run() = 0;
29 // Workaround for Necko re-entrancy dangers. We buffer IPDL messages in a
30 // queue if still dispatching previous one(s) to listeners/observers.
31 // Otherwise synchronous XMLHttpRequests and/or other code that spins the
32 // event loop (ex: IPDL rpc) could cause listener->OnDataAvailable (for
33 // instance) to be dispatched and called before mListener->OnStartRequest has
34 // completed.
36 class AutoEventEnqueuerBase;
38 class ChannelEventQueue MOZ_FINAL
40 NS_INLINE_DECL_REFCOUNTING(ChannelEventQueue)
42 public:
43 explicit ChannelEventQueue(nsISupports *owner)
44 : mSuspendCount(0)
45 , mSuspended(false)
46 , mForced(false)
47 , mFlushing(false)
48 , mOwner(owner) {}
50 // Checks to determine if an IPDL-generated channel event can be processed
51 // immediately, or needs to be queued using Enqueue().
52 inline bool ShouldEnqueue();
54 // Puts IPDL-generated channel event into queue, to be run later
55 // automatically when EndForcedQueueing and/or Resume is called.
56 inline void Enqueue(ChannelEvent* callback);
58 // After StartForcedQueueing is called, ShouldEnqueue() will return true and
59 // no events will be run/flushed until EndForcedQueueing is called.
60 // - Note: queueing may still be required after EndForcedQueueing() (if the
61 // queue is suspended, etc): always call ShouldEnqueue() to determine
62 // whether queueing is needed.
63 inline void StartForcedQueueing();
64 inline void EndForcedQueueing();
66 // Suspend/resume event queue. ShouldEnqueue() will return true and no events
67 // will be run/flushed until resume is called. These should be called when
68 // the channel owning the event queue is suspended/resumed.
69 inline void Suspend();
70 // Resume flushes the queue asynchronously, i.e. items in queue will be
71 // dispatched in a new event on the current thread.
72 void Resume();
74 // Retargets delivery of events to the target thread specified.
75 nsresult RetargetDeliveryTo(nsIEventTarget* aTargetThread);
77 private:
78 // Private destructor, to discourage deletion outside of Release():
79 ~ChannelEventQueue()
83 inline void MaybeFlushQueue();
84 void FlushQueue();
85 inline void CompleteResume();
87 nsTArray<nsAutoPtr<ChannelEvent> > mEventQueue;
89 uint32_t mSuspendCount;
90 bool mSuspended;
91 bool mForced;
92 bool mFlushing;
94 // Keep ptr to avoid refcount cycle: only grab ref during flushing.
95 nsISupports *mOwner;
97 // Target thread for delivery of events.
98 nsCOMPtr<nsIThread> mTargetThread;
100 friend class AutoEventEnqueuer;
103 inline bool
104 ChannelEventQueue::ShouldEnqueue()
106 bool answer = mForced || mSuspended || mFlushing;
108 NS_ABORT_IF_FALSE(answer == true || mEventQueue.IsEmpty(),
109 "Should always enqueue if ChannelEventQueue not empty");
111 return answer;
114 inline void
115 ChannelEventQueue::Enqueue(ChannelEvent* callback)
117 mEventQueue.AppendElement(callback);
120 inline void
121 ChannelEventQueue::StartForcedQueueing()
123 mForced = true;
126 inline void
127 ChannelEventQueue::EndForcedQueueing()
129 mForced = false;
130 MaybeFlushQueue();
133 inline void
134 ChannelEventQueue::Suspend()
136 mSuspended = true;
137 mSuspendCount++;
140 inline void
141 ChannelEventQueue::CompleteResume()
143 // channel may have been suspended again since Resume fired event to call this.
144 if (!mSuspendCount) {
145 // we need to remain logically suspended (for purposes of queuing incoming
146 // messages) until this point, else new incoming messages could run before
147 // queued ones.
148 mSuspended = false;
149 MaybeFlushQueue();
153 inline void
154 ChannelEventQueue::MaybeFlushQueue()
156 // Don't flush if forced queuing on, we're already being flushed, or
157 // suspended, or there's nothing to flush
158 if (!mForced && !mFlushing && !mSuspended && !mEventQueue.IsEmpty())
159 FlushQueue();
162 // Ensures that ShouldEnqueue() will be true during its lifetime (letting
163 // caller know incoming IPDL msgs should be queued). Flushes the queue when it
164 // goes out of scope.
165 class AutoEventEnqueuer
167 public:
168 explicit AutoEventEnqueuer(ChannelEventQueue *queue) : mEventQueue(queue) {
169 mEventQueue->StartForcedQueueing();
171 ~AutoEventEnqueuer() {
172 mEventQueue->EndForcedQueueing();
174 private:
175 ChannelEventQueue* mEventQueue;
181 #endif