diff options
Diffstat (limited to 'xpcom/threads/nsThreadPool.cpp')
-rw-r--r-- | xpcom/threads/nsThreadPool.cpp | 182 |
1 files changed, 123 insertions, 59 deletions
diff --git a/xpcom/threads/nsThreadPool.cpp b/xpcom/threads/nsThreadPool.cpp index f2b42f21a..2d35fcb20 100644 --- a/xpcom/threads/nsThreadPool.cpp +++ b/xpcom/threads/nsThreadPool.cpp @@ -1,5 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* -*- 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/. */ @@ -16,15 +16,19 @@ using namespace mozilla; #ifdef PR_LOGGING -static PRLogModuleInfo * +static PRLogModuleInfo* GetThreadPoolLog() { - static PRLogModuleInfo *sLog; - if (!sLog) + static PRLogModuleInfo* sLog; + if (!sLog) { sLog = PR_NewLogModule("nsThreadPool"); + } return sLog; } #endif +#ifdef LOG +#undef LOG +#endif #define LOG(args) PR_LOG(GetThreadPoolLog(), PR_LOG_DEBUG, args) // DESIGN: @@ -37,62 +41,74 @@ GetThreadPoolLog() #define DEFAULT_IDLE_THREAD_LIMIT 1 #define DEFAULT_IDLE_THREAD_TIMEOUT PR_SecondsToInterval(60) -NS_IMPL_THREADSAFE_ADDREF(nsThreadPool) -NS_IMPL_THREADSAFE_RELEASE(nsThreadPool) -NS_IMPL_CLASSINFO(nsThreadPool, NULL, nsIClassInfo::THREADSAFE, +NS_IMPL_ADDREF(nsThreadPool) +NS_IMPL_RELEASE(nsThreadPool) +NS_IMPL_CLASSINFO(nsThreadPool, nullptr, nsIClassInfo::THREADSAFE, NS_THREADPOOL_CID) -NS_IMPL_QUERY_INTERFACE3_CI(nsThreadPool, nsIThreadPool, nsIEventTarget, - nsIRunnable) -NS_IMPL_CI_INTERFACE_GETTER2(nsThreadPool, nsIThreadPool, nsIEventTarget) +NS_IMPL_QUERY_INTERFACE_CI(nsThreadPool, nsIThreadPool, nsIEventTarget, + nsIRunnable) +NS_IMPL_CI_INTERFACE_GETTER(nsThreadPool, nsIThreadPool, nsIEventTarget) nsThreadPool::nsThreadPool() : mThreadLimit(DEFAULT_THREAD_LIMIT) , mIdleThreadLimit(DEFAULT_IDLE_THREAD_LIMIT) , mIdleThreadTimeout(DEFAULT_IDLE_THREAD_TIMEOUT) , mIdleCount(0) + , mStackSize(nsIThreadManager::DEFAULT_STACK_SIZE) , mShutdown(false) { } nsThreadPool::~nsThreadPool() { - Shutdown(); + // Threads keep a reference to the nsThreadPool until they return from Run() + // after removing themselves from mThreads. + MOZ_ASSERT(mThreads.IsEmpty()); } nsresult -nsThreadPool::PutEvent(nsIRunnable *event) +nsThreadPool::PutEvent(nsIRunnable* aEvent) { // Avoid spawning a new thread while holding the event queue lock... - + bool spawnThread = false; + uint32_t stackSize = 0; { ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + if (NS_WARN_IF(mShutdown)) { + return NS_ERROR_NOT_AVAILABLE; + } LOG(("THRD-P(%p) put [%d %d %d]\n", this, mIdleCount, mThreads.Count(), mThreadLimit)); - MOZ_ASSERT(mIdleCount <= (uint32_t) mThreads.Count(), "oops"); + MOZ_ASSERT(mIdleCount <= (uint32_t)mThreads.Count(), "oops"); // Make sure we have a thread to service this event. - if (mIdleCount == 0 && mThreads.Count() < (int32_t) mThreadLimit) + if (mIdleCount == 0 && mThreads.Count() < (int32_t)mThreadLimit) { spawnThread = true; + } - mEvents.PutEvent(event); + mEvents.PutEvent(aEvent); + stackSize = mStackSize; } LOG(("THRD-P(%p) put [spawn=%d]\n", this, spawnThread)); - if (!spawnThread) + if (!spawnThread) { return NS_OK; + } nsCOMPtr<nsIThread> thread; nsThreadManager::get()->NewThread(0, - nsIThreadManager::DEFAULT_STACK_SIZE, + stackSize, getter_AddRefs(thread)); - NS_ENSURE_STATE(thread); + if (NS_WARN_IF(!thread)) { + return NS_ERROR_UNEXPECTED; + } bool killThread = false; { ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); - if (mThreads.Count() < (int32_t) mThreadLimit) { + if (mThreads.Count() < (int32_t)mThreadLimit) { mThreads.AppendObject(thread); } else { killThread = true; // okay, we don't need this thread anymore @@ -100,7 +116,15 @@ nsThreadPool::PutEvent(nsIRunnable *event) } LOG(("THRD-P(%p) put [%p kill=%d]\n", this, thread.get(), killThread)); if (killThread) { - thread->Shutdown(); + // Pending events are processed on the current thread during + // nsIThread::Shutdown() execution, so if nsThreadPool::Dispatch() is called + // under caller's lock then deadlock could occur. This happens e.g. in case + // of nsStreamCopier. To prevent this situation, dispatch a shutdown event + // to the current thread instead of calling nsIThread::Shutdown() directly. + + nsRefPtr<nsIRunnable> r = NS_NewRunnableMethod(thread, + &nsIThread::Shutdown); + NS_DispatchToCurrentThread(r); } else { thread->Dispatch(this, NS_DISPATCH_NORMAL); } @@ -109,16 +133,16 @@ nsThreadPool::PutEvent(nsIRunnable *event) } void -nsThreadPool::ShutdownThread(nsIThread *thread) +nsThreadPool::ShutdownThread(nsIThread* aThread) { - LOG(("THRD-P(%p) shutdown async [%p]\n", this, thread)); + LOG(("THRD-P(%p) shutdown async [%p]\n", this, aThread)); - // This method is responsible for calling Shutdown on |thread|. This must be + // This method is responsible for calling Shutdown on |aThread|. This must be // done from some other thread, so we use the main thread of the application. MOZ_ASSERT(!NS_IsMainThread(), "wrong thread"); - nsRefPtr<nsIRunnable> r = NS_NewRunnableMethod(thread, &nsIThread::Shutdown); + nsRefPtr<nsIRunnable> r = NS_NewRunnableMethod(aThread, &nsIThread::Shutdown); NS_DispatchToMainThread(r); } @@ -126,7 +150,6 @@ NS_IMETHODIMP nsThreadPool::Run() { LOG(("THRD-P(%p) enter\n", this)); - mThreadNaming.SetThreadPoolName(mName); nsCOMPtr<nsIThread> current; @@ -161,8 +184,9 @@ nsThreadPool::Run() } else { if (wasIdle) { // if too many idle threads or idle for too long, then bail. - if (mIdleCount > mIdleThreadLimit || (now - idleSince) >= timeout) + if (mIdleCount > mIdleThreadLimit || (now - idleSince) >= timeout) { exitThread = true; + } } else { // if would be too many idle threads... if (mIdleCount == mIdleThreadLimit) { @@ -176,12 +200,16 @@ nsThreadPool::Run() } if (exitThread) { - if (wasIdle) + if (wasIdle) { --mIdleCount; + } shutdownThreadOnExit = mThreads.RemoveObject(current); } else { PRIntervalTime delta = timeout - (now - idleSince); LOG(("THRD-P(%p) waiting [%d]\n", this, delta)); +#ifdef MOZ_NUWA_PROCESS + nsThreadManager::get()->SetThreadIdle(nullptr); +#endif // MOZ_NUWA_PROCESS mon.Wait(delta); } } else if (wasIdle) { @@ -191,6 +219,9 @@ nsThreadPool::Run() } if (event) { LOG(("THRD-P(%p) running [%p]\n", this, event.get())); +#ifdef MOZ_NUWA_PROCESS + nsThreadManager::get()->SetThreadWorking(); +#endif // MOZ_NUWA_PROCESS event->Run(); } } while (!exitThread); @@ -208,38 +239,51 @@ nsThreadPool::Run() } NS_IMETHODIMP -nsThreadPool::Dispatch(nsIRunnable *event, uint32_t flags) +nsThreadPool::Dispatch(nsIRunnable* aEvent, uint32_t aFlags) { - LOG(("THRD-P(%p) dispatch [%p %x]\n", this, event, flags)); + LOG(("THRD-P(%p) dispatch [%p %x]\n", this, aEvent, aFlags)); - NS_ENSURE_STATE(!mShutdown); + if (NS_WARN_IF(mShutdown)) { + return NS_ERROR_NOT_AVAILABLE; + } - if (flags & DISPATCH_SYNC) { + if (aFlags & DISPATCH_SYNC) { nsCOMPtr<nsIThread> thread; nsThreadManager::get()->GetCurrentThread(getter_AddRefs(thread)); - NS_ENSURE_STATE(thread); + if (NS_WARN_IF(!thread)) { + return NS_ERROR_NOT_AVAILABLE; + } nsRefPtr<nsThreadSyncDispatch> wrapper = - new nsThreadSyncDispatch(thread, event); + new nsThreadSyncDispatch(thread, aEvent); PutEvent(wrapper); - while (wrapper->IsPending()) + while (wrapper->IsPending()) { NS_ProcessNextEvent(thread); + } } else { - NS_ASSERTION(flags == NS_DISPATCH_NORMAL, "unexpected dispatch flags"); - PutEvent(event); + NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "unexpected dispatch flags"); + PutEvent(aEvent); } return NS_OK; } NS_IMETHODIMP -nsThreadPool::IsOnCurrentThread(bool *result) +nsThreadPool::IsOnCurrentThread(bool* aResult) { - // No one should be calling this method. If this assertion gets hit, then we - // need to think carefully about what this method should be returning. - NS_NOTREACHED("implement me"); + ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + if (NS_WARN_IF(mShutdown)) { + return NS_ERROR_NOT_AVAILABLE; + } - *result = false; + nsIThread* thread = NS_GetCurrentThread(); + for (uint32_t i = 0; i < static_cast<uint32_t>(mThreads.Count()); ++i) { + if (mThreads[i] == thread) { + *aResult = true; + return NS_OK; + } + } + *aResult = false; return NS_OK; } @@ -265,26 +309,28 @@ nsThreadPool::Shutdown() // It's important that we shutdown the threads while outside the event queue // monitor. Otherwise, we could end up dead-locking. - for (int32_t i = 0; i < threads.Count(); ++i) + for (int32_t i = 0; i < threads.Count(); ++i) { threads[i]->Shutdown(); + } return NS_OK; } NS_IMETHODIMP -nsThreadPool::GetThreadLimit(uint32_t *value) +nsThreadPool::GetThreadLimit(uint32_t* aValue) { - *value = mThreadLimit; + *aValue = mThreadLimit; return NS_OK; } NS_IMETHODIMP -nsThreadPool::SetThreadLimit(uint32_t value) +nsThreadPool::SetThreadLimit(uint32_t aValue) { ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); - mThreadLimit = value; - if (mIdleThreadLimit > mThreadLimit) + mThreadLimit = aValue; + if (mIdleThreadLimit > mThreadLimit) { mIdleThreadLimit = mThreadLimit; + } if (static_cast<uint32_t>(mThreads.Count()) > mThreadLimit) { mon.NotifyAll(); // wake up threads so they observe this change @@ -293,19 +339,20 @@ nsThreadPool::SetThreadLimit(uint32_t value) } NS_IMETHODIMP -nsThreadPool::GetIdleThreadLimit(uint32_t *value) +nsThreadPool::GetIdleThreadLimit(uint32_t* aValue) { - *value = mIdleThreadLimit; + *aValue = mIdleThreadLimit; return NS_OK; } NS_IMETHODIMP -nsThreadPool::SetIdleThreadLimit(uint32_t value) +nsThreadPool::SetIdleThreadLimit(uint32_t aValue) { ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); - mIdleThreadLimit = value; - if (mIdleThreadLimit > mThreadLimit) + mIdleThreadLimit = aValue; + if (mIdleThreadLimit > mThreadLimit) { mIdleThreadLimit = mThreadLimit; + } // Do we need to kill some idle threads? if (mIdleCount > mIdleThreadLimit) { @@ -315,18 +362,18 @@ nsThreadPool::SetIdleThreadLimit(uint32_t value) } NS_IMETHODIMP -nsThreadPool::GetIdleThreadTimeout(uint32_t *value) +nsThreadPool::GetIdleThreadTimeout(uint32_t* aValue) { - *value = mIdleThreadTimeout; + *aValue = mIdleThreadTimeout; return NS_OK; } NS_IMETHODIMP -nsThreadPool::SetIdleThreadTimeout(uint32_t value) +nsThreadPool::SetIdleThreadTimeout(uint32_t aValue) { ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); uint32_t oldTimeout = mIdleThreadTimeout; - mIdleThreadTimeout = value; + mIdleThreadTimeout = aValue; // Do we need to notify any idle threads that their sleep time has shortened? if (mIdleThreadTimeout < oldTimeout && mIdleCount > 0) { @@ -336,6 +383,22 @@ nsThreadPool::SetIdleThreadTimeout(uint32_t value) } NS_IMETHODIMP +nsThreadPool::GetThreadStackSize(uint32_t* aValue) +{ + ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + *aValue = mStackSize; + return NS_OK; +} + +NS_IMETHODIMP +nsThreadPool::SetThreadStackSize(uint32_t aValue) +{ + ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + mStackSize = aValue; + return NS_OK; +} + +NS_IMETHODIMP nsThreadPool::GetListener(nsIThreadPoolListener** aListener) { ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); @@ -359,8 +422,9 @@ nsThreadPool::SetName(const nsACString& aName) { { ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); - if (mThreads.Count()) + if (mThreads.Count()) { return NS_ERROR_NOT_AVAILABLE; + } } mName = aName; |