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 : /* ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at:
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla Code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * The Mozilla Foundation
21 : * Portions created by the Initial Developer are Copyright (C) 2010
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Josh Matthews <josh@joshmatthews.net> (initial developer)
26 : * Jason Duell <jduell.mcbugs@gmail.com>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either the GNU General Public License Version 2 or later (the "GPL"), or
30 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #ifndef mozilla_net_ChannelEventQueue_h
43 : #define mozilla_net_ChannelEventQueue_h
44 :
45 : #include <nsTArray.h>
46 : #include <nsAutoPtr.h>
47 :
48 : class nsISupports;
49 :
50 : namespace mozilla {
51 : namespace net {
52 :
53 : class ChannelEvent
54 : {
55 : public:
56 0 : ChannelEvent() { MOZ_COUNT_CTOR(ChannelEvent); }
57 0 : virtual ~ChannelEvent() { MOZ_COUNT_DTOR(ChannelEvent); }
58 : virtual void Run() = 0;
59 : };
60 :
61 : // Workaround for Necko re-entrancy dangers. We buffer IPDL messages in a
62 : // queue if still dispatching previous one(s) to listeners/observers.
63 : // Otherwise synchronous XMLHttpRequests and/or other code that spins the
64 : // event loop (ex: IPDL rpc) could cause listener->OnDataAvailable (for
65 : // instance) to be dispatched and called before mListener->OnStartRequest has
66 : // completed.
67 :
68 : class AutoEventEnqueuerBase;
69 :
70 : class ChannelEventQueue
71 : {
72 : public:
73 0 : ChannelEventQueue(nsISupports *owner)
74 : : mForced(false)
75 : , mSuspended(false)
76 : , mFlushing(false)
77 0 : , mOwner(owner) {}
78 :
79 0 : ~ChannelEventQueue() {}
80 :
81 : // Checks to determine if an IPDL-generated channel event can be processed
82 : // immediately, or needs to be queued using Enqueue().
83 : inline bool ShouldEnqueue();
84 :
85 : // Puts IPDL-generated channel event into queue, to be run later
86 : // automatically when EndForcedQueueing and/or Resume is called.
87 : inline void Enqueue(ChannelEvent* callback);
88 :
89 : // After StartForcedQueueing is called, ShouldEnqueue() will return true and
90 : // no events will be run/flushed until EndForcedQueueing is called.
91 : // - Note: queueing may still be required after EndForcedQueueing() (if the
92 : // queue is suspended, etc): always call ShouldEnqueue() to determine
93 : // whether queueing is needed.
94 : inline void StartForcedQueueing();
95 : inline void EndForcedQueueing();
96 :
97 : // Suspend/resume event queue. ShouldEnqueue() will return true and no events
98 : // will be run/flushed until resume is called. These should be called when
99 : // the channel owning the event queue is suspended/resumed.
100 : // - Note: these suspend/resume functions are NOT meant to be called
101 : // recursively: call them only at initial suspend, and actual resume).
102 : // - Note: Resume flushes the queue and invokes any pending callbacks
103 : // immediately--caller must arrange any needed asynchronicity vis a vis
104 : // the channel's own Resume() method.
105 : inline void Suspend();
106 : inline void Resume();
107 :
108 : private:
109 : inline void MaybeFlushQueue();
110 : void FlushQueue();
111 :
112 : nsTArray<nsAutoPtr<ChannelEvent> > mEventQueue;
113 :
114 : bool mForced;
115 : bool mSuspended;
116 : bool mFlushing;
117 :
118 : // Keep ptr to avoid refcount cycle: only grab ref during flushing.
119 : nsISupports *mOwner;
120 :
121 : friend class AutoEventEnqueuer;
122 : };
123 :
124 : inline bool
125 0 : ChannelEventQueue::ShouldEnqueue()
126 : {
127 0 : bool answer = mForced || mSuspended || mFlushing;
128 :
129 0 : NS_ABORT_IF_FALSE(answer == true || mEventQueue.IsEmpty(),
130 : "Should always enqueue if ChannelEventQueue not empty");
131 :
132 0 : return answer;
133 : }
134 :
135 : inline void
136 0 : ChannelEventQueue::Enqueue(ChannelEvent* callback)
137 : {
138 0 : mEventQueue.AppendElement(callback);
139 0 : }
140 :
141 : inline void
142 0 : ChannelEventQueue::StartForcedQueueing()
143 : {
144 0 : mForced = true;
145 0 : }
146 :
147 : inline void
148 0 : ChannelEventQueue::EndForcedQueueing()
149 : {
150 0 : mForced = false;
151 0 : MaybeFlushQueue();
152 0 : }
153 :
154 : inline void
155 0 : ChannelEventQueue::Suspend()
156 : {
157 0 : NS_ABORT_IF_FALSE(!mSuspended,
158 : "ChannelEventQueue::Suspend called recursively");
159 :
160 0 : mSuspended = true;
161 0 : }
162 :
163 : inline void
164 0 : ChannelEventQueue::Resume()
165 : {
166 0 : NS_ABORT_IF_FALSE(mSuspended,
167 : "ChannelEventQueue::Resume called when not suspended!");
168 :
169 0 : mSuspended = false;
170 0 : MaybeFlushQueue();
171 0 : }
172 :
173 : inline void
174 0 : ChannelEventQueue::MaybeFlushQueue()
175 : {
176 : // Don't flush if forced queuing on, we're already being flushed, or
177 : // suspended, or there's nothing to flush
178 0 : if (!mForced && !mFlushing && !mSuspended && !mEventQueue.IsEmpty())
179 0 : FlushQueue();
180 0 : }
181 :
182 : // Ensures that ShouldEnqueue() will be true during its lifetime (letting
183 : // caller know incoming IPDL msgs should be queued). Flushes the queue when it
184 : // goes out of scope.
185 : class AutoEventEnqueuer
186 : {
187 : public:
188 0 : AutoEventEnqueuer(ChannelEventQueue &queue) : mEventQueue(queue) {
189 0 : mEventQueue.StartForcedQueueing();
190 0 : }
191 0 : ~AutoEventEnqueuer() {
192 0 : mEventQueue.EndForcedQueueing();
193 0 : }
194 : private:
195 : ChannelEventQueue &mEventQueue;
196 : };
197 :
198 : }
199 : }
200 :
201 : #endif
|