michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsThreadUtils_h__ michael@0: #define nsThreadUtils_h__ michael@0: michael@0: #include "prthread.h" michael@0: #include "prinrval.h" michael@0: #include "MainThreadUtils.h" michael@0: #include "nsIThreadManager.h" michael@0: #include "nsIThread.h" michael@0: #include "nsIRunnable.h" michael@0: #include "nsICancelableRunnable.h" michael@0: #include "nsStringGlue.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "mozilla/Likely.h" michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // These methods are alternatives to the methods on nsIThreadManager, provided michael@0: // for convenience. michael@0: michael@0: /** michael@0: * Set name of the target thread. This operation is asynchronous. michael@0: */ michael@0: extern NS_COM_GLUE void michael@0: NS_SetThreadName(nsIThread *thread, const nsACString &name); michael@0: michael@0: /** michael@0: * Static length version of the above function checking length of the michael@0: * name at compile time. michael@0: */ michael@0: template michael@0: inline NS_COM_GLUE void michael@0: NS_SetThreadName(nsIThread *thread, const char (&name)[LEN]) michael@0: { michael@0: static_assert(LEN <= 16, michael@0: "Thread name must be no more than 16 characters"); michael@0: NS_SetThreadName(thread, nsDependentCString(name)); michael@0: } michael@0: michael@0: /** michael@0: * Create a new thread, and optionally provide an initial event for the thread. michael@0: * michael@0: * @param result michael@0: * The resulting nsIThread object. michael@0: * @param initialEvent michael@0: * The initial event to run on this thread. This parameter may be null. michael@0: * @param stackSize michael@0: * The size in bytes to reserve for the thread's stack. michael@0: * michael@0: * @returns NS_ERROR_INVALID_ARG michael@0: * Indicates that the given name is not unique. michael@0: */ michael@0: extern NS_COM_GLUE NS_METHOD michael@0: NS_NewThread(nsIThread **result, michael@0: nsIRunnable *initialEvent = nullptr, michael@0: uint32_t stackSize = nsIThreadManager::DEFAULT_STACK_SIZE); michael@0: michael@0: /** michael@0: * Creates a named thread, otherwise the same as NS_NewThread michael@0: */ michael@0: template michael@0: inline NS_METHOD michael@0: NS_NewNamedThread(const char (&name)[LEN], michael@0: nsIThread **result, michael@0: nsIRunnable *initialEvent = nullptr, michael@0: uint32_t stackSize = nsIThreadManager::DEFAULT_STACK_SIZE) michael@0: { michael@0: // Hold a ref while dispatching the initial event to match NS_NewThread() michael@0: nsCOMPtr thread; michael@0: nsresult rv = NS_NewThread(getter_AddRefs(thread), nullptr, stackSize); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: NS_SetThreadName(thread, name); michael@0: if (initialEvent) { michael@0: rv = thread->Dispatch(initialEvent, NS_DISPATCH_NORMAL); michael@0: NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Initial event dispatch failed"); michael@0: } michael@0: michael@0: *result = nullptr; michael@0: thread.swap(*result); michael@0: return rv; michael@0: } michael@0: michael@0: /** michael@0: * Get a reference to the current thread. michael@0: * michael@0: * @param result michael@0: * The resulting nsIThread object. michael@0: */ michael@0: extern NS_COM_GLUE NS_METHOD michael@0: NS_GetCurrentThread(nsIThread **result); michael@0: michael@0: /** michael@0: * Dispatch the given event to the current thread. michael@0: * michael@0: * @param event michael@0: * The event to dispatch. michael@0: * michael@0: * @returns NS_ERROR_INVALID_ARG michael@0: * If event is null. michael@0: */ michael@0: extern NS_COM_GLUE NS_METHOD michael@0: NS_DispatchToCurrentThread(nsIRunnable *event); michael@0: michael@0: /** michael@0: * Dispatch the given event to the main thread. michael@0: * michael@0: * @param event michael@0: * The event to dispatch. michael@0: * @param dispatchFlags michael@0: * The flags to pass to the main thread's dispatch method. michael@0: * michael@0: * @returns NS_ERROR_INVALID_ARG michael@0: * If event is null. michael@0: */ michael@0: extern NS_COM_GLUE NS_METHOD michael@0: NS_DispatchToMainThread(nsIRunnable *event, michael@0: uint32_t dispatchFlags = NS_DISPATCH_NORMAL); michael@0: michael@0: #ifndef XPCOM_GLUE_AVOID_NSPR michael@0: /** michael@0: * Process all pending events for the given thread before returning. This michael@0: * method simply calls ProcessNextEvent on the thread while HasPendingEvents michael@0: * continues to return true and the time spent in NS_ProcessPendingEvents michael@0: * does not exceed the given timeout value. michael@0: * michael@0: * @param thread michael@0: * The thread object for which to process pending events. If null, then michael@0: * events will be processed for the current thread. michael@0: * @param timeout michael@0: * The maximum number of milliseconds to spend processing pending events. michael@0: * Events are not pre-empted to honor this timeout. Rather, the timeout michael@0: * value is simply used to determine whether or not to process another event. michael@0: * Pass PR_INTERVAL_NO_TIMEOUT to specify no timeout. michael@0: */ michael@0: extern NS_COM_GLUE NS_METHOD michael@0: NS_ProcessPendingEvents(nsIThread *thread, michael@0: PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT); michael@0: #endif michael@0: michael@0: /** michael@0: * Shortcut for nsIThread::HasPendingEvents. michael@0: * michael@0: * It is an error to call this function when the given thread is not the michael@0: * current thread. This function will return false if called from some michael@0: * other thread. michael@0: * michael@0: * @param thread michael@0: * The current thread or null. michael@0: * michael@0: * @returns michael@0: * A boolean value that if "true" indicates that there are pending events michael@0: * in the current thread's event queue. michael@0: */ michael@0: extern NS_COM_GLUE bool michael@0: NS_HasPendingEvents(nsIThread *thread = nullptr); michael@0: michael@0: /** michael@0: * Shortcut for nsIThread::ProcessNextEvent. michael@0: * michael@0: * It is an error to call this function when the given thread is not the michael@0: * current thread. This function will simply return false if called michael@0: * from some other thread. michael@0: * michael@0: * @param thread michael@0: * The current thread or null. michael@0: * @param mayWait michael@0: * A boolean parameter that if "true" indicates that the method may block michael@0: * the calling thread to wait for a pending event. michael@0: * michael@0: * @returns michael@0: * A boolean value that if "true" indicates that an event from the current michael@0: * thread's event queue was processed. michael@0: */ michael@0: extern NS_COM_GLUE bool michael@0: NS_ProcessNextEvent(nsIThread *thread = nullptr, bool mayWait = true); michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // Helpers that work with nsCOMPtr: michael@0: michael@0: inline already_AddRefed michael@0: do_GetCurrentThread() { michael@0: nsIThread *thread = nullptr; michael@0: NS_GetCurrentThread(&thread); michael@0: return already_AddRefed(thread); michael@0: } michael@0: michael@0: inline already_AddRefed michael@0: do_GetMainThread() { michael@0: nsIThread *thread = nullptr; michael@0: NS_GetMainThread(&thread); michael@0: return already_AddRefed(thread); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: // Fast access to the current thread. Do not release the returned pointer! If michael@0: // you want to use this pointer from some other thread, then you will need to michael@0: // AddRef it. Otherwise, you should only consider this pointer valid from code michael@0: // running on the current thread. michael@0: extern NS_COM_GLUE nsIThread *NS_GetCurrentThread(); michael@0: #endif michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: #ifndef XPCOM_GLUE_AVOID_NSPR michael@0: michael@0: #undef IMETHOD_VISIBILITY michael@0: #define IMETHOD_VISIBILITY NS_COM_GLUE michael@0: michael@0: // This class is designed to be subclassed. michael@0: class NS_COM_GLUE nsRunnable : public nsIRunnable michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIRUNNABLE michael@0: michael@0: nsRunnable() { michael@0: } michael@0: michael@0: protected: michael@0: virtual ~nsRunnable() { michael@0: } michael@0: }; michael@0: michael@0: // This class is designed to be subclassed. michael@0: class NS_COM_GLUE nsCancelableRunnable : public nsICancelableRunnable michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIRUNNABLE michael@0: NS_DECL_NSICANCELABLERUNNABLE michael@0: michael@0: nsCancelableRunnable() { michael@0: } michael@0: michael@0: protected: michael@0: virtual ~nsCancelableRunnable() { michael@0: } michael@0: }; michael@0: michael@0: #undef IMETHOD_VISIBILITY michael@0: #define IMETHOD_VISIBILITY NS_VISIBILITY_HIDDEN michael@0: michael@0: // An event that can be used to call a method on a class. The class type must michael@0: // support reference counting. This event supports Revoke for use michael@0: // with nsRevocableEventPtr. michael@0: template michael@0: class nsRunnableMethod : public nsRunnable michael@0: { michael@0: public: michael@0: virtual void Revoke() = 0; michael@0: michael@0: // These ReturnTypeEnforcer classes set up a blacklist for return types that michael@0: // we know are not safe. The default ReturnTypeEnforcer compiles just fine but michael@0: // already_AddRefed will not. michael@0: template michael@0: class ReturnTypeEnforcer michael@0: { michael@0: public: michael@0: typedef int ReturnTypeIsSafe; michael@0: }; michael@0: michael@0: template michael@0: class ReturnTypeEnforcer > michael@0: { michael@0: // No ReturnTypeIsSafe makes this illegal! michael@0: }; michael@0: michael@0: // Make sure this return type is safe. michael@0: typedef typename ReturnTypeEnforcer::ReturnTypeIsSafe check; michael@0: }; michael@0: michael@0: template michael@0: struct nsRunnableMethodReceiver { michael@0: ClassType *mObj; michael@0: Arg mArg; michael@0: nsRunnableMethodReceiver(ClassType *obj, Arg arg) michael@0: : mObj(obj) michael@0: , mArg(arg) michael@0: { NS_IF_ADDREF(mObj); } michael@0: ~nsRunnableMethodReceiver() { Revoke(); } michael@0: void Revoke() { NS_IF_RELEASE(mObj); } michael@0: }; michael@0: michael@0: template michael@0: struct nsRunnableMethodReceiver { michael@0: ClassType *mObj; michael@0: nsRunnableMethodReceiver(ClassType *obj) : mObj(obj) michael@0: { NS_IF_ADDREF(mObj); } michael@0: ~nsRunnableMethodReceiver() { Revoke(); } michael@0: void Revoke() { NS_IF_RELEASE(mObj); } michael@0: }; michael@0: michael@0: template michael@0: struct nsRunnableMethodReceiver { michael@0: ClassType *mObj; michael@0: nsRunnableMethodReceiver(ClassType *obj) : mObj(obj) {} michael@0: void Revoke() { mObj = nullptr; } michael@0: }; michael@0: michael@0: template struct nsRunnableMethodTraits; michael@0: michael@0: template michael@0: struct nsRunnableMethodTraits { michael@0: typedef C class_type; michael@0: typedef R return_type; michael@0: typedef A arg_type; michael@0: typedef nsRunnableMethod base_type; michael@0: }; michael@0: michael@0: template michael@0: struct nsRunnableMethodTraits { michael@0: typedef C class_type; michael@0: typedef R return_type; michael@0: typedef void arg_type; michael@0: typedef nsRunnableMethod base_type; michael@0: }; michael@0: michael@0: #ifdef NS_HAVE_STDCALL michael@0: template michael@0: struct nsRunnableMethodTraits { michael@0: typedef C class_type; michael@0: typedef R return_type; michael@0: typedef A arg_type; michael@0: typedef nsRunnableMethod base_type; michael@0: }; michael@0: michael@0: template michael@0: struct nsRunnableMethodTraits { michael@0: typedef C class_type; michael@0: typedef R return_type; michael@0: typedef void arg_type; michael@0: typedef nsRunnableMethod base_type; michael@0: }; michael@0: #endif michael@0: michael@0: template michael@0: class nsRunnableMethodImpl michael@0: : public nsRunnableMethodTraits::base_type michael@0: { michael@0: typedef typename nsRunnableMethodTraits::class_type ClassType; michael@0: nsRunnableMethodReceiver mReceiver; michael@0: Method mMethod; michael@0: public: michael@0: nsRunnableMethodImpl(ClassType *obj, michael@0: Method method, michael@0: Arg arg) michael@0: : mReceiver(obj, arg) michael@0: , mMethod(method) michael@0: {} michael@0: NS_IMETHOD Run() { michael@0: if (MOZ_LIKELY(mReceiver.mObj)) michael@0: ((*mReceiver.mObj).*mMethod)(mReceiver.mArg); michael@0: return NS_OK; michael@0: } michael@0: void Revoke() { michael@0: mReceiver.Revoke(); michael@0: } michael@0: }; michael@0: michael@0: template michael@0: class nsRunnableMethodImpl michael@0: : public nsRunnableMethodTraits::base_type michael@0: { michael@0: typedef typename nsRunnableMethodTraits::class_type ClassType; michael@0: nsRunnableMethodReceiver mReceiver; michael@0: Method mMethod; michael@0: michael@0: public: michael@0: nsRunnableMethodImpl(ClassType *obj, michael@0: Method method) michael@0: : mReceiver(obj) michael@0: , mMethod(method) michael@0: {} michael@0: michael@0: NS_IMETHOD Run() { michael@0: if (MOZ_LIKELY(mReceiver.mObj)) michael@0: ((*mReceiver.mObj).*mMethod)(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void Revoke() { michael@0: mReceiver.Revoke(); michael@0: } michael@0: }; michael@0: michael@0: // Use this template function like so: michael@0: // michael@0: // nsCOMPtr event = michael@0: // NS_NewRunnableMethod(myObject, &MyClass::HandleEvent); michael@0: // NS_DispatchToCurrentThread(event); michael@0: // michael@0: // Statically enforced constraints: michael@0: // - myObject must be of (or implicitly convertible to) type MyClass michael@0: // - MyClass must defined AddRef and Release methods michael@0: // michael@0: template michael@0: typename nsRunnableMethodTraits::base_type* michael@0: NS_NewRunnableMethod(PtrType ptr, Method method) michael@0: { michael@0: return new nsRunnableMethodImpl(ptr, method); michael@0: } michael@0: michael@0: template michael@0: struct dependent_type michael@0: { michael@0: typedef T type; michael@0: }; michael@0: michael@0: michael@0: // Similar to NS_NewRunnableMethod. Call like so: michael@0: // Type myArg; michael@0: // nsCOMPtr event = michael@0: // NS_NewRunnableMethodWithArg(myObject, &MyClass::HandleEvent, myArg); michael@0: template michael@0: typename nsRunnableMethodTraits::base_type* michael@0: NS_NewRunnableMethodWithArg(PtrType ptr, Method method, typename dependent_type::type arg) michael@0: { michael@0: return new nsRunnableMethodImpl(ptr, method, arg); michael@0: } michael@0: michael@0: template michael@0: typename nsRunnableMethodTraits::base_type* michael@0: NS_NewNonOwningRunnableMethod(PtrType ptr, Method method) michael@0: { michael@0: return new nsRunnableMethodImpl(ptr, method); michael@0: } michael@0: michael@0: #endif // XPCOM_GLUE_AVOID_NSPR michael@0: michael@0: // This class is designed to be used when you have an event class E that has a michael@0: // pointer back to resource class R. If R goes away while E is still pending, michael@0: // then it is important to "revoke" E so that it does not try use R after R has michael@0: // been destroyed. nsRevocableEventPtr makes it easy for R to manage such michael@0: // situations: michael@0: // michael@0: // class R; michael@0: // michael@0: // class E : public nsRunnable { michael@0: // public: michael@0: // void Revoke() { michael@0: // mResource = nullptr; michael@0: // } michael@0: // private: michael@0: // R *mResource; michael@0: // }; michael@0: // michael@0: // class R { michael@0: // public: michael@0: // void EventHandled() { michael@0: // mEvent.Forget(); michael@0: // } michael@0: // private: michael@0: // nsRevocableEventPtr mEvent; michael@0: // }; michael@0: // michael@0: // void R::PostEvent() { michael@0: // // Make sure any pending event is revoked. michael@0: // mEvent->Revoke(); michael@0: // michael@0: // nsCOMPtr event = new E(); michael@0: // if (NS_SUCCEEDED(NS_DispatchToCurrentThread(event))) { michael@0: // // Keep pointer to event so we can revoke it. michael@0: // mEvent = event; michael@0: // } michael@0: // } michael@0: // michael@0: // NS_IMETHODIMP E::Run() { michael@0: // if (!mResource) michael@0: // return NS_OK; michael@0: // ... michael@0: // mResource->EventHandled(); michael@0: // return NS_OK; michael@0: // } michael@0: // michael@0: template michael@0: class nsRevocableEventPtr { michael@0: public: michael@0: nsRevocableEventPtr() michael@0: : mEvent(nullptr) { michael@0: } michael@0: michael@0: ~nsRevocableEventPtr() { michael@0: Revoke(); michael@0: } michael@0: michael@0: const nsRevocableEventPtr& operator=(T *event) { michael@0: if (mEvent != event) { michael@0: Revoke(); michael@0: mEvent = event; michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: void Revoke() { michael@0: if (mEvent) { michael@0: mEvent->Revoke(); michael@0: mEvent = nullptr; michael@0: } michael@0: } michael@0: michael@0: void Forget() { michael@0: mEvent = nullptr; michael@0: } michael@0: michael@0: bool IsPending() { michael@0: return mEvent != nullptr; michael@0: } michael@0: michael@0: T *get() { return mEvent; } michael@0: michael@0: private: michael@0: // Not implemented michael@0: nsRevocableEventPtr(const nsRevocableEventPtr&); michael@0: nsRevocableEventPtr& operator=(const nsRevocableEventPtr&); michael@0: michael@0: nsRefPtr mEvent; michael@0: }; michael@0: michael@0: /** michael@0: * A simple helper to suffix thread pool name michael@0: * with incremental numbers. michael@0: */ michael@0: class nsThreadPoolNaming michael@0: { michael@0: public: michael@0: nsThreadPoolNaming() : mCounter(0) {} michael@0: michael@0: /** michael@0: * Creates and sets next thread name as " #" michael@0: * on the specified thread. If no thread is specified (aThread michael@0: * is null) then the name is synchronously set on the current thread. michael@0: */ michael@0: void SetThreadPoolName(const nsACString & aPoolName, michael@0: nsIThread * aThread = nullptr); michael@0: michael@0: private: michael@0: volatile uint32_t mCounter; michael@0: michael@0: nsThreadPoolNaming(const nsThreadPoolNaming &) MOZ_DELETE; michael@0: void operator=(const nsThreadPoolNaming &) MOZ_DELETE; michael@0: }; michael@0: michael@0: /** michael@0: * Thread priority in most operating systems affect scheduling, not IO. This michael@0: * helper is used to set the current thread to low IO priority for the lifetime michael@0: * of the created object. You can only use this low priority IO setting within michael@0: * the context of the current thread. michael@0: */ michael@0: class MOZ_STACK_CLASS nsAutoLowPriorityIO michael@0: { michael@0: public: michael@0: nsAutoLowPriorityIO(); michael@0: ~nsAutoLowPriorityIO(); michael@0: michael@0: private: michael@0: bool lowIOPrioritySet; michael@0: #if defined(XP_MACOSX) michael@0: int oldPriority; michael@0: #endif michael@0: }; michael@0: michael@0: michael@0: michael@0: #endif // nsThreadUtils_h__