diff --git a/xpcom/glue/nsThreadUtils.cpp b/xpcom/glue/nsThreadUtils.cpp index c24b06736b..28f7f54397 100644 --- a/xpcom/glue/nsThreadUtils.cpp +++ b/xpcom/glue/nsThreadUtils.cpp @@ -63,11 +63,15 @@ CancelableRunnable::Cancel() NS_IMPL_ISUPPORTS_INHERITED(IdleRunnable, CancelableRunnable, nsIIdleRunnable) -void -IdleRunnable::SetDeadline(TimeStamp aDeadline) +namespace mozilla { +namespace detail { +already_AddRefed CreateTimer() { - // Do nothing + nsCOMPtr timer = do_CreateInstance(NS_TIMER_CONTRACTID); + return timer.forget(); } +} // namespace detail +} // namespace mozilla #endif // XPCOM_GLUE_AVOID_NSPR @@ -246,6 +250,7 @@ NS_IdleDispatchToCurrentThread(already_AddRefed&& aEvent) { nsresult rv; nsCOMPtr event(aEvent); + NS_ENSURE_TRUE(event, NS_ERROR_INVALID_ARG); #ifdef MOZILLA_INTERNAL_API nsIThread* thread = NS_GetCurrentThread(); if (!thread) { @@ -272,6 +277,80 @@ NS_IdleDispatchToCurrentThread(already_AddRefed&& aEvent) return rv; } +class IdleRunnableWrapper : public IdleRunnable +{ +public: + explicit IdleRunnableWrapper(already_AddRefed&& aEvent) + : mRunnable(Move(aEvent)) + { + } + + NS_IMETHOD Run() override + { + if (!mRunnable) { + return NS_OK; + } + CancelTimer(); + nsCOMPtr runnable = mRunnable.forget(); + return runnable->Run(); + } + + static void + TimedOut(nsITimer* aTimer, void* aClosure) + { + RefPtr runnable = + static_cast(aClosure); + runnable->Run(); + } + + void SetTimer(uint32_t aDelay, nsIEventTarget* aTarget) override + { + MOZ_ASSERT(aTarget); + MOZ_ASSERT(!mTimer); + mTimer = do_CreateInstance(NS_TIMER_CONTRACTID); + if (mTimer) { + mTimer->SetTarget(aTarget); + mTimer->InitWithFuncCallback(TimedOut, this, aDelay, + nsITimer::TYPE_ONE_SHOT); + } + } +private: + ~IdleRunnableWrapper() + { + CancelTimer(); + } + + void CancelTimer() + { + if (mTimer) { + mTimer->Cancel(); + } + } + + nsCOMPtr mTimer; + nsCOMPtr mRunnable; +}; + +extern nsresult +NS_IdleDispatchToCurrentThread(already_AddRefed&& aEvent, + uint32_t aTimeout) +{ + nsCOMPtr event(Move(aEvent)); + NS_ENSURE_TRUE(event, NS_ERROR_INVALID_ARG); + nsCOMPtr idleEvent = do_QueryInterface(event); + + if (!idleEvent) { + idleEvent = new IdleRunnableWrapper(event.forget()); + event = do_QueryInterface(idleEvent); + MOZ_DIAGNOSTIC_ASSERT(event); + } + + //XXX Using current thread for now as the nsIEventTarget. + idleEvent->SetTimer(aTimeout, NS_GetCurrentThread()); + + return NS_IdleDispatchToCurrentThread(event.forget()); +} + #ifndef XPCOM_GLUE_AVOID_NSPR nsresult NS_ProcessPendingEvents(nsIThread* aThread, PRIntervalTime aTimeout) diff --git a/xpcom/glue/nsThreadUtils.h b/xpcom/glue/nsThreadUtils.h index f222944ae1..dbdf0b674c 100644 --- a/xpcom/glue/nsThreadUtils.h +++ b/xpcom/glue/nsThreadUtils.h @@ -10,6 +10,7 @@ #include "prinrval.h" #include "MainThreadUtils.h" #include "nsIThreadManager.h" +#include "nsITimer.h" #include "nsIThread.h" #include "nsIRunnable.h" #include "nsICancelableRunnable.h" @@ -136,9 +137,43 @@ extern nsresult NS_DelayedDispatchToCurrentThread( already_AddRefed&& aEvent, uint32_t aDelayMs); +/** + * Dispatch the given event to the idle queue of the current thread. + * + * @param aEvent + * The event to dispatch. + * + * @returns NS_ERROR_INVALID_ARG + * If event is null. + * @returns NS_ERROR_UNEXPECTED + * If the thread is shutting down. + */ + extern nsresult NS_IdleDispatchToCurrentThread(already_AddRefed&& aEvent); +/** + * Dispatch the given event to the 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 the 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. + * + * @returns NS_ERROR_INVALID_ARG + * If event is null. + * @returns NS_ERROR_UNEXPECTED + * If the thread is shutting down. + */ +extern nsresult +NS_IdleDispatchToCurrentThread(already_AddRefed&& aEvent, uint32_t aTimeout); + #ifndef XPCOM_GLUE_AVOID_NSPR /** * Process all pending events for the given thread before returning. This @@ -248,6 +283,16 @@ private: IdlePeriod& operator=(const IdlePeriod&&) = delete; }; +// Cancelable runnable methods implement nsICancelableRunnable, and +// Idle and IdleWithTimer also nsIIdleRunnable. +enum RunnableKind +{ + Standard, + Cancelable, + Idle, + IdleWithTimer +}; + // This class is designed to be subclassed. class Runnable : public nsIRunnable { @@ -286,14 +331,13 @@ private: // This class is designed to be subclassed. class IdleRunnable : public CancelableRunnable, - public nsIIdleRunnable + public nsIIdleRunnable { public: NS_DECL_ISUPPORTS_INHERITED - // nsIIdleRunnable - virtual void SetDeadline(TimeStamp aDeadline) override; IdleRunnable() {} + explicit IdleRunnable(const char* aName) : CancelableRunnable(aName) {} protected: virtual ~IdleRunnable() {} @@ -342,16 +386,69 @@ NS_NewRunnableFunction(Function&& aFunction) (mozilla::Forward(aFunction))); } +namespace mozilla { +namespace detail { + +already_AddRefed CreateTimer(); + +template +class TimerBehaviour +{ +public: + nsITimer* GetTimer() { return nullptr; } + void CancelTimer() {} + +protected: + ~TimerBehaviour() {} +}; + +template <> +class TimerBehaviour +{ +public: + nsITimer* GetTimer() + { + if (!mTimer) { + mTimer = CreateTimer(); + } + + 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 mozilla::Conditional::Type + mozilla::RunnableKind Kind = mozilla::Standard> +class nsRunnableMethod + : public mozilla::Conditional::Type>::Type, + protected mozilla::detail::TimerBehaviour { public: virtual void Revoke() = 0; @@ -376,7 +473,7 @@ public: typedef typename ReturnTypeEnforcer::ReturnTypeIsSafe check; }; -template +template struct nsRunnableMethodReceiver { RefPtr mObj; @@ -384,71 +481,101 @@ struct nsRunnableMethodReceiver ~nsRunnableMethodReceiver() { Revoke(); } ClassType* Get() const { return mObj.get(); } void Revoke() { mObj = nullptr; } + void SetDeadline(mozilla::TimeStamp aDeadline) { if (mObj) mObj->SetDeadline(aDeadline); } }; template -struct nsRunnableMethodReceiver +struct nsRunnableMethodReceiver { ClassType* MOZ_NON_OWNING_REF mObj; explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {} ClassType* Get() const { return mObj; } void Revoke() { mObj = nullptr; } + void SetDeadline(mozilla::TimeStamp aDeadline) {} }; -template struct nsRunnableMethodTraits; +template +struct nsRunnableMethodReceiver +{ + ClassType* MOZ_NON_OWNING_REF mObj; + explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {} + ClassType* Get() const { return mObj; } + void Revoke() { mObj = nullptr; } + void SetDeadline(mozilla::TimeStamp aDeadline) { if (mObj) mObj->SetDeadline(aDeadline); } +}; -template -struct nsRunnableMethodTraits +static inline constexpr bool +IsIdle(mozilla::RunnableKind aKind) +{ + return aKind == mozilla::Idle || aKind == mozilla::IdleWithTimer; +} + +template +struct nsRunnableMethodReceiver +{ + RefPtr mObj; + explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {} + ~nsRunnableMethodReceiver() { Revoke(); } + ClassType* Get() const { return mObj.get(); } + void Revoke() { mObj = nullptr; } + void SetDeadline(mozilla::TimeStamp aDeadline) {} +}; + +template +struct nsRunnableMethodTraits; + +template +struct nsRunnableMethodTraits { typedef C class_type; typedef R return_type; - typedef nsRunnableMethod base_type; - static const bool can_cancel = Cancelable; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Kind == mozilla::Cancelable; }; -template -struct nsRunnableMethodTraits +template +struct nsRunnableMethodTraits { typedef const C class_type; typedef R return_type; - typedef nsRunnableMethod base_type; - static const bool can_cancel = Cancelable; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Kind == mozilla::Cancelable; }; #ifdef NS_HAVE_STDCALL -template -struct nsRunnableMethodTraits +template +struct nsRunnableMethodTraits { typedef C class_type; typedef R return_type; - typedef nsRunnableMethod base_type; - static const bool can_cancel = Cancelable; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Kind == mozilla::Cancelable; }; -template -struct nsRunnableMethodTraits +template +struct nsRunnableMethodTraits { typedef C class_type; typedef R return_type; - typedef nsRunnableMethod base_type; - static const bool can_cancel = Cancelable; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Kind == mozilla::Cancelable }; -template -struct nsRunnableMethodTraits +template +struct nsRunnableMethodTraits { typedef const C class_type; typedef R return_type; - typedef nsRunnableMethod base_type; - static const bool can_cancel = Cancelable; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Kind == mozilla::Cancelable }; -template +template struct nsRunnableMethodTraits { typedef const C class_type; typedef R return_type; - typedef nsRunnableMethod base_type; - static const bool can_cancel = Cancelable; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Kind == mozilla::Cancelable; }; #endif @@ -782,17 +909,28 @@ struct RunnableMethodArguments final } }; -template +template class RunnableMethodImpl final - : public ::nsRunnableMethodTraits::base_type + : public ::nsRunnableMethodTraits::base_type { - typedef typename ::nsRunnableMethodTraits::class_type - ClassType; - ::nsRunnableMethodReceiver mReceiver; + typedef typename Traits::class_type ClassType; + typedef typename Traits::base_type BaseType; + ::nsRunnableMethodReceiver mReceiver; + Method mMethod; RunnableMethodArguments mArgs; + using BaseType::GetTimer; + using BaseType::CancelTimer; 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(ClassType* aObj, Method aMethod, @@ -805,19 +943,105 @@ public: } NS_IMETHOD Run() { + CancelTimer(); if (MOZ_LIKELY(mReceiver.Get())) { mArgs.apply(mReceiver.Get(), mMethod); } return NS_OK; } - nsresult Cancel() { - static_assert(Cancelable, "Don't use me!"); + nsresult Cancel() + { + static_assert(Kind >= Cancelable, "Don't use me!"); Revoke(); return NS_OK; } - void Revoke() { mReceiver.Revoke(); } + void Revoke() + { + CancelTimer(); + mReceiver.Revoke(); + } + + void SetDeadline(TimeStamp aDeadline) + { + mReceiver.SetDeadline(aDeadline); + } + + void SetTimer(uint32_t aDelay, nsIEventTarget* aTarget) + { + MOZ_ASSERT(aTarget); + + if (nsCOMPtr timer = GetTimer()) { + timer->Cancel(); + timer->SetTarget(aTarget); + timer->InitWithFuncCallback(TimedOut, this, aDelay, + nsITimer::TYPE_ONE_SHOT); + } + } }; +// Type aliases for NewRunnableMethod. +template +using OwningRunnableMethod = typename ::nsRunnableMethodTraits< + typename RemoveReference::Type, Method, true, Standard>::base_type; +template +using OwningRunnableMethodImpl = RunnableMethodImpl< + typename RemoveReference::Type, Method, true, Standard, Storages...>; + +// Type aliases for NewCancelableRunnableMethod. +template +using CancelableRunnableMethod = typename ::nsRunnableMethodTraits< + typename RemoveReference::Type, Method, true, Cancelable>::base_type; +template +using CancelableRunnableMethodImpl = RunnableMethodImpl< + typename RemoveReference::Type, Method, true, Cancelable, Storages...>; + +// Type aliases for NewIdleRunnableMethod. +template +using IdleRunnableMethod = typename ::nsRunnableMethodTraits< + typename RemoveReference::Type, Method, true, Idle>::base_type; +template +using IdleRunnableMethodImpl = RunnableMethodImpl< + typename RemoveReference::Type, Method, true, Idle, Storages...>; + +// Type aliases for NewIdleRunnableMethodWithTimer. +template +using IdleRunnableMethodWithTimer = typename ::nsRunnableMethodTraits< + typename RemoveReference::Type, Method, true, IdleWithTimer>::base_type; +template +using IdleRunnableMethodWithTimerImpl = RunnableMethodImpl< + typename RemoveReference::Type, Method, true, IdleWithTimer, Storages...>; + +// Type aliases for NewNonOwningRunnableMethod. +template +using NonOwningRunnableMethod = typename ::nsRunnableMethodTraits< + typename RemoveReference::Type, Method, false, Standard>::base_type; +template +using NonOwningRunnableMethodImpl = RunnableMethodImpl< + typename RemoveReference::Type, Method, false, Standard, Storages...>; + +// Type aliases for NonOwningCancelableRunnableMethod +template +using NonOwningCancelableRunnableMethod = typename ::nsRunnableMethodTraits< + typename RemoveReference::Type, Method, false, Cancelable>::base_type; +template +using NonOwningCancelableRunnableMethodImpl = RunnableMethodImpl< + typename RemoveReference::Type, Method, false, Cancelable, Storages...>; + +// Type aliases for NonOwningIdleRunnableMethod +template +using NonOwningIdleRunnableMethod = typename ::nsRunnableMethodTraits< + typename RemoveReference::Type, Method, false, Idle>::base_type; +template +using NonOwningIdleRunnableMethodImpl = RunnableMethodImpl< + typename RemoveReference::Type, Method, false, Idle, Storages...>; + +// Type aliases for NewIdleRunnableMethodWithTimer. +template +using NonOwningIdleRunnableMethodWithTimer = typename ::nsRunnableMethodTraits< + typename RemoveReference::Type, Method, false, IdleWithTimer>::base_type; +template +using NonOwningIdleRunnableMethodWithTimerImpl = RunnableMethodImpl< + typename RemoveReference::Type, Method, false, IdleWithTimer, Storages...>; } // namespace detail // Use this template function like so: @@ -845,6 +1069,41 @@ NewCancelableRunnableMethod(PtrType aPtr, Method aMethod) return do_AddRef(new detail::RunnableMethodImpl(aPtr, aMethod)); } +already_AddRefed> +NewIdleRunnableMethod(PtrType&& aPtr, Method aMethod) +{ + return do_AddRef(new detail::IdleRunnableMethodImpl( + Forward(aPtr), aMethod)); +} + +template +already_AddRefed> +NewIdleRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod) +{ + return detail::SetRunnableName( + NewIdleRunnableMethod(Forward(aPtr), aMethod), aName); +} + +template +already_AddRefed> +NewIdleRunnableMethodWithTimer(PtrType&& aPtr, Method aMethod) +{ + return do_AddRef(new detail::IdleRunnableMethodWithTimerImpl( + Forward(aPtr), aMethod)); +} + +template +already_AddRefed> +NewIdleRunnableMethodWithTimer(const char* aName, + PtrType&& aPtr, + Method aMethod) +{ + return detail::SetRunnableName( + NewIdleRunnableMethodWithTimer(Forward(aPtr), aMethod), + aName); +} + + template already_AddRefed::base_type> NewNonOwningRunnableMethod(PtrType&& aPtr, Method aMethod) @@ -859,6 +1118,46 @@ NewNonOwningCancelableRunnableMethod(PtrType&& aPtr, Method aMethod) return do_AddRef(new detail::RunnableMethodImpl(aPtr, aMethod)); } + +template +already_AddRefed> +NewNonOwningIdleRunnableMethod(PtrType&& aPtr, Method aMethod) +{ + return do_AddRef( + new detail::NonOwningIdleRunnableMethodImpl( + Forward(aPtr), aMethod)); +} + +template +already_AddRefed> +NewNonOwningIdleRunnableMethod(const char* aName, + PtrType&& aPtr, + Method aMethod) +{ + return detail::SetRunnableName( + NewNonOwningIdleRunnableMethod(Forward(aPtr), aMethod), aName); +} + +template +already_AddRefed> +NewNonOwningIdleRunnableMethodWithTimer(PtrType&& aPtr, + Method aMethod) +{ + return do_AddRef( + new detail::NonOwningIdleRunnableMethodWithTimerImpl( + Forward(aPtr), aMethod)); +} + +template +already_AddRefed> +NewNonOwningIdleRunnableMethodWithTimer(const char* aName, + PtrType&& aPtr, + Method aMethod) +{ + return detail::SetRunnableName(NewNonOwningIdleRunnableMethodWithTimer( + Forward(aPtr), aMethod), + aName); + // Similar to NewRunnableMethod. Call like so: // nsCOMPtr event = // NewRunnableMethod(myObject, &MyClass::HandleEvent, myArg1,...); @@ -904,6 +1203,71 @@ NewNonOwningCancelableRunnableMethod(PtrType&& aPtr, Method aMethod, aPtr, aMethod, mozilla::Forward(aArgs)...)); } + +template +already_AddRefed> +NewIdleRunnableMethod(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( + Forward(aPtr), aMethod, mozilla::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 detail::SetRunnableName( + NewIdleRunnableMethod( + Forward(aPtr), aMethod, mozilla::Forward(aArgs)...), + aName); +} + +template +already_AddRefed> +NewNonOwningIdleRunnableMethod(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( + Forward(aPtr), aMethod, mozilla::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 detail::SetRunnableName( + NewNonOwningIdleRunnableMethod( + Forward(aPtr), aMethod, mozilla::Forward(aArgs)...), + aName); +} + } // namespace mozilla #endif // XPCOM_GLUE_AVOID_NSPR diff --git a/xpcom/threads/nsIIdleRunnable.h b/xpcom/threads/nsIIdleRunnable.h index 7263604eba..4d4b32c8af 100644 --- a/xpcom/threads/nsIIdleRunnable.h +++ b/xpcom/threads/nsIIdleRunnable.h @@ -13,6 +13,8 @@ { 0x688be92e, 0x7ade, 0x4fdc, \ { 0x9d, 0x83, 0x74, 0xcb, 0xef, 0xf4, 0xa5, 0x2c } } +class nsIEventTarget; + /** * A task interface for tasks that can schedule their work to happen @@ -27,7 +29,12 @@ public: * Notify the task of a point in time in the future when the task * should stop executing. */ - virtual void SetDeadline(mozilla::TimeStamp aDeadline) = 0; + virtual void SetDeadline(mozilla::TimeStamp aDeadline) {}; + virtual void SetTimer(uint32_t aDelay, nsIEventTarget* aTarget) + { + NS_NOTREACHED("The nsIIdleRunnable instance does not support " + "idle dispatch with timeout!"); + }; protected: nsIIdleRunnable() { } diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 23dfef4021..7f5a82bf7d 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -1044,9 +1044,9 @@ nsThread::GetIdleEvent(nsIRunnable** aEvent, MutexAutoLock& aProofOfLock) mIdleEvents.GetEvent(false, aEvent, aProofOfLock); if (*aEvent) { - nsCOMPtr incrementalEvent(do_QueryInterface(*aEvent)); - if (incrementalEvent) { - incrementalEvent->SetDeadline(idleDeadline); + nsCOMPtr idleEvent(do_QueryInterface(*aEvent)); + if (idleEvent) { + idleEvent->SetDeadline(idleDeadline); } } }