/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // nsIEventTarget wrapper for throttling event dispatch. #ifndef mozilla_ThrottledEventQueue_h #define mozilla_ThrottledEventQueue_h #include "nsIEventTarget.h" namespace mozilla { // A ThrottledEventQueue is an event target that can be used to throttle // events being dispatched to another base target. It maintains its // own queue of events and only dispatches one at a time to the wrapped // target. This can be used to avoid flooding the base target. // // Flooding is avoided via a very simply principal. Runnables dispatched // to the ThrottledEventQueue are only dispatched to the base target // one at a time. Only once that runnable has executed will we dispatch // the next runnable to the base target. This in effect makes all // runnables passing through the ThrottledEventQueue yield to other work // on the base target. // // ThrottledEventQueue keeps runnables waiting to be dispatched to the // base in its own internal queue. Code can query the length of this // queue using IsEmpty() and Length(). Further, code implement back // pressure by checking the depth of the queue and deciding to stop // issuing runnables if they see the ThrottledEventQueue is backed up. // Code running on other threads could even use AwaitIdle() to block // all operation until the ThrottledEventQueue drains. // // Note, this class is similar to TaskQueue, but also differs in a few // ways. First, it is a very simple nsIEventTarget implementation. It // does not use the AbstractThread API. // // In addition, ThrottledEventQueue currently dispatches its next // runnable to the base target *before* running the current event. This // allows the event code to spin the event loop without stalling the // ThrottledEventQueue. In contrast, TaskQueue only dispatches its next // runnable after running the current event. That approach is necessary // for TaskQueue in order to work with thread pool targets. // // So, if you are targeting a thread pool you probably want a TaskQueue. // If you are targeting a single thread or other non-concurrent event // target, you probably want a ThrottledEventQueue. // // ThrottledEventQueue also implements an automatic shutdown mechanism. // De-referencing the queue or browser shutdown will automatically begin // shutdown. // // Once shutdown begins all events will bypass the queue and be dispatched // straight to the underlying base target. class ThrottledEventQueue final : public nsIEventTarget { class Inner; RefPtr<Inner> mInner; explicit ThrottledEventQueue(already_AddRefed<Inner> aInner); ~ThrottledEventQueue(); // Begin shutdown of the event queue. This has no effect if shutdown // is already in process. After this is called nsIEventTarget methods // will bypass the queue and operate directly on the base target. // Note, this could be made public if code needs to explicitly shutdown // for some reason. void MaybeStartShutdown(); public: // Attempt to create a ThrottledEventQueue for the given target. This // may return nullptr if the browser is already shutting down. static already_AddRefed<ThrottledEventQueue> Create(nsIEventTarget* aBaseTarget); // Determine if there are any events pending in the queue. bool IsEmpty() const; // Determine how many events are pending in the queue. uint32_t Length() const; // Block the current thread until the queue is empty. This may not // be called on the main thread or the base target. void AwaitIdle() const; NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIEVENTTARGET }; } // namespace mozilla #endif // mozilla_ThrottledEventQueue_h