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: #include "nsThreadUtils.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/Likely.h" michael@0: michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: # include "nsThreadManager.h" michael@0: #else michael@0: # include "nsXPCOMCIDInternal.h" michael@0: # include "nsIThreadManager.h" michael@0: # include "nsServiceManagerUtils.h" michael@0: #endif michael@0: michael@0: #ifdef XP_WIN michael@0: #include michael@0: #include "mozilla/WindowsVersion.h" michael@0: using mozilla::IsVistaOrLater; michael@0: #elif defined(XP_MACOSX) michael@0: #include michael@0: #endif michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #ifndef XPCOM_GLUE_AVOID_NSPR michael@0: michael@0: NS_IMPL_ISUPPORTS(nsRunnable, nsIRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: nsRunnable::Run() michael@0: { michael@0: // Do nothing michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsCancelableRunnable, nsICancelableRunnable, michael@0: nsIRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: nsCancelableRunnable::Run() michael@0: { michael@0: // Do nothing michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCancelableRunnable::Cancel() michael@0: { michael@0: // Do nothing michael@0: return NS_OK; michael@0: } michael@0: michael@0: #endif // XPCOM_GLUE_AVOID_NSPR michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_METHOD michael@0: NS_NewThread(nsIThread **result, nsIRunnable *event, uint32_t stackSize) michael@0: { michael@0: nsCOMPtr thread; michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: nsresult rv = nsThreadManager::get()-> michael@0: nsThreadManager::NewThread(0, stackSize, getter_AddRefs(thread)); michael@0: #else michael@0: nsresult rv; michael@0: nsCOMPtr mgr = michael@0: do_GetService(NS_THREADMANAGER_CONTRACTID, &rv); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: rv = mgr->NewThread(0, stackSize, getter_AddRefs(thread)); michael@0: #endif michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: if (event) { michael@0: rv = thread->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: } michael@0: michael@0: *result = nullptr; michael@0: thread.swap(*result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_METHOD michael@0: NS_GetCurrentThread(nsIThread **result) michael@0: { michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: return nsThreadManager::get()->nsThreadManager::GetCurrentThread(result); michael@0: #else michael@0: nsresult rv; michael@0: nsCOMPtr mgr = michael@0: do_GetService(NS_THREADMANAGER_CONTRACTID, &rv); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: return mgr->GetCurrentThread(result); michael@0: #endif michael@0: } michael@0: michael@0: NS_METHOD michael@0: NS_GetMainThread(nsIThread **result) michael@0: { michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: return nsThreadManager::get()->nsThreadManager::GetMainThread(result); michael@0: #else michael@0: nsresult rv; michael@0: nsCOMPtr mgr = michael@0: do_GetService(NS_THREADMANAGER_CONTRACTID, &rv); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: return mgr->GetMainThread(result); michael@0: #endif michael@0: } michael@0: michael@0: #if defined(MOZILLA_INTERNAL_API) && defined(XP_WIN) michael@0: extern DWORD gTLSThreadIDIndex; michael@0: bool michael@0: NS_IsMainThread() michael@0: { michael@0: return TlsGetValue(gTLSThreadIDIndex) == (void*) mozilla::threads::Main; michael@0: } michael@0: #elif defined(MOZILLA_INTERNAL_API) && defined(NS_TLS) michael@0: #ifdef MOZ_ASAN michael@0: // Temporary workaround, see bug 895845 michael@0: bool NS_IsMainThread() michael@0: { michael@0: return gTLSThreadID == mozilla::threads::Main; michael@0: } michael@0: #else michael@0: // NS_IsMainThread() is defined inline in MainThreadUtils.h michael@0: #endif michael@0: #else michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: bool NS_IsMainThread() michael@0: { michael@0: bool result = false; michael@0: nsThreadManager::get()->nsThreadManager::GetIsMainThread(&result); michael@0: return bool(result); michael@0: } michael@0: #else michael@0: bool NS_IsMainThread() michael@0: { michael@0: bool result = false; michael@0: nsCOMPtr mgr = michael@0: do_GetService(NS_THREADMANAGER_CONTRACTID); michael@0: if (mgr) michael@0: mgr->GetIsMainThread(&result); michael@0: return bool(result); michael@0: } michael@0: #endif michael@0: #endif michael@0: michael@0: NS_METHOD michael@0: NS_DispatchToCurrentThread(nsIRunnable *event) michael@0: { michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: nsIThread *thread = NS_GetCurrentThread(); michael@0: if (!thread) { return NS_ERROR_UNEXPECTED; } michael@0: #else michael@0: nsCOMPtr thread; michael@0: nsresult rv = NS_GetCurrentThread(getter_AddRefs(thread)); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: #endif michael@0: return thread->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: NS_METHOD michael@0: NS_DispatchToMainThread(nsIRunnable *event, uint32_t dispatchFlags) michael@0: { michael@0: nsCOMPtr thread; michael@0: nsresult rv = NS_GetMainThread(getter_AddRefs(thread)); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: return thread->Dispatch(event, dispatchFlags); michael@0: } michael@0: michael@0: #ifndef XPCOM_GLUE_AVOID_NSPR michael@0: NS_METHOD michael@0: NS_ProcessPendingEvents(nsIThread *thread, PRIntervalTime timeout) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: if (!thread) { michael@0: thread = NS_GetCurrentThread(); michael@0: if (NS_WARN_IF(!thread)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: #else michael@0: nsCOMPtr current; michael@0: if (!thread) { michael@0: rv = NS_GetCurrentThread(getter_AddRefs(current)); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: thread = current.get(); michael@0: } michael@0: #endif michael@0: michael@0: PRIntervalTime start = PR_IntervalNow(); michael@0: for (;;) { michael@0: bool processedEvent; michael@0: rv = thread->ProcessNextEvent(false, &processedEvent); michael@0: if (NS_FAILED(rv) || !processedEvent) michael@0: break; michael@0: if (PR_IntervalNow() - start > timeout) michael@0: break; michael@0: } michael@0: return rv; michael@0: } michael@0: #endif // XPCOM_GLUE_AVOID_NSPR michael@0: michael@0: inline bool michael@0: hasPendingEvents(nsIThread *thread) michael@0: { michael@0: bool val; michael@0: return NS_SUCCEEDED(thread->HasPendingEvents(&val)) && val; michael@0: } michael@0: michael@0: bool michael@0: NS_HasPendingEvents(nsIThread *thread) michael@0: { michael@0: if (!thread) { michael@0: #ifndef MOZILLA_INTERNAL_API michael@0: nsCOMPtr current; michael@0: NS_GetCurrentThread(getter_AddRefs(current)); michael@0: return hasPendingEvents(current); michael@0: #else michael@0: thread = NS_GetCurrentThread(); michael@0: if (NS_WARN_IF(!thread)) michael@0: return false; michael@0: #endif michael@0: } michael@0: return hasPendingEvents(thread); michael@0: } michael@0: michael@0: bool michael@0: NS_ProcessNextEvent(nsIThread *thread, bool mayWait) michael@0: { michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: if (!thread) { michael@0: thread = NS_GetCurrentThread(); michael@0: if (NS_WARN_IF(!thread)) michael@0: return false; michael@0: } michael@0: #else michael@0: nsCOMPtr current; michael@0: if (!thread) { michael@0: NS_GetCurrentThread(getter_AddRefs(current)); michael@0: if (NS_WARN_IF(!current)) michael@0: return false; michael@0: thread = current.get(); michael@0: } michael@0: #endif michael@0: bool val; michael@0: return NS_SUCCEEDED(thread->ProcessNextEvent(mayWait, &val)) && val; michael@0: } michael@0: michael@0: #ifndef XPCOM_GLUE_AVOID_NSPR michael@0: michael@0: namespace { michael@0: michael@0: class nsNameThreadRunnable MOZ_FINAL : public nsIRunnable michael@0: { michael@0: public: michael@0: nsNameThreadRunnable(const nsACString &name) : mName(name) { } michael@0: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIRUNNABLE michael@0: michael@0: protected: michael@0: const nsCString mName; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsNameThreadRunnable, nsIRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: nsNameThreadRunnable::Run() michael@0: { michael@0: PR_SetCurrentThreadName(mName.BeginReading()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: void michael@0: NS_SetThreadName(nsIThread *thread, const nsACString &name) michael@0: { michael@0: if (!thread) michael@0: return; michael@0: michael@0: thread->Dispatch(new nsNameThreadRunnable(name), michael@0: nsIEventTarget::DISPATCH_NORMAL); michael@0: } michael@0: michael@0: #else // !XPCOM_GLUE_AVOID_NSPR michael@0: michael@0: void michael@0: NS_SetThreadName(nsIThread *thread, const nsACString &name) michael@0: { michael@0: // No NSPR, no love. michael@0: } michael@0: michael@0: #endif michael@0: michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: nsIThread * michael@0: NS_GetCurrentThread() { michael@0: return nsThreadManager::get()->GetCurrentThread(); michael@0: } michael@0: #endif michael@0: michael@0: // nsThreadPoolNaming michael@0: void michael@0: nsThreadPoolNaming::SetThreadPoolName(const nsACString & aPoolName, michael@0: nsIThread * aThread) michael@0: { michael@0: nsCString name(aPoolName); michael@0: name.Append(NS_LITERAL_CSTRING(" #")); michael@0: name.AppendInt(++mCounter, 10); // The counter is declared as volatile michael@0: michael@0: if (aThread) { michael@0: // Set on the target thread michael@0: NS_SetThreadName(aThread, name); michael@0: } michael@0: else { michael@0: // Set on the current thread michael@0: PR_SetCurrentThreadName(name.BeginReading()); michael@0: } michael@0: } michael@0: michael@0: // nsAutoLowPriorityIO michael@0: nsAutoLowPriorityIO::nsAutoLowPriorityIO() michael@0: { michael@0: #if defined(XP_WIN) michael@0: lowIOPrioritySet = IsVistaOrLater() && michael@0: SetThreadPriority(GetCurrentThread(), michael@0: THREAD_MODE_BACKGROUND_BEGIN); michael@0: #elif defined(XP_MACOSX) michael@0: oldPriority = getiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD); michael@0: lowIOPrioritySet = oldPriority != -1 && michael@0: setiopolicy_np(IOPOL_TYPE_DISK, michael@0: IOPOL_SCOPE_THREAD, michael@0: IOPOL_THROTTLE) != -1; michael@0: #else michael@0: lowIOPrioritySet = false; michael@0: #endif michael@0: } michael@0: michael@0: nsAutoLowPriorityIO::~nsAutoLowPriorityIO() michael@0: { michael@0: #if defined(XP_WIN) michael@0: if (MOZ_LIKELY(lowIOPrioritySet)) { michael@0: // On Windows the old thread priority is automatically restored michael@0: SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_END); michael@0: } michael@0: #elif defined(XP_MACOSX) michael@0: if (MOZ_LIKELY(lowIOPrioritySet)) { michael@0: setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, oldPriority); michael@0: } michael@0: #endif michael@0: } michael@0: