diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /xpcom/threads/nsThreadUtils.h | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xpcom/threads/nsThreadUtils.h')
-rw-r--r-- | xpcom/threads/nsThreadUtils.h | 1968 |
1 files changed, 1968 insertions, 0 deletions
diff --git a/xpcom/threads/nsThreadUtils.h b/xpcom/threads/nsThreadUtils.h new file mode 100644 index 0000000000..a8d64de83e --- /dev/null +++ b/xpcom/threads/nsThreadUtils.h @@ -0,0 +1,1968 @@ +/* -*- 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 <type_traits> +#include <utility> + +#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 "mozilla/Tuple.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 aStackSize + * The size in bytes to reserve for the thread's stack. + * + * @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, + uint32_t aStackSize = nsIThreadManager::DEFAULT_STACK_SIZE); + +extern nsresult NS_NewNamedThread( + const nsACString& aName, nsIThread** aResult, + already_AddRefed<nsIRunnable> aInitialEvent, + uint32_t aStackSize = nsIThreadManager::DEFAULT_STACK_SIZE); + +template <size_t LEN> +inline nsresult NS_NewNamedThread( + const char (&aName)[LEN], nsIThread** aResult, + already_AddRefed<nsIRunnable> aInitialEvent, + uint32_t aStackSize = nsIThreadManager::DEFAULT_STACK_SIZE) { + static_assert(LEN <= 16, "Thread name must be no more than 16 characters"); + return NS_NewNamedThread(nsDependentCString(aName, LEN - 1), aResult, + std::move(aInitialEvent), aStackSize); +} + +template <size_t LEN> +inline nsresult NS_NewNamedThread( + const char (&aName)[LEN], nsIThread** aResult, + nsIRunnable* aInitialEvent = nullptr, + uint32_t aStackSize = nsIThreadManager::DEFAULT_STACK_SIZE) { + nsCOMPtr<nsIRunnable> 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(), aStackSize); +} + +/** + * 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<nsIRunnable>&& 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<nsIRunnable>&& aEvent, + uint32_t aDispatchFlags = NS_DISPATCH_NORMAL); + +extern nsresult NS_DelayedDispatchToCurrentThread( + already_AddRefed<nsIRunnable>&& 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<nsIRunnable>&& 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<nsIRunnable>&& 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<nsIRunnable>&& 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<nsIRunnable>&& 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<nsIRunnable>&& 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<nsIThread> do_GetCurrentThread() { + nsIThread* thread = nullptr; + NS_GetCurrentThread(&thread); + return already_AddRefed<nsIThread>(thread); +} + +inline already_AddRefed<nsIThread> do_GetMainThread() { + nsIThread* thread = nullptr; + NS_GetMainThread(&thread); + return already_AddRefed<nsIThread>(thread); +} + +//----------------------------------------------------------------------------- + +#ifdef MOZILLA_INTERNAL_API +// 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); +#endif + +//----------------------------------------------------------------------------- + +#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 + + IdleRunnable() : DiscardableRunnable("IdleRunnable") {} + 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<nsIRunnable>&& 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<nsIRunnable> mRunnable; + uint32_t mPriority; +}; + +extern already_AddRefed<nsIRunnable> CreateMediumHighRunnable( + already_AddRefed<nsIRunnable>&& 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 <typename StoredFunction> +class RunnableFunction : public Runnable { + public: + template <typename F> + explicit RunnableFunction(const char* aName, F&& aFunction) + : Runnable(aName), mFunction(std::forward<F>(aFunction)) {} + + NS_IMETHOD Run() override { + static_assert(std::is_void_v<decltype(mFunction())>, + "The lambda must return void!"); + mFunction(); + return NS_OK; + } + + private: + StoredFunction mFunction; +}; + +// Type alias for NS_NewRunnableFunction +template <typename Function> +using RunnableFunctionImpl = + // Make sure we store a non-reference in nsRunnableFunction. + typename detail::RunnableFunction<std::remove_reference_t<Function>>; +} // namespace detail + +namespace detail { + +template <typename CVRemoved> +struct IsRefcountedSmartPointerHelper : std::false_type {}; + +template <typename Pointee> +struct IsRefcountedSmartPointerHelper<RefPtr<Pointee>> : std::true_type {}; + +template <typename Pointee> +struct IsRefcountedSmartPointerHelper<nsCOMPtr<Pointee>> : std::true_type {}; + +} // namespace detail + +template <typename T> +struct IsRefcountedSmartPointer + : detail::IsRefcountedSmartPointerHelper<std::remove_cv_t<T>> {}; + +namespace detail { + +template <typename T, typename CVRemoved> +struct RemoveSmartPointerHelper { + typedef T Type; +}; + +template <typename T, typename Pointee> +struct RemoveSmartPointerHelper<T, RefPtr<Pointee>> { + typedef Pointee Type; +}; + +template <typename T, typename Pointee> +struct RemoveSmartPointerHelper<T, nsCOMPtr<Pointee>> { + typedef Pointee Type; +}; + +} // namespace detail + +template <typename T> +struct RemoveSmartPointer + : detail::RemoveSmartPointerHelper<T, std::remove_cv_t<T>> {}; + +namespace detail { + +template <typename T, typename CVRemoved> +struct RemoveRawOrSmartPointerHelper { + typedef T Type; +}; + +template <typename T, typename Pointee> +struct RemoveRawOrSmartPointerHelper<T, Pointee*> { + typedef Pointee Type; +}; + +template <typename T, typename Pointee> +struct RemoveRawOrSmartPointerHelper<T, RefPtr<Pointee>> { + typedef Pointee Type; +}; + +template <typename T, typename Pointee> +struct RemoveRawOrSmartPointerHelper<T, nsCOMPtr<Pointee>> { + typedef Pointee Type; +}; + +} // namespace detail + +template <typename T> +struct RemoveRawOrSmartPointer + : detail::RemoveRawOrSmartPointerHelper<T, std::remove_cv_t<T>> {}; + +} // namespace mozilla + +inline nsISupports* ToSupports(mozilla::Runnable* p) { + return static_cast<nsIRunnable*>(p); +} + +template <typename Function> +already_AddRefed<mozilla::Runnable> 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<Function>( + aName, std::forward<Function>(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 <typename Function> +already_AddRefed<mozilla::CancelableRunnable> NS_NewCancelableRunnableFunction( + const char* aName, Function&& aFunc) { + class FuncCancelableRunnable final : public mozilla::CancelableRunnable { + public: + static_assert(std::is_void_v<decltype( + std::declval<std::remove_reference_t<Function>>()())>); + + NS_INLINE_DECL_REFCOUNTING_INHERITED(FuncCancelableRunnable, + CancelableRunnable) + + explicit FuncCancelableRunnable(const char* aName, Function&& aFunc) + : CancelableRunnable{aName}, + mFunc{mozilla::Some(std::forward<Function>(aFunc))} {} + + NS_IMETHOD Run() override { + if (mFunc) { + (*mFunc)(); + } + + return NS_OK; + } + + nsresult Cancel() override { + mFunc.reset(); + return NS_OK; + } + + private: + ~FuncCancelableRunnable() = default; + + mozilla::Maybe<std::remove_reference_t<Function>> mFunc; + }; + + return mozilla::MakeAndAddRef<FuncCancelableRunnable>( + aName, std::forward<Function>(aFunc)); +} + +namespace mozilla { +namespace detail { + +template <RunnableKind Kind> +class TimerBehaviour { + public: + nsITimer* GetTimer() { return nullptr; } + void CancelTimer() {} + + protected: + ~TimerBehaviour() = default; +}; + +template <> +class TimerBehaviour<RunnableKind::IdleWithTimer> { + public: + nsITimer* GetTimer() { + if (!mTimer) { + mTimer = NS_NewTimer(); + } + + return mTimer; + } + + void CancelTimer() { + if (mTimer) { + mTimer->Cancel(); + } + } + + protected: + ~TimerBehaviour() { CancelTimer(); } + + private: + nsCOMPtr<nsITimer> 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 ClassType, typename ReturnType = void, bool Owning = true, + mozilla::RunnableKind Kind = mozilla::RunnableKind::Standard> +class nsRunnableMethod + : public std::conditional_t< + Kind == mozilla::RunnableKind::Standard, mozilla::Runnable, + std::conditional_t<Kind == mozilla::RunnableKind::Cancelable, + mozilla::CancelableRunnable, + mozilla::CancelableIdleRunnable>>, + protected mozilla::detail::TimerBehaviour<Kind> { + using BaseType = std::conditional_t< + Kind == mozilla::RunnableKind::Standard, mozilla::Runnable, + std::conditional_t<Kind == mozilla::RunnableKind::Cancelable, + mozilla::CancelableRunnable, + mozilla::CancelableIdleRunnable>>; + + 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 <typename OtherReturnType> + class ReturnTypeEnforcer { + public: + typedef int ReturnTypeIsSafe; + }; + + template <class T> + class ReturnTypeEnforcer<already_AddRefed<T>> { + // No ReturnTypeIsSafe makes this illegal! + }; + + // Make sure this return type is safe. + typedef typename ReturnTypeEnforcer<ReturnType>::ReturnTypeIsSafe check; +}; + +template <class ClassType, bool Owning> +struct nsRunnableMethodReceiver { + RefPtr<ClassType> mObj; + explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {} + explicit nsRunnableMethodReceiver(RefPtr<ClassType>&& aObj) + : mObj(std::move(aObj)) {} + ~nsRunnableMethodReceiver() { Revoke(); } + ClassType* Get() const { return mObj.get(); } + void Revoke() { mObj = nullptr; } +}; + +template <class ClassType> +struct nsRunnableMethodReceiver<ClassType, false> { + 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 <typename PtrType, typename Method, bool Owning, + mozilla::RunnableKind Kind> +struct nsRunnableMethodTraits; + +template <typename PtrType, class C, typename R, bool Owning, + mozilla::RunnableKind Kind, typename... As> +struct nsRunnableMethodTraits<PtrType, R (C::*)(As...), Owning, Kind> { + typedef typename mozilla::RemoveRawOrSmartPointer<PtrType>::Type class_type; + static_assert(std::is_base_of<C, class_type>::value, + "Stored class must inherit from method's class"); + typedef R return_type; + typedef nsRunnableMethod<C, R, Owning, Kind> base_type; + static const bool can_cancel = Kind == mozilla::RunnableKind::Cancelable; +}; + +template <typename PtrType, class C, typename R, bool Owning, + mozilla::RunnableKind Kind, typename... As> +struct nsRunnableMethodTraits<PtrType, R (C::*)(As...) const, Owning, Kind> { + typedef const typename mozilla::RemoveRawOrSmartPointer<PtrType>::Type + class_type; + static_assert(std::is_base_of<C, class_type>::value, + "Stored class must inherit from method's class"); + typedef R return_type; + typedef nsRunnableMethod<C, R, Owning, Kind> base_type; + static const bool can_cancel = Kind == mozilla::RunnableKind::Cancelable; +}; + +# ifdef NS_HAVE_STDCALL +template <typename PtrType, class C, typename R, bool Owning, + mozilla::RunnableKind Kind, typename... As> +struct nsRunnableMethodTraits<PtrType, R (__stdcall C::*)(As...), Owning, + Kind> { + typedef typename mozilla::RemoveRawOrSmartPointer<PtrType>::Type class_type; + static_assert(std::is_base_of<C, class_type>::value, + "Stored class must inherit from method's class"); + typedef R return_type; + typedef nsRunnableMethod<C, R, Owning, Kind> base_type; + static const bool can_cancel = Kind == mozilla::RunnableKind::Cancelable; +}; + +template <typename PtrType, class C, typename R, bool Owning, + mozilla::RunnableKind Kind> +struct nsRunnableMethodTraits<PtrType, R (NS_STDCALL C::*)(), Owning, Kind> { + typedef typename mozilla::RemoveRawOrSmartPointer<PtrType>::Type class_type; + static_assert(std::is_base_of<C, class_type>::value, + "Stored class must inherit from method's class"); + typedef R return_type; + typedef nsRunnableMethod<C, R, Owning, Kind> base_type; + static const bool can_cancel = Kind == mozilla::RunnableKind::Cancelable; +}; + +template <typename PtrType, class C, typename R, bool Owning, + mozilla::RunnableKind Kind, typename... As> +struct nsRunnableMethodTraits<PtrType, R (__stdcall C::*)(As...) const, Owning, + Kind> { + typedef const typename mozilla::RemoveRawOrSmartPointer<PtrType>::Type + class_type; + static_assert(std::is_base_of<C, class_type>::value, + "Stored class must inherit from method's class"); + typedef R return_type; + typedef nsRunnableMethod<C, R, Owning, Kind> base_type; + static const bool can_cancel = Kind == mozilla::RunnableKind::Cancelable; +}; + +template <typename PtrType, class C, typename R, bool Owning, + mozilla::RunnableKind Kind> +struct nsRunnableMethodTraits<PtrType, R (NS_STDCALL C::*)() const, Owning, + Kind> { + typedef const typename mozilla::RemoveRawOrSmartPointer<PtrType>::Type + class_type; + static_assert(std::is_base_of<C, class_type>::value, + "Stored class must inherit from method's class"); + typedef R return_type; + typedef nsRunnableMethod<C, R, Owning, Kind> base_type; + static const bool can_cancel = Kind == mozilla::RunnableKind::Cancelable; +}; +# endif + +// IsParameterStorageClass<T>::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 <typename T> +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 <typename T> +struct StoreCopyPassByValue { + using stored_type = std::decay_t<T>; + typedef stored_type passed_type; + stored_type m; + template <typename A> + MOZ_IMPLICIT StoreCopyPassByValue(A&& a) : m(std::forward<A>(a)) {} + passed_type PassAsParameter() { return m; } +}; +template <typename S> +struct IsParameterStorageClass<StoreCopyPassByValue<S>> + : public std::true_type {}; + +template <typename T> +struct StoreCopyPassByConstLRef { + using stored_type = std::decay_t<T>; + typedef const stored_type& passed_type; + stored_type m; + template <typename A> + MOZ_IMPLICIT StoreCopyPassByConstLRef(A&& a) : m(std::forward<A>(a)) {} + passed_type PassAsParameter() { return m; } +}; +template <typename S> +struct IsParameterStorageClass<StoreCopyPassByConstLRef<S>> + : public std::true_type {}; + +template <typename T> +struct StoreCopyPassByLRef { + using stored_type = std::decay_t<T>; + typedef stored_type& passed_type; + stored_type m; + template <typename A> + MOZ_IMPLICIT StoreCopyPassByLRef(A&& a) : m(std::forward<A>(a)) {} + passed_type PassAsParameter() { return m; } +}; +template <typename S> +struct IsParameterStorageClass<StoreCopyPassByLRef<S>> : public std::true_type { +}; + +template <typename T> +struct StoreCopyPassByRRef { + using stored_type = std::decay_t<T>; + typedef stored_type&& passed_type; + stored_type m; + template <typename A> + MOZ_IMPLICIT StoreCopyPassByRRef(A&& a) : m(std::forward<A>(a)) {} + passed_type PassAsParameter() { return std::move(m); } +}; +template <typename S> +struct IsParameterStorageClass<StoreCopyPassByRRef<S>> : public std::true_type { +}; + +template <typename T> +struct StoreRefPassByLRef { + typedef T& stored_type; + typedef T& passed_type; + stored_type m; + template <typename A> + MOZ_IMPLICIT StoreRefPassByLRef(A& a) : m(a) {} + passed_type PassAsParameter() { return m; } +}; +template <typename S> +struct IsParameterStorageClass<StoreRefPassByLRef<S>> : public std::true_type { +}; + +template <typename T> +struct StoreConstRefPassByConstLRef { + typedef const T& stored_type; + typedef const T& passed_type; + stored_type m; + template <typename A> + MOZ_IMPLICIT StoreConstRefPassByConstLRef(const A& a) : m(a) {} + passed_type PassAsParameter() { return m; } +}; +template <typename S> +struct IsParameterStorageClass<StoreConstRefPassByConstLRef<S>> + : public std::true_type {}; + +template <typename T> +struct StoreRefPtrPassByPtr { + typedef RefPtr<T> stored_type; + typedef T* passed_type; + stored_type m; + template <typename A> + MOZ_IMPLICIT StoreRefPtrPassByPtr(A&& a) : m(std::forward<A>(a)) {} + passed_type PassAsParameter() { return m.get(); } +}; +template <typename S> +struct IsParameterStorageClass<StoreRefPtrPassByPtr<S>> + : public std::true_type {}; + +template <typename T> +struct StorePtrPassByPtr { + typedef T* stored_type; + typedef T* passed_type; + stored_type m; + template <typename A> + MOZ_IMPLICIT StorePtrPassByPtr(A a) : m(a) {} + passed_type PassAsParameter() { return m; } +}; +template <typename S> +struct IsParameterStorageClass<StorePtrPassByPtr<S>> : public std::true_type {}; + +template <typename T> +struct StoreConstPtrPassByConstPtr { + typedef const T* stored_type; + typedef const T* passed_type; + stored_type m; + template <typename A> + MOZ_IMPLICIT StoreConstPtrPassByConstPtr(A a) : m(a) {} + passed_type PassAsParameter() { return m; } +}; +template <typename S> +struct IsParameterStorageClass<StoreConstPtrPassByConstPtr<S>> + : public std::true_type {}; + +template <typename T> +struct StoreCopyPassByConstPtr { + typedef T stored_type; + typedef const T* passed_type; + stored_type m; + template <typename A> + MOZ_IMPLICIT StoreCopyPassByConstPtr(A&& a) : m(std::forward<A>(a)) {} + passed_type PassAsParameter() { return &m; } +}; +template <typename S> +struct IsParameterStorageClass<StoreCopyPassByConstPtr<S>> + : public std::true_type {}; + +template <typename T> +struct StoreCopyPassByPtr { + typedef T stored_type; + typedef T* passed_type; + stored_type m; + template <typename A> + MOZ_IMPLICIT StoreCopyPassByPtr(A&& a) : m(std::forward<A>(a)) {} + passed_type PassAsParameter() { return &m; } +}; +template <typename S> +struct IsParameterStorageClass<StoreCopyPassByPtr<S>> : public std::true_type { +}; + +namespace detail { + +template <typename> +struct SFINAE1True : std::true_type {}; + +template <class T> +static auto HasRefCountMethodsTest(int) + -> SFINAE1True<decltype(std::declval<T>().AddRef(), + std::declval<T>().Release())>; +template <class> +static auto HasRefCountMethodsTest(long) -> std::false_type; + +template <class T> +struct HasRefCountMethods : decltype(HasRefCountMethodsTest<T>(0)) {}; + +template <typename TWithoutPointer> +struct NonnsISupportsPointerStorageClass + : std::conditional< + std::is_const_v<TWithoutPointer>, + StoreConstPtrPassByConstPtr<std::remove_const_t<TWithoutPointer>>, + StorePtrPassByPtr<TWithoutPointer>> { + using Type = typename NonnsISupportsPointerStorageClass::conditional::type; +}; + +template <typename TWithoutPointer> +struct PointerStorageClass + : std::conditional< + HasRefCountMethods<TWithoutPointer>::value, + StoreRefPtrPassByPtr<TWithoutPointer>, + typename NonnsISupportsPointerStorageClass<TWithoutPointer>::Type> { + using Type = typename PointerStorageClass::conditional::type; +}; + +template <typename TWithoutRef> +struct LValueReferenceStorageClass + : std::conditional< + std::is_const_v<TWithoutRef>, + StoreConstRefPassByConstLRef<std::remove_const_t<TWithoutRef>>, + StoreRefPassByLRef<TWithoutRef>> { + using Type = typename LValueReferenceStorageClass::conditional::type; +}; + +template <typename T> +struct SmartPointerStorageClass + : std::conditional< + mozilla::IsRefcountedSmartPointer<T>::value, + StoreRefPtrPassByPtr<typename mozilla::RemoveSmartPointer<T>::Type>, + StoreCopyPassByConstLRef<T>> { + using Type = typename SmartPointerStorageClass::conditional::type; +}; + +template <typename T> +struct NonLValueReferenceStorageClass + : std::conditional<std::is_rvalue_reference_v<T>, + StoreCopyPassByRRef<std::remove_reference_t<T>>, + typename SmartPointerStorageClass<T>::Type> { + using Type = typename NonLValueReferenceStorageClass::conditional::type; +}; + +template <typename T> +struct NonPointerStorageClass + : std::conditional<std::is_lvalue_reference_v<T>, + typename LValueReferenceStorageClass< + std::remove_reference_t<T>>::Type, + typename NonLValueReferenceStorageClass<T>::Type> { + using Type = typename NonPointerStorageClass::conditional::type; +}; + +template <typename T> +struct NonParameterStorageClass + : std::conditional< + std::is_pointer_v<T>, + typename PointerStorageClass<std::remove_pointer_t<T>>::Type, + typename NonPointerStorageClass<T>::Type> { + using Type = typename NonParameterStorageClass::conditional::type; +}; + +// Choose storage&passing strategy based on preferred storage type: +// - If IsParameterStorageClass<T>::value is true, use as-is. +// - RC* -> StoreRefPtrPassByPtr<RC> :Store RefPtr<RC>, pass RC* +// ^^ RC quacks like a ref-counted type (i.e., has AddRef and Release methods) +// - const T* -> StoreConstPtrPassByConstPtr<T> :Store const T*, pass const T* +// - T* -> StorePtrPassByPtr<T> :Store T*, pass T*. +// - const T& -> StoreConstRefPassByConstLRef<T>:Store const T&, pass const T&. +// - T& -> StoreRefPassByLRef<T> :Store T&, pass T&. +// - T&& -> StoreCopyPassByRRef<T> :Store T, pass std::move(T). +// - RefPtr<T>, nsCOMPtr<T> +// -> StoreRefPtrPassByPtr<T> :Store RefPtr<T>, pass T* +// - Other T -> StoreCopyPassByConstLRef<T> :Store T, pass const T&. +// Other available explicit options: +// - StoreCopyPassByValue<T> :Store T, pass T. +// - StoreCopyPassByLRef<T> :Store T, pass T& (of copy!) +// - StoreCopyPassByConstPtr<T> :Store T, pass const T* +// - StoreCopyPassByPtr<T> :Store T, pass T* (of copy!) +// Or create your own class with PassAsParameter() method, optional +// clean-up in destructor, and with associated IsParameterStorageClass<>. +template <typename T> +struct ParameterStorage + : std::conditional<IsParameterStorageClass<T>::value, T, + typename NonParameterStorageClass<T>::Type> { + using Type = typename ParameterStorage::conditional::type; +}; + +template <class T> +static auto HasSetDeadlineTest(int) -> SFINAE1True<decltype( + std::declval<T>().SetDeadline(std::declval<mozilla::TimeStamp>()))>; + +template <class T> +static auto HasSetDeadlineTest(long) -> std::false_type; + +template <class T> +struct HasSetDeadline : decltype(HasSetDeadlineTest<T>(0)) {}; + +template <class T> +std::enable_if_t<::detail::HasSetDeadline<T>::value> SetDeadlineImpl( + T* aObj, mozilla::TimeStamp aTimeStamp) { + aObj->SetDeadline(aTimeStamp); +} + +template <class T> +std::enable_if_t<!::detail::HasSetDeadline<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 <typename... Ts> +struct RunnableMethodArguments final { + Tuple<typename ::detail::ParameterStorage<Ts>::Type...> mArguments; + template <typename... As> + explicit RunnableMethodArguments(As&&... aArguments) + : mArguments(std::forward<As>(aArguments)...) {} + template <typename C, typename M, typename... Args, size_t... Indices> + static auto applyImpl(C* o, M m, Tuple<Args...>& args, + std::index_sequence<Indices...>) + -> decltype(((*o).*m)(Get<Indices>(args).PassAsParameter()...)) { + return ((*o).*m)(Get<Indices>(args).PassAsParameter()...); + } + template <class C, typename M> + auto apply(C* o, M m) + -> decltype(applyImpl(o, m, mArguments, + std::index_sequence_for<Ts...>{})) { + return applyImpl(o, m, mArguments, std::index_sequence_for<Ts...>{}); + } +}; + +template <typename PtrType, typename Method, bool Owning, RunnableKind Kind, + typename... Storages> +class RunnableMethodImpl final + : public ::nsRunnableMethodTraits<PtrType, Method, Owning, + Kind>::base_type { + typedef typename ::nsRunnableMethodTraits<PtrType, Method, Owning, Kind> + Traits; + + typedef typename Traits::class_type ClassType; + typedef typename Traits::base_type BaseType; + ::nsRunnableMethodReceiver<ClassType, Owning> mReceiver; + Method mMethod; + RunnableMethodArguments<Storages...> 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<CancelableIdleRunnable> r = + static_cast<CancelableIdleRunnable*>(aClosure); + r->SetDeadline(TimeStamp()); + r->Run(); + r->Cancel(); + } + + public: + template <typename ForwardedPtrType, typename... Args> + explicit RunnableMethodImpl(const char* aName, ForwardedPtrType&& aObj, + Method aMethod, Args&&... aArgs) + : BaseType(aName), + mReceiver(std::forward<ForwardedPtrType>(aObj)), + mMethod(aMethod), + mArgs(std::forward<Args>(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<nsITimer> timer = GetTimer()) { + timer->Cancel(); + timer->SetTarget(aTarget); + timer->InitWithNamedFuncCallback(TimedOut, this, aDelay, + nsITimer::TYPE_ONE_SHOT, + "detail::RunnableMethodImpl::SetTimer"); + } + } +}; + +// Type aliases for NewRunnableMethod. +template <typename PtrType, typename Method> +using OwningRunnableMethod = + typename ::nsRunnableMethodTraits<std::remove_reference_t<PtrType>, Method, + true, RunnableKind::Standard>::base_type; +template <typename PtrType, typename Method, typename... Storages> +using OwningRunnableMethodImpl = + RunnableMethodImpl<std::remove_reference_t<PtrType>, Method, true, + RunnableKind::Standard, Storages...>; + +// Type aliases for NewCancelableRunnableMethod. +template <typename PtrType, typename Method> +using CancelableRunnableMethod = + typename ::nsRunnableMethodTraits<std::remove_reference_t<PtrType>, Method, + true, + RunnableKind::Cancelable>::base_type; +template <typename PtrType, typename Method, typename... Storages> +using CancelableRunnableMethodImpl = + RunnableMethodImpl<std::remove_reference_t<PtrType>, Method, true, + RunnableKind::Cancelable, Storages...>; + +// Type aliases for NewIdleRunnableMethod. +template <typename PtrType, typename Method> +using IdleRunnableMethod = + typename ::nsRunnableMethodTraits<std::remove_reference_t<PtrType>, Method, + true, RunnableKind::Idle>::base_type; +template <typename PtrType, typename Method, typename... Storages> +using IdleRunnableMethodImpl = + RunnableMethodImpl<std::remove_reference_t<PtrType>, Method, true, + RunnableKind::Idle, Storages...>; + +// Type aliases for NewIdleRunnableMethodWithTimer. +template <typename PtrType, typename Method> +using IdleRunnableMethodWithTimer = + typename ::nsRunnableMethodTraits<std::remove_reference_t<PtrType>, Method, + true, + RunnableKind::IdleWithTimer>::base_type; +template <typename PtrType, typename Method, typename... Storages> +using IdleRunnableMethodWithTimerImpl = + RunnableMethodImpl<std::remove_reference_t<PtrType>, Method, true, + RunnableKind::IdleWithTimer, Storages...>; + +// Type aliases for NewNonOwningRunnableMethod. +template <typename PtrType, typename Method> +using NonOwningRunnableMethod = + typename ::nsRunnableMethodTraits<std::remove_reference_t<PtrType>, Method, + false, RunnableKind::Standard>::base_type; +template <typename PtrType, typename Method, typename... Storages> +using NonOwningRunnableMethodImpl = + RunnableMethodImpl<std::remove_reference_t<PtrType>, Method, false, + RunnableKind::Standard, Storages...>; + +// Type aliases for NonOwningCancelableRunnableMethod +template <typename PtrType, typename Method> +using NonOwningCancelableRunnableMethod = + typename ::nsRunnableMethodTraits<std::remove_reference_t<PtrType>, Method, + false, + RunnableKind::Cancelable>::base_type; +template <typename PtrType, typename Method, typename... Storages> +using NonOwningCancelableRunnableMethodImpl = + RunnableMethodImpl<std::remove_reference_t<PtrType>, Method, false, + RunnableKind::Cancelable, Storages...>; + +// Type aliases for NonOwningIdleRunnableMethod +template <typename PtrType, typename Method> +using NonOwningIdleRunnableMethod = + typename ::nsRunnableMethodTraits<std::remove_reference_t<PtrType>, Method, + false, RunnableKind::Idle>::base_type; +template <typename PtrType, typename Method, typename... Storages> +using NonOwningIdleRunnableMethodImpl = + RunnableMethodImpl<std::remove_reference_t<PtrType>, Method, false, + RunnableKind::Idle, Storages...>; + +// Type aliases for NewIdleRunnableMethodWithTimer. +template <typename PtrType, typename Method> +using NonOwningIdleRunnableMethodWithTimer = + typename ::nsRunnableMethodTraits<std::remove_reference_t<PtrType>, Method, + false, + RunnableKind::IdleWithTimer>::base_type; +template <typename PtrType, typename Method, typename... Storages> +using NonOwningIdleRunnableMethodWithTimerImpl = + RunnableMethodImpl<std::remove_reference_t<PtrType>, 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<nsIRunnable> 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<nsIRunnable> 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<nsIRunnable> event = +// mozilla::NewRunnableMethod<RefPtr<T>, nsTArray<U>> +// ("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<T> ptr = ...; +// nsTArray<U> array = ...; +// nsCOMPtr<nsIRunnable> event = +// mozilla::NewRunnableMethod<RefPtr<T>, nsTArray<U>> +// ("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 <typename PtrType, typename Method> +already_AddRefed<detail::OwningRunnableMethod<PtrType, Method>> +NewRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod) { + return do_AddRef(new detail::OwningRunnableMethodImpl<PtrType, Method>( + aName, std::forward<PtrType>(aPtr), aMethod)); +} + +template <typename PtrType, typename Method> +already_AddRefed<detail::CancelableRunnableMethod<PtrType, Method>> +NewCancelableRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod) { + return do_AddRef(new detail::CancelableRunnableMethodImpl<PtrType, Method>( + aName, std::forward<PtrType>(aPtr), aMethod)); +} + +template <typename PtrType, typename Method> +already_AddRefed<detail::IdleRunnableMethod<PtrType, Method>> +NewIdleRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod) { + return do_AddRef(new detail::IdleRunnableMethodImpl<PtrType, Method>( + aName, std::forward<PtrType>(aPtr), aMethod)); +} + +template <typename PtrType, typename Method> +already_AddRefed<detail::IdleRunnableMethodWithTimer<PtrType, Method>> +NewIdleRunnableMethodWithTimer(const char* aName, PtrType&& aPtr, + Method aMethod) { + return do_AddRef(new detail::IdleRunnableMethodWithTimerImpl<PtrType, Method>( + aName, std::forward<PtrType>(aPtr), aMethod)); +} + +template <typename PtrType, typename Method> +already_AddRefed<detail::NonOwningRunnableMethod<PtrType, Method>> +NewNonOwningRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod) { + return do_AddRef(new detail::NonOwningRunnableMethodImpl<PtrType, Method>( + aName, std::forward<PtrType>(aPtr), aMethod)); +} + +template <typename PtrType, typename Method> +already_AddRefed<detail::NonOwningCancelableRunnableMethod<PtrType, Method>> +NewNonOwningCancelableRunnableMethod(const char* aName, PtrType&& aPtr, + Method aMethod) { + return do_AddRef( + new detail::NonOwningCancelableRunnableMethodImpl<PtrType, Method>( + aName, std::forward<PtrType>(aPtr), aMethod)); +} + +template <typename PtrType, typename Method> +already_AddRefed<detail::NonOwningIdleRunnableMethod<PtrType, Method>> +NewNonOwningIdleRunnableMethod(const char* aName, PtrType&& aPtr, + Method aMethod) { + return do_AddRef(new detail::NonOwningIdleRunnableMethodImpl<PtrType, Method>( + aName, std::forward<PtrType>(aPtr), aMethod)); +} + +template <typename PtrType, typename Method> +already_AddRefed<detail::NonOwningIdleRunnableMethodWithTimer<PtrType, Method>> +NewNonOwningIdleRunnableMethodWithTimer(const char* aName, PtrType&& aPtr, + Method aMethod) { + return do_AddRef( + new detail::NonOwningIdleRunnableMethodWithTimerImpl<PtrType, Method>( + aName, std::forward<PtrType>(aPtr), aMethod)); +} + +// Similar to NewRunnableMethod. Call like so: +// nsCOMPtr<nsIRunnable> event = +// NewRunnableMethod<Types,...>(myObject, &MyClass::HandleEvent, myArg1,...); +// 'Types' are the stored type for each argument, see ParameterStorage for +// details. +template <typename... Storages, typename PtrType, typename Method, + typename... Args> +already_AddRefed<detail::OwningRunnableMethod<PtrType, Method>> +NewRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod, + Args&&... aArgs) { + static_assert(sizeof...(Storages) == sizeof...(Args), + "<Storages...> size should be equal to number of arguments"); + return do_AddRef( + new detail::OwningRunnableMethodImpl<PtrType, Method, Storages...>( + aName, std::forward<PtrType>(aPtr), aMethod, + std::forward<Args>(aArgs)...)); +} + +template <typename... Storages, typename PtrType, typename Method, + typename... Args> +already_AddRefed<detail::NonOwningRunnableMethod<PtrType, Method>> +NewNonOwningRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod, + Args&&... aArgs) { + static_assert(sizeof...(Storages) == sizeof...(Args), + "<Storages...> size should be equal to number of arguments"); + return do_AddRef( + new detail::NonOwningRunnableMethodImpl<PtrType, Method, Storages...>( + aName, std::forward<PtrType>(aPtr), aMethod, + std::forward<Args>(aArgs)...)); +} + +template <typename... Storages, typename PtrType, typename Method, + typename... Args> +already_AddRefed<detail::CancelableRunnableMethod<PtrType, Method>> +NewCancelableRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod, + Args&&... aArgs) { + static_assert(sizeof...(Storages) == sizeof...(Args), + "<Storages...> size should be equal to number of arguments"); + return do_AddRef( + new detail::CancelableRunnableMethodImpl<PtrType, Method, Storages...>( + aName, std::forward<PtrType>(aPtr), aMethod, + std::forward<Args>(aArgs)...)); +} + +template <typename... Storages, typename PtrType, typename Method, + typename... Args> +already_AddRefed<detail::NonOwningCancelableRunnableMethod<PtrType, Method>> +NewNonOwningCancelableRunnableMethod(const char* aName, PtrType&& aPtr, + Method aMethod, Args&&... aArgs) { + static_assert(sizeof...(Storages) == sizeof...(Args), + "<Storages...> size should be equal to number of arguments"); + return do_AddRef( + new detail::NonOwningCancelableRunnableMethodImpl<PtrType, Method, + Storages...>( + aName, std::forward<PtrType>(aPtr), aMethod, + std::forward<Args>(aArgs)...)); +} + +template <typename... Storages, typename PtrType, typename Method, + typename... Args> +already_AddRefed<detail::IdleRunnableMethod<PtrType, Method>> +NewIdleRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod, + Args&&... aArgs) { + static_assert(sizeof...(Storages) == sizeof...(Args), + "<Storages...> size should be equal to number of arguments"); + return do_AddRef( + new detail::IdleRunnableMethodImpl<PtrType, Method, Storages...>( + aName, std::forward<PtrType>(aPtr), aMethod, + std::forward<Args>(aArgs)...)); +} + +template <typename... Storages, typename PtrType, typename Method, + typename... Args> +already_AddRefed<detail::NonOwningIdleRunnableMethod<PtrType, Method>> +NewNonOwningIdleRunnableMethod(const char* aName, PtrType&& aPtr, + Method aMethod, Args&&... aArgs) { + static_assert(sizeof...(Storages) == sizeof...(Args), + "<Storages...> size should be equal to number of arguments"); + return do_AddRef( + new detail::NonOwningIdleRunnableMethodImpl<PtrType, Method, Storages...>( + aName, std::forward<PtrType>(aPtr), aMethod, + std::forward<Args>(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<E> mEvent; +// }; +// +// void R::PostEvent() { +// // Make sure any pending event is revoked. +// mEvent->Revoke(); +// +// nsCOMPtr<nsIRunnable> 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 T> +class nsRevocableEventPtr { + public: + nsRevocableEventPtr() : mEvent(nullptr) {} + ~nsRevocableEventPtr() { Revoke(); } + + const nsRevocableEventPtr& operator=(RefPtr<T>&& 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<T> mEvent; +}; + +template <class T> +inline already_AddRefed<T> do_AddRef(nsRevocableEventPtr<T>& 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 "<aPoolName> #<n>" and increments the counter. + */ + nsCString GetNextThreadName(const nsACString& aPoolName); + + template <size_t LEN> + nsCString GetNextThreadName(const char (&aPoolName)[LEN]) { + return GetNextThreadName(nsDependentCString(aPoolName, LEN - 1)); + } + + private: + mozilla::Atomic<uint32_t> 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<nsIRunnable> 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); + +// Predeclaration for logging function below +namespace IPC { +class Message; +} + +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; +}; + +// These functions return event targets that can be used to dispatch to the +// current or main thread. They can also be used to test if you're on those +// threads (via IsOnCurrentThread). These functions should be used in preference +// to the nsIThread-based NS_Get{Current,Main}Thread functions since they will +// return more useful answers in the case of threads sharing an event loop. + +nsIEventTarget* GetCurrentEventTarget(); + +nsIEventTarget* GetMainThreadEventTarget(); + +// These variants of the above functions assert that the given thread has a +// serial event target (i.e., that it's not part of a thread pool) and returns +// that. + +nsISerialEventTarget* GetCurrentSerialEventTarget(); + +nsISerialEventTarget* GetMainThreadSerialEventTarget(); + +// Returns a wrapper around the current thread which routes normal dispatches +// through the tail dispatcher. +// This means that they will run at the end of the current task, rather than +// after all the subsequent tasks queued. This is useful to allow MozPromise +// callbacks returned by IPDL methods to avoid an extra trip through the event +// loop, and thus maintain correct ordering relative to other IPC events. The +// current thread implementation must support tail dispatch. +class TailDispatchingTarget : public nsISerialEventTarget { + public: + NS_DECL_THREADSAFE_ISUPPORTS + TailDispatchingTarget() +#if DEBUG + : mOwnerThread(AbstractThread::GetCurrent()) +#endif + { + MOZ_ASSERT(mOwnerThread, "Must be used with AbstractThreads"); + } + + NS_IMETHOD + Dispatch(already_AddRefed<nsIRunnable> event, uint32_t flags) override { + MOZ_ASSERT(flags == DISPATCH_NORMAL); + MOZ_ASSERT( + AbstractThread::GetCurrent() == mOwnerThread, + "TailDispatchingTarget can only be used on the thread upon which it " + "was created - see the comment on the class declaration."); + AbstractThread::DispatchDirectTask(std::move(event)); + return NS_OK; + } + NS_IMETHOD_(bool) IsOnCurrentThreadInfallible(void) override { return true; } + NS_IMETHOD IsOnCurrentThread(bool* _retval) override { + *_retval = true; + return NS_OK; + } + NS_IMETHOD DispatchFromScript(nsIRunnable* event, uint32_t flags) override { + MOZ_ASSERT_UNREACHABLE("not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; + } + NS_IMETHOD DelayedDispatch(already_AddRefed<nsIRunnable> event, + uint32_t delay) override { + MOZ_ASSERT_UNREACHABLE("not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; + } + + private: + virtual ~TailDispatchingTarget() = default; +#if DEBUG + const RefPtr<AbstractThread> mOwnerThread; +#endif +}; + +// 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<nsIRunnable> 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 <typename T> +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<nsIRunnable>::Run::Run(nsIRunnable* aEvent, bool aWillRunAgain); +template <> +LogTaskBase<Task>::Run::Run(Task* aTask, bool aWillRunAgain); +template <> +void LogTaskBase<IPC::Message>::LogDispatchWithPid(IPC::Message* aEvent, + int32_t aPid); +template <> +LogTaskBase<IPC::Message>::Run::Run(IPC::Message* aMessage, bool aWillRunAgain); +template <> +LogTaskBase<nsTimerImpl>::Run::Run(nsTimerImpl* aEvent, bool aWillRunAgain); + +typedef LogTaskBase<nsIRunnable> LogRunnable; +typedef LogTaskBase<MicroTaskRunnable> LogMicroTaskRunnable; +typedef LogTaskBase<IPC::Message> LogIPCMessage; +typedef LogTaskBase<nsTimerImpl> LogTimerEvent; +typedef LogTaskBase<Task> LogTask; +typedef LogTaskBase<PresShell> LogPresShellObserver; +typedef LogTaskBase<dom::FrameRequestCallback> LogFrameRequestCallback; +// If you add new types don't forget to add: +// `template class LogTaskBase<YourType>;` to nsThreadUtils.cpp + +class DelayedRunnable : public mozilla::Runnable, public nsITimerCallback { + public: + DelayedRunnable(already_AddRefed<nsIEventTarget> aTarget, + already_AddRefed<nsIRunnable> aRunnable, uint32_t aDelay); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIRUNNABLE + NS_DECL_NSITIMERCALLBACK + + nsresult Init(); + + private: + ~DelayedRunnable() = default; + nsresult DoRun(); + + const nsCOMPtr<nsIEventTarget> mTarget; + nsCOMPtr<nsIRunnable> mWrappedRunnable; + nsCOMPtr<nsITimer> mTimer; + const mozilla::TimeStamp mDelayedFrom; + uint32_t mDelay; +}; + +} // namespace mozilla + +#endif // nsThreadUtils_h__ |