From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- xpcom/threads/nsThreadUtils.h | 1925 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1925 insertions(+) create mode 100644 xpcom/threads/nsThreadUtils.h (limited to 'xpcom/threads/nsThreadUtils.h') diff --git a/xpcom/threads/nsThreadUtils.h b/xpcom/threads/nsThreadUtils.h new file mode 100644 index 0000000000..72041da295 --- /dev/null +++ b/xpcom/threads/nsThreadUtils.h @@ -0,0 +1,1925 @@ +/* -*- 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/. */ + +#ifndef nsThreadUtils_h__ +#define nsThreadUtils_h__ + +#include +#include +#include + +#include "MainThreadUtils.h" +#include "mozilla/EventQueue.h" +#include "mozilla/AbstractThread.h" +#include "mozilla/Atomics.h" +#include "mozilla/Likely.h" +#include "mozilla/Maybe.h" +#include "mozilla/ThreadLocal.h" +#include "mozilla/TimeStamp.h" + +#include "nsCOMPtr.h" +#include "nsICancelableRunnable.h" +#include "nsIDiscardableRunnable.h" +#include "nsIIdlePeriod.h" +#include "nsIIdleRunnable.h" +#include "nsINamed.h" +#include "nsIRunnable.h" +#include "nsIThreadManager.h" +#include "nsITimer.h" +#include "nsString.h" +#include "prinrval.h" +#include "prthread.h" + +class MessageLoop; +class nsIThread; + +//----------------------------------------------------------------------------- +// These methods are alternatives to the methods on nsIThreadManager, provided +// for convenience. + +/** + * Create a new thread, and optionally provide an initial event for the thread. + * + * @param aName + * The name of the thread. + * @param aResult + * The resulting nsIThread object. + * @param aInitialEvent + * The initial event to run on this thread. This parameter may be null. + * @param aOptions + * Options used to configure thread creation. + * Options are documented in nsIThreadManager.idl. + * + * @returns NS_ERROR_INVALID_ARG + * Indicates that the given name is not unique. + */ + +extern nsresult NS_NewNamedThread( + const nsACString& aName, nsIThread** aResult, + nsIRunnable* aInitialEvent = nullptr, + nsIThreadManager::ThreadCreationOptions aOptions = {}); + +extern nsresult NS_NewNamedThread( + const nsACString& aName, nsIThread** aResult, + already_AddRefed aInitialEvent, + nsIThreadManager::ThreadCreationOptions aOptions = {}); + +template +inline nsresult NS_NewNamedThread( + const char (&aName)[LEN], nsIThread** aResult, + already_AddRefed aInitialEvent, + nsIThreadManager::ThreadCreationOptions aOptions = {}) { + static_assert(LEN <= 16, "Thread name must be no more than 16 characters"); + return NS_NewNamedThread(nsDependentCString(aName, LEN - 1), aResult, + std::move(aInitialEvent), aOptions); +} + +template +inline nsresult NS_NewNamedThread( + const char (&aName)[LEN], nsIThread** aResult, + nsIRunnable* aInitialEvent = nullptr, + nsIThreadManager::ThreadCreationOptions aOptions = {}) { + nsCOMPtr event = aInitialEvent; + static_assert(LEN <= 16, "Thread name must be no more than 16 characters"); + return NS_NewNamedThread(nsDependentCString(aName, LEN - 1), aResult, + event.forget(), aOptions); +} + +/** + * Get a reference to the current thread, creating it if it does not exist yet. + * + * @param aResult + * The resulting nsIThread object. + */ +extern nsresult NS_GetCurrentThread(nsIThread** aResult); + +/** + * Dispatch the given event to the current thread. + * + * @param aEvent + * The event to dispatch. + * + * @returns NS_ERROR_INVALID_ARG + * If event is null. + */ +extern nsresult NS_DispatchToCurrentThread(nsIRunnable* aEvent); +extern nsresult NS_DispatchToCurrentThread( + already_AddRefed&& aEvent); + +/** + * Dispatch the given event to the main thread. + * + * @param aEvent + * The event to dispatch. + * @param aDispatchFlags + * The flags to pass to the main thread's dispatch method. + * + * @returns NS_ERROR_INVALID_ARG + * If event is null. + */ +extern nsresult NS_DispatchToMainThread( + nsIRunnable* aEvent, uint32_t aDispatchFlags = NS_DISPATCH_NORMAL); +extern nsresult NS_DispatchToMainThread( + already_AddRefed&& aEvent, + uint32_t aDispatchFlags = NS_DISPATCH_NORMAL); + +extern nsresult NS_DelayedDispatchToCurrentThread( + already_AddRefed&& aEvent, uint32_t aDelayMs); + +/** + * Dispatch the given event to the specified queue of the current thread. + * + * @param aEvent The event to dispatch. + * @param aQueue The event queue for the thread to use + * + * @returns NS_ERROR_INVALID_ARG + * If event is null. + * @returns NS_ERROR_UNEXPECTED + * If the thread is shutting down. + */ +extern nsresult NS_DispatchToCurrentThreadQueue( + already_AddRefed&& aEvent, mozilla::EventQueuePriority aQueue); + +/** + * Dispatch the given event to the specified queue of the main thread. + * + * @param aEvent The event to dispatch. + * @param aQueue The event queue for the thread to use + * + * @returns NS_ERROR_INVALID_ARG + * If event is null. + * @returns NS_ERROR_UNEXPECTED + * If the thread is shutting down. + */ +extern nsresult NS_DispatchToMainThreadQueue( + already_AddRefed&& aEvent, mozilla::EventQueuePriority aQueue); + +/** + * Dispatch the given event to an idle queue of the current thread. + * + * @param aEvent The event to dispatch. If the event implements + * nsIIdleRunnable, it will receive a call on + * nsIIdleRunnable::SetTimer when dispatched, with the value of + * aTimeout. + * + * @param aTimeout The time in milliseconds until the event should be + * moved from an idle queue to the regular queue, if it hasn't been + * executed. If aEvent is also an nsIIdleRunnable, it is expected + * that it should handle the timeout itself, after a call to + * nsIIdleRunnable::SetTimer. + * + * @param aQueue + * The event queue for the thread to use. Must be an idle queue + * (Idle or DeferredTimers) + * + * @returns NS_ERROR_INVALID_ARG + * If event is null. + * @returns NS_ERROR_UNEXPECTED + * If the thread is shutting down. + */ +extern nsresult NS_DispatchToCurrentThreadQueue( + already_AddRefed&& aEvent, uint32_t aTimeout, + mozilla::EventQueuePriority aQueue); + +/** + * Dispatch the given event to a queue of a thread. + * + * @param aEvent The event to dispatch. + * @param aThread The target thread for the dispatch. + * @param aQueue The event queue for the thread to use. + * + * @returns NS_ERROR_INVALID_ARG + * If event is null. + * @returns NS_ERROR_UNEXPECTED + * If the thread is shutting down. + */ +extern nsresult NS_DispatchToThreadQueue(already_AddRefed&& aEvent, + nsIThread* aThread, + mozilla::EventQueuePriority aQueue); + +/** + * Dispatch the given event to an idle queue of a thread. + * + * @param aEvent The event to dispatch. If the event implements + * nsIIdleRunnable, it will receive a call on + * nsIIdleRunnable::SetTimer when dispatched, with the value of + * aTimeout. + * + * @param aTimeout The time in milliseconds until the event should be + * moved from an idle queue to the regular queue, if it hasn't been + * executed. If aEvent is also an nsIIdleRunnable, it is expected + * that it should handle the timeout itself, after a call to + * nsIIdleRunnable::SetTimer. + * + * @param aThread The target thread for the dispatch. + * + * @param aQueue + * The event queue for the thread to use. Must be an idle queue + * (Idle or DeferredTimers) + * + * @returns NS_ERROR_INVALID_ARG + * If event is null. + * @returns NS_ERROR_UNEXPECTED + * If the thread is shutting down. + */ +extern nsresult NS_DispatchToThreadQueue(already_AddRefed&& aEvent, + uint32_t aTimeout, nsIThread* aThread, + mozilla::EventQueuePriority aQueue); + +#ifndef XPCOM_GLUE_AVOID_NSPR +/** + * Process all pending events for the given thread before returning. This + * method simply calls ProcessNextEvent on the thread while HasPendingEvents + * continues to return true and the time spent in NS_ProcessPendingEvents + * does not exceed the given timeout value. + * + * @param aThread + * The thread object for which to process pending events. If null, then + * events will be processed for the current thread. + * @param aTimeout + * The maximum number of milliseconds to spend processing pending events. + * Events are not pre-empted to honor this timeout. Rather, the timeout + * value is simply used to determine whether or not to process another event. + * Pass PR_INTERVAL_NO_TIMEOUT to specify no timeout. + */ +extern nsresult NS_ProcessPendingEvents( + nsIThread* aThread, PRIntervalTime aTimeout = PR_INTERVAL_NO_TIMEOUT); +#endif + +/** + * Shortcut for nsIThread::HasPendingEvents. + * + * It is an error to call this function when the given thread is not the + * current thread. This function will return false if called from some + * other thread. + * + * @param aThread + * The current thread or null. + * + * @returns + * A boolean value that if "true" indicates that there are pending events + * in the current thread's event queue. + */ +extern bool NS_HasPendingEvents(nsIThread* aThread = nullptr); + +/** + * Shortcut for nsIThread::ProcessNextEvent. + * + * It is an error to call this function when the given thread is not the + * current thread. This function will simply return false if called + * from some other thread. + * + * @param aThread + * The current thread or null. + * @param aMayWait + * A boolean parameter that if "true" indicates that the method may block + * the calling thread to wait for a pending event. + * + * @returns + * A boolean value that if "true" indicates that an event from the current + * thread's event queue was processed. + */ +extern bool NS_ProcessNextEvent(nsIThread* aThread = nullptr, + bool aMayWait = true); + +/** + * Returns true if we're in the compositor thread. + * + * We declare this here because the headers required to invoke + * CompositorThreadHolder::IsInCompositorThread() also pull in a bunch of system + * headers that #define various tokens in a way that can break the build. + */ +extern bool NS_IsInCompositorThread(); + +extern bool NS_IsInCanvasThreadOrWorker(); + +extern bool NS_IsInVRThread(); + +//----------------------------------------------------------------------------- +// Helpers that work with nsCOMPtr: + +inline already_AddRefed do_GetCurrentThread() { + nsIThread* thread = nullptr; + NS_GetCurrentThread(&thread); + return already_AddRefed(thread); +} + +inline already_AddRefed do_GetMainThread() { + nsIThread* thread = nullptr; + NS_GetMainThread(&thread); + return already_AddRefed(thread); +} + +//----------------------------------------------------------------------------- + +// Fast access to the current thread. Will create an nsIThread if one does not +// exist already! Do not release the returned pointer! If you want to use this +// pointer from some other thread, then you will need to AddRef it. Otherwise, +// you should only consider this pointer valid from code running on the current +// thread. +extern nsIThread* NS_GetCurrentThread(); + +// Exactly the same as NS_GetCurrentThread, except it will not create an +// nsThread if one does not exist yet. This is useful in cases where you have +// code that runs on threads that may or may not not be driven by an nsThread +// event loop, and wish to avoid inadvertently creating a superfluous nsThread. +extern nsIThread* NS_GetCurrentThreadNoCreate(); + +/** + * Set the name of the current thread. Prefer this function over + * PR_SetCurrentThreadName() if possible. The name will also be included in the + * crash report. + * + * @param aName + * Name of the thread. A C language null-terminated string. + */ +extern void NS_SetCurrentThreadName(const char* aName); + +//----------------------------------------------------------------------------- + +#ifndef XPCOM_GLUE_AVOID_NSPR + +namespace mozilla { + +// This class is designed to be subclassed. +class IdlePeriod : public nsIIdlePeriod { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIIDLEPERIOD + + IdlePeriod() = default; + + protected: + virtual ~IdlePeriod() = default; + + private: + IdlePeriod(const IdlePeriod&) = delete; + IdlePeriod& operator=(const IdlePeriod&) = delete; + IdlePeriod& operator=(const IdlePeriod&&) = delete; +}; + +// Cancelable runnable methods implement nsICancelableRunnable, and +// Idle and IdleWithTimer also nsIIdleRunnable. +enum class RunnableKind { Standard, Cancelable, Idle, IdleWithTimer }; + +// Implementing nsINamed on Runnable bloats vtables for the hundreds of +// Runnable subclasses that we have, so we want to avoid that overhead +// when we're not using nsINamed for anything. +# ifndef RELEASE_OR_BETA +# define MOZ_COLLECTING_RUNNABLE_TELEMETRY +# endif + +// This class is designed to be subclassed. +class Runnable : public nsIRunnable +# ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY + , + public nsINamed +# endif +{ + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIRUNNABLE +# ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY + NS_DECL_NSINAMED +# endif + + Runnable() = delete; + +# ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY + explicit Runnable(const char* aName) : mName(aName) {} +# else + explicit Runnable(const char* aName) {} +# endif + + protected: + virtual ~Runnable() = default; + +# ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY + const char* mName = nullptr; +# endif + + private: + Runnable(const Runnable&) = delete; + Runnable& operator=(const Runnable&) = delete; + Runnable& operator=(const Runnable&&) = delete; +}; + +// This is a base class for tasks that might not be run, such as those that may +// be dispatched to workers. +// The owner of an event target will call either Run() or OnDiscard() +// exactly once. +// Derived classes should override Run(). An OnDiscard() override may +// provide cleanup when Run() will not be called. +class DiscardableRunnable : public Runnable, public nsIDiscardableRunnable { + public: + NS_DECL_ISUPPORTS_INHERITED + // nsIDiscardableRunnable + void OnDiscard() override {} + + DiscardableRunnable() = delete; + explicit DiscardableRunnable(const char* aName) : Runnable(aName) {} + + protected: + virtual ~DiscardableRunnable() = default; + + private: + DiscardableRunnable(const DiscardableRunnable&) = delete; + DiscardableRunnable& operator=(const DiscardableRunnable&) = delete; + DiscardableRunnable& operator=(const DiscardableRunnable&&) = delete; +}; + +// This class is designed to be subclassed. +// Derived classes should override Run() and Cancel() to provide that +// calling Run() after Cancel() is a no-op. +class CancelableRunnable : public DiscardableRunnable, + public nsICancelableRunnable { + public: + NS_DECL_ISUPPORTS_INHERITED + // nsIDiscardableRunnable + void OnDiscard() override; + // nsICancelableRunnable + virtual nsresult Cancel() override = 0; + + CancelableRunnable() = delete; + explicit CancelableRunnable(const char* aName) : DiscardableRunnable(aName) {} + + protected: + virtual ~CancelableRunnable() = default; + + private: + CancelableRunnable(const CancelableRunnable&) = delete; + CancelableRunnable& operator=(const CancelableRunnable&) = delete; + CancelableRunnable& operator=(const CancelableRunnable&&) = delete; +}; + +// This class is designed to be subclassed. +class IdleRunnable : public DiscardableRunnable, public nsIIdleRunnable { + public: + NS_DECL_ISUPPORTS_INHERITED + + explicit IdleRunnable(const char* aName) : DiscardableRunnable(aName) {} + + protected: + virtual ~IdleRunnable() = default; + + private: + IdleRunnable(const IdleRunnable&) = delete; + IdleRunnable& operator=(const IdleRunnable&) = delete; + IdleRunnable& operator=(const IdleRunnable&&) = delete; +}; + +// This class is designed to be subclassed. +class CancelableIdleRunnable : public CancelableRunnable, + public nsIIdleRunnable { + public: + NS_DECL_ISUPPORTS_INHERITED + + CancelableIdleRunnable() : CancelableRunnable("CancelableIdleRunnable") {} + explicit CancelableIdleRunnable(const char* aName) + : CancelableRunnable(aName) {} + + protected: + virtual ~CancelableIdleRunnable() = default; + + private: + CancelableIdleRunnable(const CancelableIdleRunnable&) = delete; + CancelableIdleRunnable& operator=(const CancelableIdleRunnable&) = delete; + CancelableIdleRunnable& operator=(const CancelableIdleRunnable&&) = delete; +}; + +// This class is designed to be a wrapper of a real runnable to support event +// prioritizable. +class PrioritizableRunnable : public Runnable, public nsIRunnablePriority { + public: + PrioritizableRunnable(already_AddRefed&& aRunnable, + uint32_t aPriority); + +# ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY + NS_IMETHOD GetName(nsACString& aName) override; +# endif + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIRUNNABLE + NS_DECL_NSIRUNNABLEPRIORITY + + protected: + virtual ~PrioritizableRunnable() = default; + + nsCOMPtr mRunnable; + uint32_t mPriority; +}; + +class PrioritizableCancelableRunnable : public CancelableRunnable, + public nsIRunnablePriority { + public: + PrioritizableCancelableRunnable(uint32_t aPriority, const char* aName) + : CancelableRunnable(aName), mPriority(aPriority) {} + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIRUNNABLEPRIORITY + + protected: + virtual ~PrioritizableCancelableRunnable() = default; + + const uint32_t mPriority; +}; + +extern already_AddRefed CreateRenderBlockingRunnable( + already_AddRefed&& aRunnable); + +namespace detail { + +// An event that can be used to call a C++11 functions or function objects, +// including lambdas. The function must have no required arguments, and must +// return void. +template +class RunnableFunction : public Runnable { + public: + template + explicit RunnableFunction(const char* aName, F&& aFunction) + : Runnable(aName), mFunction(std::forward(aFunction)) {} + + NS_IMETHOD Run() override { + static_assert(std::is_void_v, + "The lambda must return void!"); + mFunction(); + return NS_OK; + } + + private: + StoredFunction mFunction; +}; + +// Type alias for NS_NewRunnableFunction +template +using RunnableFunctionImpl = + // Make sure we store a non-reference in nsRunnableFunction. + typename detail::RunnableFunction>; +} // namespace detail + +namespace detail { + +template +struct IsRefcountedSmartPointerHelper : std::false_type {}; + +template +struct IsRefcountedSmartPointerHelper> : std::true_type {}; + +template +struct IsRefcountedSmartPointerHelper> : std::true_type {}; + +} // namespace detail + +template +struct IsRefcountedSmartPointer + : detail::IsRefcountedSmartPointerHelper> {}; + +namespace detail { + +template +struct RemoveSmartPointerHelper { + typedef T Type; +}; + +template +struct RemoveSmartPointerHelper> { + typedef Pointee Type; +}; + +template +struct RemoveSmartPointerHelper> { + typedef Pointee Type; +}; + +} // namespace detail + +template +struct RemoveSmartPointer + : detail::RemoveSmartPointerHelper> {}; + +namespace detail { + +template +struct RemoveRawOrSmartPointerHelper { + typedef T Type; +}; + +template +struct RemoveRawOrSmartPointerHelper { + typedef Pointee Type; +}; + +template +struct RemoveRawOrSmartPointerHelper> { + typedef Pointee Type; +}; + +template +struct RemoveRawOrSmartPointerHelper> { + typedef Pointee Type; +}; + +} // namespace detail + +template +struct RemoveRawOrSmartPointer + : detail::RemoveRawOrSmartPointerHelper> {}; + +} // namespace mozilla + +inline nsISupports* ToSupports(mozilla::Runnable* p) { + return static_cast(p); +} + +template +already_AddRefed NS_NewRunnableFunction( + const char* aName, Function&& aFunction) { + // We store a non-reference in RunnableFunction, but still forward aFunction + // to move if possible. + return do_AddRef(new mozilla::detail::RunnableFunctionImpl( + aName, std::forward(aFunction))); +} + +// Creates a new object implementing nsIRunnable and nsICancelableRunnable, +// which runs a given function on Run and clears the stored function object on a +// call to `Cancel` (and thus destroys all objects it holds). +template +already_AddRefed NS_NewCancelableRunnableFunction( + const char* aName, Function&& aFunc) { + class FuncCancelableRunnable final : public mozilla::CancelableRunnable { + public: + static_assert( + std::is_void_v< + decltype(std::declval>()())>); + + NS_INLINE_DECL_REFCOUNTING_INHERITED(FuncCancelableRunnable, + CancelableRunnable) + + explicit FuncCancelableRunnable(const char* aName, Function&& aFunc) + : CancelableRunnable{aName}, + mFunc{mozilla::Some(std::forward(aFunc))} {} + + NS_IMETHOD Run() override { + if (mFunc) { + (*mFunc)(); + } + + return NS_OK; + } + + nsresult Cancel() override { + mFunc.reset(); + return NS_OK; + } + + private: + ~FuncCancelableRunnable() = default; + + mozilla::Maybe> mFunc; + }; + + return mozilla::MakeAndAddRef( + aName, std::forward(aFunc)); +} + +namespace mozilla { +namespace detail { + +template +class TimerBehaviour { + public: + nsITimer* GetTimer() { return nullptr; } + void CancelTimer() {} + + protected: + ~TimerBehaviour() = default; +}; + +template <> +class TimerBehaviour { + public: + nsITimer* GetTimer() { + if (!mTimer) { + mTimer = NS_NewTimer(); + } + + return mTimer; + } + + void CancelTimer() { + if (mTimer) { + mTimer->Cancel(); + } + } + + protected: + ~TimerBehaviour() { CancelTimer(); } + + private: + nsCOMPtr mTimer; +}; + +} // namespace detail +} // namespace mozilla + +// An event that can be used to call a method on a class. The class type must +// support reference counting. This event supports Revoke for use +// with nsRevocableEventPtr. +template +class nsRunnableMethod + : public std::conditional_t< + Kind == mozilla::RunnableKind::Standard, mozilla::Runnable, + std::conditional_t>, + protected mozilla::detail::TimerBehaviour { + using BaseType = std::conditional_t< + Kind == mozilla::RunnableKind::Standard, mozilla::Runnable, + std::conditional_t>; + + public: + nsRunnableMethod(const char* aName) : BaseType(aName) {} + + virtual void Revoke() = 0; + + // These ReturnTypeEnforcer classes disallow return types that + // we know are not safe. The default ReturnTypeEnforcer compiles just fine but + // already_AddRefed will not. + template + class ReturnTypeEnforcer { + public: + typedef int ReturnTypeIsSafe; + }; + + template + class ReturnTypeEnforcer> { + // No ReturnTypeIsSafe makes this illegal! + }; + + // Make sure this return type is safe. + typedef typename ReturnTypeEnforcer::ReturnTypeIsSafe check; +}; + +template +struct nsRunnableMethodReceiver { + RefPtr mObj; + explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {} + explicit nsRunnableMethodReceiver(RefPtr&& aObj) + : mObj(std::move(aObj)) {} + ~nsRunnableMethodReceiver() { Revoke(); } + ClassType* Get() const { return mObj.get(); } + void Revoke() { mObj = nullptr; } +}; + +template +struct nsRunnableMethodReceiver { + ClassType* MOZ_NON_OWNING_REF mObj; + explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {} + ClassType* Get() const { return mObj; } + void Revoke() { mObj = nullptr; } +}; + +static inline constexpr bool IsIdle(mozilla::RunnableKind aKind) { + return aKind == mozilla::RunnableKind::Idle || + aKind == mozilla::RunnableKind::IdleWithTimer; +} + +template +struct nsRunnableMethodTraits; + +template +struct nsRunnableMethodTraits { + typedef typename mozilla::RemoveRawOrSmartPointer::Type class_type; + static_assert(std::is_base_of::value, + "Stored class must inherit from method's class"); + typedef R return_type; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Kind == mozilla::RunnableKind::Cancelable; +}; + +template +struct nsRunnableMethodTraits { + typedef const typename mozilla::RemoveRawOrSmartPointer::Type + class_type; + static_assert(std::is_base_of::value, + "Stored class must inherit from method's class"); + typedef R return_type; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Kind == mozilla::RunnableKind::Cancelable; +}; + +# ifdef NS_HAVE_STDCALL +template +struct nsRunnableMethodTraits { + typedef typename mozilla::RemoveRawOrSmartPointer::Type class_type; + static_assert(std::is_base_of::value, + "Stored class must inherit from method's class"); + typedef R return_type; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Kind == mozilla::RunnableKind::Cancelable; +}; + +template +struct nsRunnableMethodTraits { + typedef typename mozilla::RemoveRawOrSmartPointer::Type class_type; + static_assert(std::is_base_of::value, + "Stored class must inherit from method's class"); + typedef R return_type; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Kind == mozilla::RunnableKind::Cancelable; +}; + +template +struct nsRunnableMethodTraits { + typedef const typename mozilla::RemoveRawOrSmartPointer::Type + class_type; + static_assert(std::is_base_of::value, + "Stored class must inherit from method's class"); + typedef R return_type; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Kind == mozilla::RunnableKind::Cancelable; +}; + +template +struct nsRunnableMethodTraits { + typedef const typename mozilla::RemoveRawOrSmartPointer::Type + class_type; + static_assert(std::is_base_of::value, + "Stored class must inherit from method's class"); + typedef R return_type; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Kind == mozilla::RunnableKind::Cancelable; +}; +# endif + +// IsParameterStorageClass::value is true if T is a parameter-storage class +// that will be recognized by NS_New[NonOwning]RunnableMethodWithArg[s] to +// force a specific storage&passing strategy (instead of inferring one, +// see ParameterStorage). +// When creating a new storage class, add a specialization for it to be +// recognized. +template +struct IsParameterStorageClass : public std::false_type {}; + +// StoreXPassByY structs used to inform nsRunnableMethodArguments how to +// store arguments, and how to pass them to the target method. + +template +struct StoreCopyPassByValue { + using stored_type = std::decay_t; + typedef stored_type passed_type; + stored_type m; + template + MOZ_IMPLICIT StoreCopyPassByValue(A&& a) : m(std::forward(a)) {} + passed_type PassAsParameter() { return m; } +}; +template +struct IsParameterStorageClass> + : public std::true_type {}; + +template +struct StoreCopyPassByConstLRef { + using stored_type = std::decay_t; + typedef const stored_type& passed_type; + stored_type m; + template + MOZ_IMPLICIT StoreCopyPassByConstLRef(A&& a) : m(std::forward(a)) {} + passed_type PassAsParameter() { return m; } +}; +template +struct IsParameterStorageClass> + : public std::true_type {}; + +template +struct StoreCopyPassByLRef { + using stored_type = std::decay_t; + typedef stored_type& passed_type; + stored_type m; + template + MOZ_IMPLICIT StoreCopyPassByLRef(A&& a) : m(std::forward(a)) {} + passed_type PassAsParameter() { return m; } +}; +template +struct IsParameterStorageClass> : public std::true_type { +}; + +template +struct StoreCopyPassByRRef { + using stored_type = std::decay_t; + typedef stored_type&& passed_type; + stored_type m; + template + MOZ_IMPLICIT StoreCopyPassByRRef(A&& a) : m(std::forward(a)) {} + passed_type PassAsParameter() { return std::move(m); } +}; +template +struct IsParameterStorageClass> : public std::true_type { +}; + +template +struct StoreRefPassByLRef { + typedef T& stored_type; + typedef T& passed_type; + stored_type m; + template + MOZ_IMPLICIT StoreRefPassByLRef(A& a) : m(a) {} + passed_type PassAsParameter() { return m; } +}; +template +struct IsParameterStorageClass> : public std::true_type { +}; + +template +struct StoreConstRefPassByConstLRef { + typedef const T& stored_type; + typedef const T& passed_type; + stored_type m; + template + MOZ_IMPLICIT StoreConstRefPassByConstLRef(const A& a) : m(a) {} + passed_type PassAsParameter() { return m; } +}; +template +struct IsParameterStorageClass> + : public std::true_type {}; + +template +struct StoreRefPtrPassByPtr { + typedef RefPtr stored_type; + typedef T* passed_type; + stored_type m; + template + MOZ_IMPLICIT StoreRefPtrPassByPtr(A&& a) : m(std::forward(a)) {} + passed_type PassAsParameter() { return m.get(); } +}; +template +struct IsParameterStorageClass> + : public std::true_type {}; + +template +struct StorePtrPassByPtr { + typedef T* stored_type; + typedef T* passed_type; + stored_type m; + template + MOZ_IMPLICIT StorePtrPassByPtr(A a) : m(a) {} + passed_type PassAsParameter() { return m; } +}; +template +struct IsParameterStorageClass> : public std::true_type {}; + +template +struct StoreConstPtrPassByConstPtr { + typedef const T* stored_type; + typedef const T* passed_type; + stored_type m; + template + MOZ_IMPLICIT StoreConstPtrPassByConstPtr(A a) : m(a) {} + passed_type PassAsParameter() { return m; } +}; +template +struct IsParameterStorageClass> + : public std::true_type {}; + +template +struct StoreCopyPassByConstPtr { + typedef T stored_type; + typedef const T* passed_type; + stored_type m; + template + MOZ_IMPLICIT StoreCopyPassByConstPtr(A&& a) : m(std::forward(a)) {} + passed_type PassAsParameter() { return &m; } +}; +template +struct IsParameterStorageClass> + : public std::true_type {}; + +template +struct StoreCopyPassByPtr { + typedef T stored_type; + typedef T* passed_type; + stored_type m; + template + MOZ_IMPLICIT StoreCopyPassByPtr(A&& a) : m(std::forward(a)) {} + passed_type PassAsParameter() { return &m; } +}; +template +struct IsParameterStorageClass> : public std::true_type { +}; + +namespace detail { + +template +struct SFINAE1True : std::true_type {}; + +template +static auto HasRefCountMethodsTest(int) + -> SFINAE1True().AddRef(), + std::declval().Release())>; +template +static auto HasRefCountMethodsTest(long) -> std::false_type; + +template +struct HasRefCountMethods : decltype(HasRefCountMethodsTest(0)) {}; + +template +struct NonnsISupportsPointerStorageClass + : std::conditional< + std::is_const_v, + StoreConstPtrPassByConstPtr>, + StorePtrPassByPtr> { + using Type = typename NonnsISupportsPointerStorageClass::conditional::type; +}; + +template +struct PointerStorageClass + : std::conditional< + HasRefCountMethods::value, + StoreRefPtrPassByPtr, + typename NonnsISupportsPointerStorageClass::Type> { + using Type = typename PointerStorageClass::conditional::type; +}; + +template +struct LValueReferenceStorageClass + : std::conditional< + std::is_const_v, + StoreConstRefPassByConstLRef>, + StoreRefPassByLRef> { + using Type = typename LValueReferenceStorageClass::conditional::type; +}; + +template +struct SmartPointerStorageClass + : std::conditional< + mozilla::IsRefcountedSmartPointer::value, + StoreRefPtrPassByPtr::Type>, + StoreCopyPassByConstLRef> { + using Type = typename SmartPointerStorageClass::conditional::type; +}; + +template +struct NonLValueReferenceStorageClass + : std::conditional, + StoreCopyPassByRRef>, + typename SmartPointerStorageClass::Type> { + using Type = typename NonLValueReferenceStorageClass::conditional::type; +}; + +template +struct NonPointerStorageClass + : std::conditional, + typename LValueReferenceStorageClass< + std::remove_reference_t>::Type, + typename NonLValueReferenceStorageClass::Type> { + using Type = typename NonPointerStorageClass::conditional::type; +}; + +template +struct NonParameterStorageClass + : std::conditional< + std::is_pointer_v, + typename PointerStorageClass>::Type, + typename NonPointerStorageClass::Type> { + using Type = typename NonParameterStorageClass::conditional::type; +}; + +// Choose storage&passing strategy based on preferred storage type: +// - If IsParameterStorageClass::value is true, use as-is. +// - RC* -> StoreRefPtrPassByPtr :Store RefPtr, pass RC* +// ^^ RC quacks like a ref-counted type (i.e., has AddRef and Release methods) +// - const T* -> StoreConstPtrPassByConstPtr :Store const T*, pass const T* +// - T* -> StorePtrPassByPtr :Store T*, pass T*. +// - const T& -> StoreConstRefPassByConstLRef:Store const T&, pass const T&. +// - T& -> StoreRefPassByLRef :Store T&, pass T&. +// - T&& -> StoreCopyPassByRRef :Store T, pass std::move(T). +// - RefPtr, nsCOMPtr +// -> StoreRefPtrPassByPtr :Store RefPtr, pass T* +// - Other T -> StoreCopyPassByConstLRef :Store T, pass const T&. +// Other available explicit options: +// - StoreCopyPassByValue :Store T, pass T. +// - StoreCopyPassByLRef :Store T, pass T& (of copy!) +// - StoreCopyPassByConstPtr :Store T, pass const T* +// - StoreCopyPassByPtr :Store T, pass T* (of copy!) +// Or create your own class with PassAsParameter() method, optional +// clean-up in destructor, and with associated IsParameterStorageClass<>. +template +struct ParameterStorage + : std::conditional::value, T, + typename NonParameterStorageClass::Type> { + using Type = typename ParameterStorage::conditional::type; +}; + +template +static auto HasSetDeadlineTest(int) + -> SFINAE1True().SetDeadline( + std::declval()))>; + +template +static auto HasSetDeadlineTest(long) -> std::false_type; + +template +struct HasSetDeadline : decltype(HasSetDeadlineTest(0)) {}; + +template +std::enable_if_t<::detail::HasSetDeadline::value> SetDeadlineImpl( + T* aObj, mozilla::TimeStamp aTimeStamp) { + aObj->SetDeadline(aTimeStamp); +} + +template +std::enable_if_t::value> SetDeadlineImpl( + T* aObj, mozilla::TimeStamp aTimeStamp) {} +} /* namespace detail */ + +namespace mozilla { +namespace detail { + +// struct used to store arguments and later apply them to a method. +template +struct RunnableMethodArguments final { + std::tuple::Type...> mArguments; + template + explicit RunnableMethodArguments(As&&... aArguments) + : mArguments(std::forward(aArguments)...) {} + template + decltype(auto) apply(C* o, M m) { + return std::apply( + [&o, m](auto&&... args) { + return ((*o).*m)(args.PassAsParameter()...); + }, + mArguments); + } +}; + +template +class RunnableMethodImpl final + : public ::nsRunnableMethodTraits::base_type { + typedef typename ::nsRunnableMethodTraits + Traits; + + typedef typename Traits::class_type ClassType; + typedef typename Traits::base_type BaseType; + ::nsRunnableMethodReceiver mReceiver; + Method mMethod; + RunnableMethodArguments mArgs; + using BaseType::CancelTimer; + using BaseType::GetTimer; + + private: + virtual ~RunnableMethodImpl() { Revoke(); }; + static void TimedOut(nsITimer* aTimer, void* aClosure) { + static_assert(IsIdle(Kind), "Don't use me!"); + RefPtr r = + static_cast(aClosure); + r->SetDeadline(TimeStamp()); + r->Run(); + r->Cancel(); + } + + public: + template + explicit RunnableMethodImpl(const char* aName, ForwardedPtrType&& aObj, + Method aMethod, Args&&... aArgs) + : BaseType(aName), + mReceiver(std::forward(aObj)), + mMethod(aMethod), + mArgs(std::forward(aArgs)...) { + static_assert(sizeof...(Storages) == sizeof...(Args), + "Storages and Args should have equal sizes"); + } + + NS_IMETHOD Run() { + CancelTimer(); + + if (MOZ_LIKELY(mReceiver.Get())) { + mArgs.apply(mReceiver.Get(), mMethod); + } + + return NS_OK; + } + + nsresult Cancel() { + static_assert(Kind >= RunnableKind::Cancelable, "Don't use me!"); + Revoke(); + return NS_OK; + } + + void Revoke() { + CancelTimer(); + mReceiver.Revoke(); + } + + void SetDeadline(TimeStamp aDeadline) { + if (MOZ_LIKELY(mReceiver.Get())) { + ::detail::SetDeadlineImpl(mReceiver.Get(), aDeadline); + } + } + + void SetTimer(uint32_t aDelay, nsIEventTarget* aTarget) { + MOZ_ASSERT(aTarget); + + if (nsCOMPtr timer = GetTimer()) { + timer->Cancel(); + timer->SetTarget(aTarget); + timer->InitWithNamedFuncCallback(TimedOut, this, aDelay, + nsITimer::TYPE_ONE_SHOT, + "detail::RunnableMethodImpl::SetTimer"); + } + } +}; + +// Type aliases for NewRunnableMethod. +template +using OwningRunnableMethod = + typename ::nsRunnableMethodTraits, Method, + true, RunnableKind::Standard>::base_type; +template +using OwningRunnableMethodImpl = + RunnableMethodImpl, Method, true, + RunnableKind::Standard, Storages...>; + +// Type aliases for NewCancelableRunnableMethod. +template +using CancelableRunnableMethod = + typename ::nsRunnableMethodTraits, Method, + true, + RunnableKind::Cancelable>::base_type; +template +using CancelableRunnableMethodImpl = + RunnableMethodImpl, Method, true, + RunnableKind::Cancelable, Storages...>; + +// Type aliases for NewIdleRunnableMethod. +template +using IdleRunnableMethod = + typename ::nsRunnableMethodTraits, Method, + true, RunnableKind::Idle>::base_type; +template +using IdleRunnableMethodImpl = + RunnableMethodImpl, Method, true, + RunnableKind::Idle, Storages...>; + +// Type aliases for NewIdleRunnableMethodWithTimer. +template +using IdleRunnableMethodWithTimer = + typename ::nsRunnableMethodTraits, Method, + true, + RunnableKind::IdleWithTimer>::base_type; +template +using IdleRunnableMethodWithTimerImpl = + RunnableMethodImpl, Method, true, + RunnableKind::IdleWithTimer, Storages...>; + +// Type aliases for NewNonOwningRunnableMethod. +template +using NonOwningRunnableMethod = + typename ::nsRunnableMethodTraits, Method, + false, RunnableKind::Standard>::base_type; +template +using NonOwningRunnableMethodImpl = + RunnableMethodImpl, Method, false, + RunnableKind::Standard, Storages...>; + +// Type aliases for NonOwningCancelableRunnableMethod +template +using NonOwningCancelableRunnableMethod = + typename ::nsRunnableMethodTraits, Method, + false, + RunnableKind::Cancelable>::base_type; +template +using NonOwningCancelableRunnableMethodImpl = + RunnableMethodImpl, Method, false, + RunnableKind::Cancelable, Storages...>; + +// Type aliases for NonOwningIdleRunnableMethod +template +using NonOwningIdleRunnableMethod = + typename ::nsRunnableMethodTraits, Method, + false, RunnableKind::Idle>::base_type; +template +using NonOwningIdleRunnableMethodImpl = + RunnableMethodImpl, Method, false, + RunnableKind::Idle, Storages...>; + +// Type aliases for NewIdleRunnableMethodWithTimer. +template +using NonOwningIdleRunnableMethodWithTimer = + typename ::nsRunnableMethodTraits, Method, + false, + RunnableKind::IdleWithTimer>::base_type; +template +using NonOwningIdleRunnableMethodWithTimerImpl = + RunnableMethodImpl, Method, false, + RunnableKind::IdleWithTimer, Storages...>; + +} // namespace detail + +// NewRunnableMethod and friends +// +// Very often in Gecko, you'll find yourself in a situation where you want +// to invoke a method (with or without arguments) asynchronously. You +// could write a small helper class inheriting from nsRunnable to handle +// all these details, or you could let NewRunnableMethod take care of all +// those details for you. +// +// The simplest use of NewRunnableMethod looks like: +// +// nsCOMPtr event = +// mozilla::NewRunnableMethod("description", myObject, +// &MyClass::HandleEvent); +// NS_DispatchToCurrentThread(event); +// +// Statically enforced constraints: +// - myObject must be of (or implicitly convertible to) type MyClass +// - MyClass must define AddRef and Release methods +// +// The "description" string should specify a human-readable name for the +// runnable; the provided string is used by various introspection tools +// in the browser. +// +// The created runnable will take a strong reference to `myObject`. For +// non-refcounted objects, or refcounted objects with unusual refcounting +// requirements, and if and only if you are 110% certain that `myObject` +// will live long enough, you can use NewNonOwningRunnableMethod instead, +// which will, as its name implies, take a non-owning reference. If you +// find yourself having to use this function, you should accompany your use +// with a proof comment describing why the runnable will not lead to +// use-after-frees. +// +// (If you find yourself writing contorted code to Release() an object +// asynchronously on a different thread, you should use the +// NS_ProxyRelease function.) +// +// Invoking a method with arguments takes a little more care. The +// natural extension of the above: +// +// nsCOMPtr event = +// mozilla::NewRunnableMethod("description", myObject, +// &MyClass::HandleEvent, +// arg1, arg2, ...); +// +// can lead to security hazards (e.g. passing in raw pointers to refcounted +// objects and storing those raw pointers in the runnable). We therefore +// require you to specify the storage types used by the runnable, just as +// you would if you were writing out the class by hand: +// +// nsCOMPtr event = +// mozilla::NewRunnableMethod, nsTArray> +// ("description", myObject, &MyClass::HandleEvent, arg1, arg2); +// +// Please note that you do not have to pass the same argument type as you +// specify in the template arguments. For example, if you want to transfer +// ownership to a runnable, you can write: +// +// RefPtr ptr = ...; +// nsTArray array = ...; +// nsCOMPtr event = +// mozilla::NewRunnableMethod, nsTArray> +// ("description", myObject, &MyClass::DoSomething, +// std::move(ptr), std::move(array)); +// +// and there will be no extra AddRef/Release traffic, or copying of the array. +// +// Each type that you specify as a template argument to NewRunnableMethod +// comes with its own style of storage in the runnable and its own style +// of argument passing to the invoked method. See the comment for +// ParameterStorage above for more details. +// +// If you need to customize the storage type and/or argument passing type, +// you can write your own class to use as a template argument to +// NewRunnableMethod. If you find yourself having to do that frequently, +// please file a bug in Core::XPCOM about adding the custom type to the +// core code in this file, and/or for custom rules for ParameterStorage +// to select that strategy. +// +// For places that require you to use cancelable runnables, such as +// workers, there's also NewCancelableRunnableMethod and its non-owning +// counterpart. The runnables returned by these methods additionally +// implement nsICancelableRunnable. +// +// Finally, all of the functions discussed above have additional overloads +// that do not take a `const char*` as their first parameter; you may see +// these in older code. The `const char*` overload is preferred and +// should be used in new code exclusively. + +template +already_AddRefed> +NewRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod) { + return do_AddRef(new detail::OwningRunnableMethodImpl( + aName, std::forward(aPtr), aMethod)); +} + +template +already_AddRefed> +NewCancelableRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod) { + return do_AddRef(new detail::CancelableRunnableMethodImpl( + aName, std::forward(aPtr), aMethod)); +} + +template +already_AddRefed> +NewIdleRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod) { + return do_AddRef(new detail::IdleRunnableMethodImpl( + aName, std::forward(aPtr), aMethod)); +} + +template +already_AddRefed> +NewIdleRunnableMethodWithTimer(const char* aName, PtrType&& aPtr, + Method aMethod) { + return do_AddRef(new detail::IdleRunnableMethodWithTimerImpl( + aName, std::forward(aPtr), aMethod)); +} + +template +already_AddRefed> +NewNonOwningRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod) { + return do_AddRef(new detail::NonOwningRunnableMethodImpl( + aName, std::forward(aPtr), aMethod)); +} + +template +already_AddRefed> +NewNonOwningCancelableRunnableMethod(const char* aName, PtrType&& aPtr, + Method aMethod) { + return do_AddRef( + new detail::NonOwningCancelableRunnableMethodImpl( + aName, std::forward(aPtr), aMethod)); +} + +template +already_AddRefed> +NewNonOwningIdleRunnableMethod(const char* aName, PtrType&& aPtr, + Method aMethod) { + return do_AddRef(new detail::NonOwningIdleRunnableMethodImpl( + aName, std::forward(aPtr), aMethod)); +} + +template +already_AddRefed> +NewNonOwningIdleRunnableMethodWithTimer(const char* aName, PtrType&& aPtr, + Method aMethod) { + return do_AddRef( + new detail::NonOwningIdleRunnableMethodWithTimerImpl( + aName, std::forward(aPtr), aMethod)); +} + +// Similar to NewRunnableMethod. Call like so: +// nsCOMPtr event = +// NewRunnableMethod(myObject, &MyClass::HandleEvent, myArg1,...); +// 'Types' are the stored type for each argument, see ParameterStorage for +// details. +template +already_AddRefed> +NewRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod, + Args&&... aArgs) { + static_assert(sizeof...(Storages) == sizeof...(Args), + " size should be equal to number of arguments"); + return do_AddRef( + new detail::OwningRunnableMethodImpl( + aName, std::forward(aPtr), aMethod, + std::forward(aArgs)...)); +} + +template +already_AddRefed> +NewNonOwningRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod, + Args&&... aArgs) { + static_assert(sizeof...(Storages) == sizeof...(Args), + " size should be equal to number of arguments"); + return do_AddRef( + new detail::NonOwningRunnableMethodImpl( + aName, std::forward(aPtr), aMethod, + std::forward(aArgs)...)); +} + +template +already_AddRefed> +NewCancelableRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod, + Args&&... aArgs) { + static_assert(sizeof...(Storages) == sizeof...(Args), + " size should be equal to number of arguments"); + return do_AddRef( + new detail::CancelableRunnableMethodImpl( + aName, std::forward(aPtr), aMethod, + std::forward(aArgs)...)); +} + +template +already_AddRefed> +NewNonOwningCancelableRunnableMethod(const char* aName, PtrType&& aPtr, + Method aMethod, Args&&... aArgs) { + static_assert(sizeof...(Storages) == sizeof...(Args), + " size should be equal to number of arguments"); + return do_AddRef( + new detail::NonOwningCancelableRunnableMethodImpl( + aName, std::forward(aPtr), aMethod, + std::forward(aArgs)...)); +} + +template +already_AddRefed> +NewIdleRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod, + Args&&... aArgs) { + static_assert(sizeof...(Storages) == sizeof...(Args), + " size should be equal to number of arguments"); + return do_AddRef( + new detail::IdleRunnableMethodImpl( + aName, std::forward(aPtr), aMethod, + std::forward(aArgs)...)); +} + +template +already_AddRefed> +NewNonOwningIdleRunnableMethod(const char* aName, PtrType&& aPtr, + Method aMethod, Args&&... aArgs) { + static_assert(sizeof...(Storages) == sizeof...(Args), + " size should be equal to number of arguments"); + return do_AddRef( + new detail::NonOwningIdleRunnableMethodImpl( + aName, std::forward(aPtr), aMethod, + std::forward(aArgs)...)); +} + +} // namespace mozilla + +#endif // XPCOM_GLUE_AVOID_NSPR + +// This class is designed to be used when you have an event class E that has a +// pointer back to resource class R. If R goes away while E is still pending, +// then it is important to "revoke" E so that it does not try use R after R has +// been destroyed. nsRevocableEventPtr makes it easy for R to manage such +// situations: +// +// class R; +// +// class E : public mozilla::Runnable { +// public: +// void Revoke() { +// mResource = nullptr; +// } +// private: +// R *mResource; +// }; +// +// class R { +// public: +// void EventHandled() { +// mEvent.Forget(); +// } +// private: +// nsRevocableEventPtr mEvent; +// }; +// +// void R::PostEvent() { +// // Make sure any pending event is revoked. +// mEvent->Revoke(); +// +// nsCOMPtr event = new E(); +// if (NS_SUCCEEDED(NS_DispatchToCurrentThread(event))) { +// // Keep pointer to event so we can revoke it. +// mEvent = event; +// } +// } +// +// NS_IMETHODIMP E::Run() { +// if (!mResource) +// return NS_OK; +// ... +// mResource->EventHandled(); +// return NS_OK; +// } +// +template +class nsRevocableEventPtr { + public: + nsRevocableEventPtr() : mEvent(nullptr) {} + ~nsRevocableEventPtr() { Revoke(); } + + const nsRevocableEventPtr& operator=(RefPtr&& aEvent) { + if (mEvent != aEvent) { + Revoke(); + mEvent = std::move(aEvent); + } + return *this; + } + + void Revoke() { + if (mEvent) { + mEvent->Revoke(); + mEvent = nullptr; + } + } + + void Forget() { mEvent = nullptr; } + bool IsPending() { return mEvent != nullptr; } + T* get() { return mEvent; } + + private: + // Not implemented + nsRevocableEventPtr(const nsRevocableEventPtr&); + nsRevocableEventPtr& operator=(const nsRevocableEventPtr&); + + RefPtr mEvent; +}; + +template +inline already_AddRefed do_AddRef(nsRevocableEventPtr& aObj) { + return do_AddRef(aObj.get()); +} + +/** + * A simple helper to suffix thread pool name + * with incremental numbers. + */ +class nsThreadPoolNaming { + public: + nsThreadPoolNaming() = default; + + /** + * Returns a thread name as " #" and increments the counter. + */ + nsCString GetNextThreadName(const nsACString& aPoolName); + + template + nsCString GetNextThreadName(const char (&aPoolName)[LEN]) { + return GetNextThreadName(nsDependentCString(aPoolName, LEN - 1)); + } + + private: + mozilla::Atomic mCounter{0}; + + nsThreadPoolNaming(const nsThreadPoolNaming&) = delete; + void operator=(const nsThreadPoolNaming&) = delete; +}; + +/** + * Thread priority in most operating systems affect scheduling, not IO. This + * helper is used to set the current thread to low IO priority for the lifetime + * of the created object. You can only use this low priority IO setting within + * the context of the current thread. + */ +class MOZ_STACK_CLASS nsAutoLowPriorityIO { + public: + nsAutoLowPriorityIO(); + ~nsAutoLowPriorityIO(); + + private: + bool lowIOPrioritySet; +#if defined(XP_MACOSX) + int oldPriority; +#endif +}; + +void NS_SetMainThread(); + +// Used only on cooperatively scheduled "main" threads. Causes the thread to be +// considered a main thread and also causes GetCurrentVirtualThread to return +// aVirtualThread. +void NS_SetMainThread(PRThread* aVirtualThread); + +// Used only on cooperatively scheduled "main" threads. Causes the thread to no +// longer be considered a main thread. Also causes GetCurrentVirtualThread() to +// return a unique value. +void NS_UnsetMainThread(); + +/** + * Return the expiration time of the next timer to run on the current + * thread. If that expiration time is greater than aDefault, then + * return aDefault. aSearchBound specifies a maximum number of timers + * to examine to find a timer on the current thread. If no timer that + * will run on the current thread is found after examining + * aSearchBound timers, return the highest seen expiration time as a + * best effort guess. + * + * Timers with either the type nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY or + * nsITIMER::TYPE_REPEATING_SLACK_LOW_PRIORITY will be skipped when + * searching for the next expiration time. This enables timers to + * have lower priority than callbacks dispatched from + * nsIThread::IdleDispatch. + */ +extern mozilla::TimeStamp NS_GetTimerDeadlineHintOnCurrentThread( + mozilla::TimeStamp aDefault, uint32_t aSearchBound); + +/** + * Dispatches the given event to a background thread. The primary benefit of + * this API is that you do not have to manage the lifetime of your own thread + * for running your own events; the thread manager will take care of the + * background thread's lifetime. Not having to manage your own thread also + * means less resource usage, as the underlying implementation here can manage + * spinning up and shutting down threads appropriately. + * + * NOTE: there is no guarantee that events dispatched via these APIs are run + * serially, in dispatch order; several dispatched events may run in parallel. + * If you depend on serial execution of dispatched events, you should use + * NS_CreateBackgroundTaskQueue instead, and dispatch events to the returned + * event target. + */ +extern nsresult NS_DispatchBackgroundTask( + already_AddRefed aEvent, + uint32_t aDispatchFlags = NS_DISPATCH_NORMAL); +extern "C" nsresult NS_DispatchBackgroundTask( + nsIRunnable* aEvent, uint32_t aDispatchFlags = NS_DISPATCH_NORMAL); + +/** + * Obtain a new serial event target that dispatches runnables to a background + * thread. In many cases, this is a straight replacement for creating your + * own, private thread, and is generally preferred to creating your own, + * private thread. + */ +extern "C" nsresult NS_CreateBackgroundTaskQueue( + const char* aName, nsISerialEventTarget** aTarget); + +/** + * Dispatch the given runnable to the given event target, spinning the current + * thread's event loop until the runnable has finished executing. + * + * This is roughly equivalent to the previously-supported `NS_DISPATCH_SYNC` + * flag. + */ +extern nsresult NS_DispatchAndSpinEventLoopUntilComplete( + const nsACString& aVeryGoodReasonToDoThis, nsIEventTarget* aEventTarget, + already_AddRefed aEvent); + +// Predeclaration for logging function below +namespace IPC { +class Message; +class MessageReader; +class MessageWriter; +} // namespace IPC + +class nsTimerImpl; + +namespace mozilla { + +// RAII class that will set the TLS entry to return the currently running +// nsISerialEventTarget. +// It should be used from inner event loop implementation. +class SerialEventTargetGuard { + public: + explicit SerialEventTargetGuard(nsISerialEventTarget* aThread) + : mLastCurrentThread(sCurrentThreadTLS.get()) { + Set(aThread); + } + + ~SerialEventTargetGuard() { sCurrentThreadTLS.set(mLastCurrentThread); } + + static void InitTLS(); + static nsISerialEventTarget* GetCurrentSerialEventTarget() { + return sCurrentThreadTLS.get(); + } + + protected: + friend class ::MessageLoop; + static void Set(nsISerialEventTarget* aThread) { + MOZ_ASSERT(aThread->IsOnCurrentThread()); + sCurrentThreadTLS.set(aThread); + } + + private: + static MOZ_THREAD_LOCAL(nsISerialEventTarget*) sCurrentThreadTLS; + nsISerialEventTarget* mLastCurrentThread; +}; + +// Get the serial event target corresponding to the currently executing task +// queue or thread. This method will assert if called on a thread pool without +// an active task queue. +// +// This function should generally be preferred over NS_GetCurrentThread since it +// will return a more useful answer when called from a task queue running on a +// thread pool or on a non-xpcom thread which accepts runnable dispatches. +// +// NOTE: The returned nsISerialEventTarget may not accept runnable dispatches +// (e.g. if it corresponds to a non-xpcom thread), however it may still be used +// to check if you're on the given thread/queue using IsOnCurrentThread(). + +nsISerialEventTarget* GetCurrentSerialEventTarget(); + +// Get a weak reference to a serial event target which can be used to dispatch +// runnables to the main thread. +// +// NOTE: While this is currently a weak pointer to the nsIThread* returned from +// NS_GetMainThread(), this may change in the future. + +nsISerialEventTarget* GetMainThreadSerialEventTarget(); + +// Returns the number of CPUs, like PR_GetNumberOfProcessors, except +// that it can return a cached value on platforms where sandboxing +// would prevent reading the current value (currently Linux). CPU +// hotplugging is uncommon, so this is unlikely to make a difference +// in practice. +size_t GetNumberOfProcessors(); + +/** + * A helper class to log tasks dispatch and run with "MOZ_LOG=events:1". The + * output is more machine readable and creates a link between dispatch and run. + * + * Usage example for the concrete template type nsIRunnable. + * To log a dispatch, which means putting an event to a queue: + * LogRunnable::LogDispatch(event); + * theQueue.putEvent(event); + * + * To log execution (running) of the event: + * nsCOMPtr event = theQueue.popEvent(); + * { + * LogRunnable::Run log(event); + * event->Run(); + * event = null; // to include the destructor code in the span + * } + * + * The class is a template so that we can support various specific super-types + * of tasks in the future. We can't use void* because it may cast differently + * and tracking the pointer in logs would then be impossible. + */ +template +class LogTaskBase { + public: + LogTaskBase() = delete; + + // Adds a simple log about dispatch of this runnable. + static void LogDispatch(T* aEvent); + // The `aContext` pointer adds another uniqe identifier, nothing more + static void LogDispatch(T* aEvent, void* aContext); + + // Logs dispatch of the message and along that also the PID of the target + // proccess, purposed for uniquely identifying IPC messages. + static void LogDispatchWithPid(T* aEvent, int32_t aPid); + + // This is designed to surround a call to `Run()` or any code representing + // execution of the task body. + // The constructor adds a simple log about start of the runnable execution and + // the destructor adds a log about ending the execution. + class MOZ_RAII Run { + public: + Run() = delete; + explicit Run(T* aEvent, bool aWillRunAgain = false); + explicit Run(T* aEvent, void* aContext, bool aWillRunAgain = false); + ~Run(); + + // When this is called, the log in this RAII dtor will only say + // "interrupted" expecting that the event will run again. + void WillRunAgain() { mWillRunAgain = true; } + + private: + bool mWillRunAgain = false; + }; +}; + +class MicroTaskRunnable; +class Task; // TaskController +class PresShell; +namespace dom { +class FrameRequestCallback; +} // namespace dom + +// Specialized methods must be explicitly predeclared. +template <> +LogTaskBase::Run::Run(nsIRunnable* aEvent, bool aWillRunAgain); +template <> +LogTaskBase::Run::Run(Task* aTask, bool aWillRunAgain); +template <> +void LogTaskBase::LogDispatchWithPid(IPC::Message* aEvent, + int32_t aPid); +template <> +LogTaskBase::Run::Run(IPC::Message* aMessage, bool aWillRunAgain); +template <> +LogTaskBase::Run::Run(nsTimerImpl* aEvent, bool aWillRunAgain); + +typedef LogTaskBase LogRunnable; +typedef LogTaskBase LogMicroTaskRunnable; +typedef LogTaskBase LogIPCMessage; +typedef LogTaskBase LogTimerEvent; +typedef LogTaskBase LogTask; +typedef LogTaskBase LogPresShellObserver; +typedef LogTaskBase LogFrameRequestCallback; +// If you add new types don't forget to add: +// `template class LogTaskBase;` to nsThreadUtils.cpp + +} // namespace mozilla + +#endif // nsThreadUtils_h__ -- cgit v1.2.3