Bug 1932613 - temporarily disable browser_ml_end_to_end.js for permanent failures...
[gecko.git] / xpcom / threads / ThrottledEventQueue.h
blobcf37a10a6dc9f36001d92f887beb6747e4597d89
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // nsIEventTarget wrapper for throttling event dispatch.
9 #ifndef mozilla_ThrottledEventQueue_h
10 #define mozilla_ThrottledEventQueue_h
12 #include "nsISerialEventTarget.h"
14 #define NS_THROTTLEDEVENTQUEUE_IID \
15 { \
16 0x8f3cf7dc, 0xfc14, 0x4ad5, { \
17 0x9f, 0xd5, 0xdb, 0x79, 0xbc, 0xe6, 0xd5, 0x08 \
18 } \
21 namespace mozilla {
23 // A ThrottledEventQueue is an event target that can be used to throttle
24 // events being dispatched to another base target. It maintains its
25 // own queue of events and only dispatches one at a time to the wrapped
26 // target. This can be used to avoid flooding the base target.
28 // Flooding is avoided via a very simple principle. Runnables dispatched
29 // to the ThrottledEventQueue are only dispatched to the base target
30 // one at a time. Only once that runnable has executed will we dispatch
31 // the next runnable to the base target. This in effect makes all
32 // runnables passing through the ThrottledEventQueue yield to other work
33 // on the base target.
35 // ThrottledEventQueue keeps runnables waiting to be dispatched to the
36 // base in its own internal queue. Code can query the length of this
37 // queue using IsEmpty() and Length(). Further, code implement back
38 // pressure by checking the depth of the queue and deciding to stop
39 // issuing runnables if they see the ThrottledEventQueue is backed up.
40 // Code running on other threads could even use AwaitIdle() to block
41 // all operation until the ThrottledEventQueue drains.
43 // Note, this class is similar to TaskQueue, but also differs in a few
44 // ways. First, it is a very simple nsIEventTarget implementation. It
45 // does not use the AbstractThread API.
47 // In addition, ThrottledEventQueue currently dispatches its next
48 // runnable to the base target *before* running the current event. This
49 // allows the event code to spin the event loop without stalling the
50 // ThrottledEventQueue. In contrast, TaskQueue only dispatches its next
51 // runnable after running the current event. That approach is necessary
52 // for TaskQueue in order to work with thread pool targets.
54 // So, if you are targeting a thread pool you probably want a TaskQueue.
55 // If you are targeting a single thread or other non-concurrent event
56 // target, you probably want a ThrottledEventQueue.
58 // If you drop a ThrottledEventQueue while its queue still has events to be run,
59 // they will continue to be dispatched as usual to the base. Only once the last
60 // event has run will all the ThrottledEventQueue's memory be freed.
61 class ThrottledEventQueue final : public nsISerialEventTarget {
62 class Inner;
63 RefPtr<Inner> mInner;
65 explicit ThrottledEventQueue(already_AddRefed<Inner> aInner);
66 ~ThrottledEventQueue() = default;
68 public:
69 // Create a ThrottledEventQueue for the given target.
70 static already_AddRefed<ThrottledEventQueue> Create(
71 nsISerialEventTarget* aBaseTarget, const char* aName,
72 uint32_t aPriority = nsIRunnablePriority::PRIORITY_NORMAL);
74 // Determine if there are any events pending in the queue.
75 bool IsEmpty() const;
77 // Determine how many events are pending in the queue.
78 uint32_t Length() const;
80 already_AddRefed<nsIRunnable> GetEvent();
82 // Block the current thread until the queue is empty. This may not be called
83 // on the main thread or the base target. The ThrottledEventQueue must not be
84 // paused.
85 void AwaitIdle() const;
87 // If |aIsPaused| is true, pause execution of events from this queue. No
88 // events from this queue will be run until this is called with |aIsPaused|
89 // false.
91 // To un-pause a ThrottledEventQueue, we need to dispatch a runnable to the
92 // underlying event target. That operation may fail, so this method is
93 // fallible as well.
95 // Note that, although ThrottledEventQueue's behavior is descibed as queueing
96 // events on the base target, an event queued on a TEQ is never actually moved
97 // to any other queue. What is actually dispatched to the base is an
98 // "executor" event which, when run, removes an event from the TEQ and runs it
99 // immediately. This means that you can pause a TEQ even after the executor
100 // has been queued on the base target, and even so, no events from the TEQ
101 // will run. When the base target gets around to running the executor, the
102 // executor will see that the TEQ is paused, and do nothing.
103 [[nodiscard]] nsresult SetIsPaused(bool aIsPaused);
105 // Return true if this ThrottledEventQueue is paused.
106 bool IsPaused() const;
108 NS_DECL_THREADSAFE_ISUPPORTS
109 NS_DECL_NSIEVENTTARGET_FULL
111 NS_DECLARE_STATIC_IID_ACCESSOR(NS_THROTTLEDEVENTQUEUE_IID);
114 NS_DEFINE_STATIC_IID_ACCESSOR(ThrottledEventQueue, NS_THROTTLEDEVENTQUEUE_IID);
116 } // namespace mozilla
118 #endif // mozilla_ThrottledEventQueue_h