Bug 1690340 - Part 5: Remove the menu separators from the developer tools menu. r...
[gecko.git] / netwerk / ipc / ChannelEventQueue.h
blob75df2d15a03e489b41a50b3eeb71b578b865ca91
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 "nsIEventTarget.h"
13 #include "nsThreadUtils.h"
14 #include "nsXULAppAPI.h"
15 #include "mozilla/DebugOnly.h"
16 #include "mozilla/Mutex.h"
17 #include "mozilla/RecursiveMutex.h"
18 #include "mozilla/UniquePtr.h"
19 #include "mozilla/Unused.h"
21 class nsISupports;
23 namespace mozilla {
24 namespace net {
26 class ChannelEvent {
27 public:
28 MOZ_COUNTED_DEFAULT_CTOR(ChannelEvent)
29 MOZ_COUNTED_DTOR_VIRTUAL(ChannelEvent) virtual void Run() = 0;
30 virtual already_AddRefed<nsIEventTarget> GetEventTarget() = 0;
33 // Note that MainThreadChannelEvent should not be used in child process since
34 // GetEventTarget() directly returns an unlabeled event target.
35 class MainThreadChannelEvent : public ChannelEvent {
36 public:
37 MOZ_COUNTED_DEFAULT_CTOR(MainThreadChannelEvent)
38 MOZ_COUNTED_DTOR_OVERRIDE(MainThreadChannelEvent)
40 already_AddRefed<nsIEventTarget> GetEventTarget() override {
41 MOZ_ASSERT(XRE_IsParentProcess());
43 return do_AddRef(GetMainThreadEventTarget());
47 class ChannelFunctionEvent : public ChannelEvent {
48 public:
49 ChannelFunctionEvent(
50 std::function<already_AddRefed<nsIEventTarget>()>&& aGetEventTarget,
51 std::function<void()>&& aCallback)
52 : mGetEventTarget(std::move(aGetEventTarget)),
53 mCallback(std::move(aCallback)) {}
55 void Run() override { mCallback(); }
56 already_AddRefed<nsIEventTarget> GetEventTarget() override {
57 return mGetEventTarget();
60 private:
61 const std::function<already_AddRefed<nsIEventTarget>()> mGetEventTarget;
62 const std::function<void()> mCallback;
65 // UnsafePtr is a work-around our static analyzer that requires all
66 // ref-counted objects to be captured in lambda via a RefPtr
67 // The ChannelEventQueue makes it safe to capture "this" by pointer only.
68 // This is required as work-around to prevent cycles until bug 1596295
69 // is resolved.
70 template <typename T>
71 class UnsafePtr {
72 public:
73 explicit UnsafePtr(T* aPtr) : mPtr(aPtr) {}
75 T& operator*() const { return *mPtr; }
76 T* operator->() const {
77 MOZ_ASSERT(mPtr, "dereferencing a null pointer");
78 return mPtr;
80 operator T*() const& { return mPtr; }
81 explicit operator bool() const { return mPtr != nullptr; }
83 private:
84 T* const mPtr;
87 class NeckoTargetChannelFunctionEvent : public ChannelFunctionEvent {
88 public:
89 template <typename T>
90 NeckoTargetChannelFunctionEvent(T* aChild, std::function<void()>&& aCallback)
91 : ChannelFunctionEvent(
92 [child = UnsafePtr<T>(aChild)]() {
93 MOZ_ASSERT(child);
94 return child->GetNeckoTarget();
96 std::move(aCallback)) {}
99 // Workaround for Necko re-entrancy dangers. We buffer IPDL messages in a
100 // queue if still dispatching previous one(s) to listeners/observers.
101 // Otherwise synchronous XMLHttpRequests and/or other code that spins the
102 // event loop (ex: IPDL rpc) could cause listener->OnDataAvailable (for
103 // instance) to be dispatched and called before mListener->OnStartRequest has
104 // completed.
106 class ChannelEventQueue final {
107 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ChannelEventQueue)
109 public:
110 explicit ChannelEventQueue(nsISupports* owner)
111 : mSuspendCount(0),
112 mSuspended(false),
113 mForcedCount(0),
114 mFlushing(false),
115 mHasCheckedForXMLHttpRequest(false),
116 mForXMLHttpRequest(false),
117 mOwner(owner),
118 mMutex("ChannelEventQueue::mMutex"),
119 mRunningMutex("ChannelEventQueue::mRunningMutex") {}
121 // Puts IPDL-generated channel event into queue, to be run later
122 // automatically when EndForcedQueueing and/or Resume is called.
124 // @param aCallback - the ChannelEvent
125 // @param aAssertionWhenNotQueued - this optional param will be used in an
126 // assertion when the event is executed directly.
127 inline void RunOrEnqueue(ChannelEvent* aCallback,
128 bool aAssertionWhenNotQueued = false);
130 // Append ChannelEvent in front of the event queue.
131 inline void PrependEvent(UniquePtr<ChannelEvent>&& aEvent);
132 inline void PrependEvents(nsTArray<UniquePtr<ChannelEvent>>& aEvents);
134 // After StartForcedQueueing is called, RunOrEnqueue() will start enqueuing
135 // events that will be run/flushed when EndForcedQueueing is called.
136 // - Note: queueing may still be required after EndForcedQueueing() (if the
137 // queue is suspended, etc): always call RunOrEnqueue() to avoid race
138 // conditions.
139 inline void StartForcedQueueing();
140 inline void EndForcedQueueing();
142 // Suspend/resume event queue. RunOrEnqueue() will start enqueuing
143 // events and they will be run/flushed when resume is called. These should be
144 // called when the channel owning the event queue is suspended/resumed.
145 void Suspend();
146 // Resume flushes the queue asynchronously, i.e. items in queue will be
147 // dispatched in a new event on the current thread.
148 void Resume();
150 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
151 bool IsEmpty() const { return mEventQueue.IsEmpty(); }
152 #endif
154 private:
155 // Private destructor, to discourage deletion outside of Release():
156 ~ChannelEventQueue() = default;
158 void SuspendInternal();
159 void ResumeInternal();
161 bool MaybeSuspendIfEventsAreSuppressed();
163 inline void MaybeFlushQueue();
164 void FlushQueue();
165 inline void CompleteResume();
167 ChannelEvent* TakeEvent();
169 nsTArray<UniquePtr<ChannelEvent>> mEventQueue;
171 uint32_t mSuspendCount;
172 bool mSuspended;
173 uint32_t mForcedCount; // Support ForcedQueueing on multiple thread.
174 bool mFlushing;
176 // Whether the queue is associated with an XHR. This is lazily instantiated
177 // the first time it is needed.
178 bool mHasCheckedForXMLHttpRequest;
179 bool mForXMLHttpRequest;
181 // Keep ptr to avoid refcount cycle: only grab ref during flushing.
182 nsISupports* mOwner;
184 // For atomic mEventQueue operation and state update
185 Mutex mMutex;
187 // To guarantee event execution order among threads
188 RecursiveMutex mRunningMutex;
190 friend class AutoEventEnqueuer;
193 inline void ChannelEventQueue::RunOrEnqueue(ChannelEvent* aCallback,
194 bool aAssertionWhenNotQueued) {
195 MOZ_ASSERT(aCallback);
197 // Events execution could be a destruction of the channel (and our own
198 // destructor) unless we make sure its refcount doesn't drop to 0 while this
199 // method is running.
200 nsCOMPtr<nsISupports> kungFuDeathGrip(mOwner);
201 Unused << kungFuDeathGrip; // Not used in this function
203 // To avoid leaks.
204 UniquePtr<ChannelEvent> event(aCallback);
206 // To guarantee that the running event and all the events generated within
207 // it will be finished before events on other threads.
208 RecursiveMutexAutoLock lock(mRunningMutex);
211 MutexAutoLock lock(mMutex);
213 bool enqueue = !!mForcedCount || mSuspended || mFlushing ||
214 !mEventQueue.IsEmpty() ||
215 MaybeSuspendIfEventsAreSuppressed();
217 if (enqueue) {
218 mEventQueue.AppendElement(std::move(event));
219 return;
222 nsCOMPtr<nsIEventTarget> target = event->GetEventTarget();
223 MOZ_ASSERT(target);
225 bool isCurrentThread = false;
226 DebugOnly<nsresult> rv = target->IsOnCurrentThread(&isCurrentThread);
227 MOZ_ASSERT(NS_SUCCEEDED(rv));
229 if (!isCurrentThread) {
230 // Leverage Suspend/Resume mechanism to trigger flush procedure without
231 // creating a new one.
232 SuspendInternal();
233 mEventQueue.AppendElement(std::move(event));
234 ResumeInternal();
235 return;
239 MOZ_RELEASE_ASSERT(!aAssertionWhenNotQueued);
240 event->Run();
243 inline void ChannelEventQueue::StartForcedQueueing() {
244 MutexAutoLock lock(mMutex);
245 ++mForcedCount;
248 inline void ChannelEventQueue::EndForcedQueueing() {
249 bool tryFlush = false;
251 MutexAutoLock lock(mMutex);
252 MOZ_ASSERT(mForcedCount > 0);
253 if (!--mForcedCount) {
254 tryFlush = true;
258 if (tryFlush) {
259 MaybeFlushQueue();
263 inline void ChannelEventQueue::PrependEvent(UniquePtr<ChannelEvent>&& aEvent) {
264 MutexAutoLock lock(mMutex);
266 // Prepending event while no queue flush foreseen might cause the following
267 // channel events not run. This assertion here guarantee there must be a
268 // queue flush, either triggered by Resume or EndForcedQueueing, to execute
269 // the added event.
270 MOZ_ASSERT(mSuspended || !!mForcedCount);
272 mEventQueue.InsertElementAt(0, std::move(aEvent));
275 inline void ChannelEventQueue::PrependEvents(
276 nsTArray<UniquePtr<ChannelEvent>>& aEvents) {
277 MutexAutoLock lock(mMutex);
279 // Prepending event while no queue flush foreseen might cause the following
280 // channel events not run. This assertion here guarantee there must be a
281 // queue flush, either triggered by Resume or EndForcedQueueing, to execute
282 // the added events.
283 MOZ_ASSERT(mSuspended || !!mForcedCount);
285 mEventQueue.InsertElementsAt(0, aEvents.Length());
287 for (uint32_t i = 0; i < aEvents.Length(); i++) {
288 mEventQueue[i] = std::move(aEvents[i]);
292 inline void ChannelEventQueue::CompleteResume() {
293 bool tryFlush = false;
295 MutexAutoLock lock(mMutex);
297 // channel may have been suspended again since Resume fired event to call
298 // this.
299 if (!mSuspendCount) {
300 // we need to remain logically suspended (for purposes of queuing incoming
301 // messages) until this point, else new incoming messages could run before
302 // queued ones.
303 mSuspended = false;
304 tryFlush = true;
308 if (tryFlush) {
309 MaybeFlushQueue();
313 inline void ChannelEventQueue::MaybeFlushQueue() {
314 // Don't flush if forced queuing on, we're already being flushed, or
315 // suspended, or there's nothing to flush
316 bool flushQueue = false;
319 MutexAutoLock lock(mMutex);
320 flushQueue = !mForcedCount && !mFlushing && !mSuspended &&
321 !mEventQueue.IsEmpty() && !MaybeSuspendIfEventsAreSuppressed();
323 // Only one thread is allowed to run FlushQueue at a time.
324 if (flushQueue) {
325 mFlushing = true;
329 if (flushQueue) {
330 FlushQueue();
334 // Ensures that RunOrEnqueue() will be collecting events during its lifetime
335 // (letting caller know incoming IPDL msgs should be queued). Flushes the queue
336 // when it goes out of scope.
337 class MOZ_STACK_CLASS AutoEventEnqueuer {
338 public:
339 explicit AutoEventEnqueuer(ChannelEventQueue* queue)
340 : mEventQueue(queue), mOwner(queue->mOwner) {
341 mEventQueue->StartForcedQueueing();
343 ~AutoEventEnqueuer() { mEventQueue->EndForcedQueueing(); }
345 private:
346 RefPtr<ChannelEventQueue> mEventQueue;
347 // Ensure channel object lives longer than ChannelEventQueue.
348 nsCOMPtr<nsISupports> mOwner;
351 } // namespace net
352 } // namespace mozilla
354 #endif