xpcom/threads/nsThreadPool.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "nsIClassInfoImpl.h"
michael@0 8 #include "nsThreadPool.h"
michael@0 9 #include "nsThreadManager.h"
michael@0 10 #include "nsThread.h"
michael@0 11 #include "nsMemory.h"
michael@0 12 #include "nsAutoPtr.h"
michael@0 13 #include "prinrval.h"
michael@0 14 #include "prlog.h"
michael@0 15
michael@0 16 using namespace mozilla;
michael@0 17
michael@0 18 #ifdef PR_LOGGING
michael@0 19 static PRLogModuleInfo *
michael@0 20 GetThreadPoolLog()
michael@0 21 {
michael@0 22 static PRLogModuleInfo *sLog;
michael@0 23 if (!sLog)
michael@0 24 sLog = PR_NewLogModule("nsThreadPool");
michael@0 25 return sLog;
michael@0 26 }
michael@0 27 #endif
michael@0 28 #ifdef LOG
michael@0 29 #undef LOG
michael@0 30 #endif
michael@0 31 #define LOG(args) PR_LOG(GetThreadPoolLog(), PR_LOG_DEBUG, args)
michael@0 32
michael@0 33 // DESIGN:
michael@0 34 // o Allocate anonymous threads.
michael@0 35 // o Use nsThreadPool::Run as the main routine for each thread.
michael@0 36 // o Each thread waits on the event queue's monitor, checking for
michael@0 37 // pending events and rescheduling itself as an idle thread.
michael@0 38
michael@0 39 #define DEFAULT_THREAD_LIMIT 4
michael@0 40 #define DEFAULT_IDLE_THREAD_LIMIT 1
michael@0 41 #define DEFAULT_IDLE_THREAD_TIMEOUT PR_SecondsToInterval(60)
michael@0 42
michael@0 43 NS_IMPL_ADDREF(nsThreadPool)
michael@0 44 NS_IMPL_RELEASE(nsThreadPool)
michael@0 45 NS_IMPL_CLASSINFO(nsThreadPool, nullptr, nsIClassInfo::THREADSAFE,
michael@0 46 NS_THREADPOOL_CID)
michael@0 47 NS_IMPL_QUERY_INTERFACE_CI(nsThreadPool, nsIThreadPool, nsIEventTarget,
michael@0 48 nsIRunnable)
michael@0 49 NS_IMPL_CI_INTERFACE_GETTER(nsThreadPool, nsIThreadPool, nsIEventTarget)
michael@0 50
michael@0 51 nsThreadPool::nsThreadPool()
michael@0 52 : mThreadLimit(DEFAULT_THREAD_LIMIT)
michael@0 53 , mIdleThreadLimit(DEFAULT_IDLE_THREAD_LIMIT)
michael@0 54 , mIdleThreadTimeout(DEFAULT_IDLE_THREAD_TIMEOUT)
michael@0 55 , mIdleCount(0)
michael@0 56 , mStackSize(nsIThreadManager::DEFAULT_STACK_SIZE)
michael@0 57 , mShutdown(false)
michael@0 58 {
michael@0 59 }
michael@0 60
michael@0 61 nsThreadPool::~nsThreadPool()
michael@0 62 {
michael@0 63 // Threads keep a reference to the nsThreadPool until they return from Run()
michael@0 64 // after removing themselves from mThreads.
michael@0 65 MOZ_ASSERT(mThreads.IsEmpty());
michael@0 66 }
michael@0 67
michael@0 68 nsresult
michael@0 69 nsThreadPool::PutEvent(nsIRunnable *event)
michael@0 70 {
michael@0 71 // Avoid spawning a new thread while holding the event queue lock...
michael@0 72
michael@0 73 bool spawnThread = false;
michael@0 74 uint32_t stackSize = 0;
michael@0 75 {
michael@0 76 ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
michael@0 77
michael@0 78 LOG(("THRD-P(%p) put [%d %d %d]\n", this, mIdleCount, mThreads.Count(),
michael@0 79 mThreadLimit));
michael@0 80 MOZ_ASSERT(mIdleCount <= (uint32_t) mThreads.Count(), "oops");
michael@0 81
michael@0 82 // Make sure we have a thread to service this event.
michael@0 83 if (mIdleCount == 0 && mThreads.Count() < (int32_t) mThreadLimit)
michael@0 84 spawnThread = true;
michael@0 85
michael@0 86 mEvents.PutEvent(event);
michael@0 87 stackSize = mStackSize;
michael@0 88 }
michael@0 89
michael@0 90 LOG(("THRD-P(%p) put [spawn=%d]\n", this, spawnThread));
michael@0 91 if (!spawnThread)
michael@0 92 return NS_OK;
michael@0 93
michael@0 94 nsCOMPtr<nsIThread> thread;
michael@0 95 nsThreadManager::get()->NewThread(0,
michael@0 96 stackSize,
michael@0 97 getter_AddRefs(thread));
michael@0 98 if (NS_WARN_IF(!thread))
michael@0 99 return NS_ERROR_UNEXPECTED;
michael@0 100
michael@0 101 bool killThread = false;
michael@0 102 {
michael@0 103 ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
michael@0 104 if (mThreads.Count() < (int32_t) mThreadLimit) {
michael@0 105 mThreads.AppendObject(thread);
michael@0 106 } else {
michael@0 107 killThread = true; // okay, we don't need this thread anymore
michael@0 108 }
michael@0 109 }
michael@0 110 LOG(("THRD-P(%p) put [%p kill=%d]\n", this, thread.get(), killThread));
michael@0 111 if (killThread) {
michael@0 112 // Pending events are processed on the current thread during
michael@0 113 // nsIThread::Shutdown() execution, so if nsThreadPool::Dispatch() is called
michael@0 114 // under caller's lock then deadlock could occur. This happens e.g. in case
michael@0 115 // of nsStreamCopier. To prevent this situation, dispatch a shutdown event
michael@0 116 // to the current thread instead of calling nsIThread::Shutdown() directly.
michael@0 117
michael@0 118 nsRefPtr<nsIRunnable> r = NS_NewRunnableMethod(thread,
michael@0 119 &nsIThread::Shutdown);
michael@0 120 NS_DispatchToCurrentThread(r);
michael@0 121 } else {
michael@0 122 thread->Dispatch(this, NS_DISPATCH_NORMAL);
michael@0 123 }
michael@0 124
michael@0 125 return NS_OK;
michael@0 126 }
michael@0 127
michael@0 128 void
michael@0 129 nsThreadPool::ShutdownThread(nsIThread *thread)
michael@0 130 {
michael@0 131 LOG(("THRD-P(%p) shutdown async [%p]\n", this, thread));
michael@0 132
michael@0 133 // This method is responsible for calling Shutdown on |thread|. This must be
michael@0 134 // done from some other thread, so we use the main thread of the application.
michael@0 135
michael@0 136 MOZ_ASSERT(!NS_IsMainThread(), "wrong thread");
michael@0 137
michael@0 138 nsRefPtr<nsIRunnable> r = NS_NewRunnableMethod(thread, &nsIThread::Shutdown);
michael@0 139 NS_DispatchToMainThread(r);
michael@0 140 }
michael@0 141
michael@0 142 NS_IMETHODIMP
michael@0 143 nsThreadPool::Run()
michael@0 144 {
michael@0 145 LOG(("THRD-P(%p) enter\n", this));
michael@0 146
michael@0 147 mThreadNaming.SetThreadPoolName(mName);
michael@0 148
michael@0 149 nsCOMPtr<nsIThread> current;
michael@0 150 nsThreadManager::get()->GetCurrentThread(getter_AddRefs(current));
michael@0 151
michael@0 152 bool shutdownThreadOnExit = false;
michael@0 153 bool exitThread = false;
michael@0 154 bool wasIdle = false;
michael@0 155 PRIntervalTime idleSince;
michael@0 156
michael@0 157 nsCOMPtr<nsIThreadPoolListener> listener;
michael@0 158 {
michael@0 159 ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
michael@0 160 listener = mListener;
michael@0 161 }
michael@0 162
michael@0 163 if (listener) {
michael@0 164 listener->OnThreadCreated();
michael@0 165 }
michael@0 166
michael@0 167 do {
michael@0 168 nsCOMPtr<nsIRunnable> event;
michael@0 169 {
michael@0 170 ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
michael@0 171 if (!mEvents.GetPendingEvent(getter_AddRefs(event))) {
michael@0 172 PRIntervalTime now = PR_IntervalNow();
michael@0 173 PRIntervalTime timeout = PR_MillisecondsToInterval(mIdleThreadTimeout);
michael@0 174
michael@0 175 // If we are shutting down, then don't keep any idle threads
michael@0 176 if (mShutdown) {
michael@0 177 exitThread = true;
michael@0 178 } else {
michael@0 179 if (wasIdle) {
michael@0 180 // if too many idle threads or idle for too long, then bail.
michael@0 181 if (mIdleCount > mIdleThreadLimit || (now - idleSince) >= timeout)
michael@0 182 exitThread = true;
michael@0 183 } else {
michael@0 184 // if would be too many idle threads...
michael@0 185 if (mIdleCount == mIdleThreadLimit) {
michael@0 186 exitThread = true;
michael@0 187 } else {
michael@0 188 ++mIdleCount;
michael@0 189 idleSince = now;
michael@0 190 wasIdle = true;
michael@0 191 }
michael@0 192 }
michael@0 193 }
michael@0 194
michael@0 195 if (exitThread) {
michael@0 196 if (wasIdle)
michael@0 197 --mIdleCount;
michael@0 198 shutdownThreadOnExit = mThreads.RemoveObject(current);
michael@0 199 } else {
michael@0 200 PRIntervalTime delta = timeout - (now - idleSince);
michael@0 201 LOG(("THRD-P(%p) waiting [%d]\n", this, delta));
michael@0 202 mon.Wait(delta);
michael@0 203 }
michael@0 204 } else if (wasIdle) {
michael@0 205 wasIdle = false;
michael@0 206 --mIdleCount;
michael@0 207 }
michael@0 208 }
michael@0 209 if (event) {
michael@0 210 LOG(("THRD-P(%p) running [%p]\n", this, event.get()));
michael@0 211 event->Run();
michael@0 212 }
michael@0 213 } while (!exitThread);
michael@0 214
michael@0 215 if (listener) {
michael@0 216 listener->OnThreadShuttingDown();
michael@0 217 }
michael@0 218
michael@0 219 if (shutdownThreadOnExit) {
michael@0 220 ShutdownThread(current);
michael@0 221 }
michael@0 222
michael@0 223 LOG(("THRD-P(%p) leave\n", this));
michael@0 224 return NS_OK;
michael@0 225 }
michael@0 226
michael@0 227 NS_IMETHODIMP
michael@0 228 nsThreadPool::Dispatch(nsIRunnable *event, uint32_t flags)
michael@0 229 {
michael@0 230 LOG(("THRD-P(%p) dispatch [%p %x]\n", this, event, flags));
michael@0 231
michael@0 232 if (NS_WARN_IF(mShutdown))
michael@0 233 return NS_ERROR_NOT_AVAILABLE;
michael@0 234
michael@0 235 if (flags & DISPATCH_SYNC) {
michael@0 236 nsCOMPtr<nsIThread> thread;
michael@0 237 nsThreadManager::get()->GetCurrentThread(getter_AddRefs(thread));
michael@0 238 if (NS_WARN_IF(!thread))
michael@0 239 return NS_ERROR_NOT_AVAILABLE;
michael@0 240
michael@0 241 nsRefPtr<nsThreadSyncDispatch> wrapper =
michael@0 242 new nsThreadSyncDispatch(thread, event);
michael@0 243 PutEvent(wrapper);
michael@0 244
michael@0 245 while (wrapper->IsPending())
michael@0 246 NS_ProcessNextEvent(thread);
michael@0 247 } else {
michael@0 248 NS_ASSERTION(flags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
michael@0 249 PutEvent(event);
michael@0 250 }
michael@0 251 return NS_OK;
michael@0 252 }
michael@0 253
michael@0 254 NS_IMETHODIMP
michael@0 255 nsThreadPool::IsOnCurrentThread(bool *result)
michael@0 256 {
michael@0 257 ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
michael@0 258 nsIThread* thread = NS_GetCurrentThread();
michael@0 259 for (uint32_t i = 0; i < static_cast<uint32_t>(mThreads.Count()); ++i) {
michael@0 260 if (mThreads[i] == thread) {
michael@0 261 *result = true;
michael@0 262 return NS_OK;
michael@0 263 }
michael@0 264 }
michael@0 265 *result = false;
michael@0 266 return NS_OK;
michael@0 267 }
michael@0 268
michael@0 269 NS_IMETHODIMP
michael@0 270 nsThreadPool::Shutdown()
michael@0 271 {
michael@0 272 nsCOMArray<nsIThread> threads;
michael@0 273 nsCOMPtr<nsIThreadPoolListener> listener;
michael@0 274 {
michael@0 275 ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
michael@0 276 mShutdown = true;
michael@0 277 mon.NotifyAll();
michael@0 278
michael@0 279 threads.AppendObjects(mThreads);
michael@0 280 mThreads.Clear();
michael@0 281
michael@0 282 // Swap in a null listener so that we release the listener at the end of
michael@0 283 // this method. The listener will be kept alive as long as the other threads
michael@0 284 // that were created when it was set.
michael@0 285 mListener.swap(listener);
michael@0 286 }
michael@0 287
michael@0 288 // It's important that we shutdown the threads while outside the event queue
michael@0 289 // monitor. Otherwise, we could end up dead-locking.
michael@0 290
michael@0 291 for (int32_t i = 0; i < threads.Count(); ++i)
michael@0 292 threads[i]->Shutdown();
michael@0 293
michael@0 294 return NS_OK;
michael@0 295 }
michael@0 296
michael@0 297 NS_IMETHODIMP
michael@0 298 nsThreadPool::GetThreadLimit(uint32_t *value)
michael@0 299 {
michael@0 300 *value = mThreadLimit;
michael@0 301 return NS_OK;
michael@0 302 }
michael@0 303
michael@0 304 NS_IMETHODIMP
michael@0 305 nsThreadPool::SetThreadLimit(uint32_t value)
michael@0 306 {
michael@0 307 ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
michael@0 308 mThreadLimit = value;
michael@0 309 if (mIdleThreadLimit > mThreadLimit)
michael@0 310 mIdleThreadLimit = mThreadLimit;
michael@0 311
michael@0 312 if (static_cast<uint32_t>(mThreads.Count()) > mThreadLimit) {
michael@0 313 mon.NotifyAll(); // wake up threads so they observe this change
michael@0 314 }
michael@0 315 return NS_OK;
michael@0 316 }
michael@0 317
michael@0 318 NS_IMETHODIMP
michael@0 319 nsThreadPool::GetIdleThreadLimit(uint32_t *value)
michael@0 320 {
michael@0 321 *value = mIdleThreadLimit;
michael@0 322 return NS_OK;
michael@0 323 }
michael@0 324
michael@0 325 NS_IMETHODIMP
michael@0 326 nsThreadPool::SetIdleThreadLimit(uint32_t value)
michael@0 327 {
michael@0 328 ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
michael@0 329 mIdleThreadLimit = value;
michael@0 330 if (mIdleThreadLimit > mThreadLimit)
michael@0 331 mIdleThreadLimit = mThreadLimit;
michael@0 332
michael@0 333 // Do we need to kill some idle threads?
michael@0 334 if (mIdleCount > mIdleThreadLimit) {
michael@0 335 mon.NotifyAll(); // wake up threads so they observe this change
michael@0 336 }
michael@0 337 return NS_OK;
michael@0 338 }
michael@0 339
michael@0 340 NS_IMETHODIMP
michael@0 341 nsThreadPool::GetIdleThreadTimeout(uint32_t *value)
michael@0 342 {
michael@0 343 *value = mIdleThreadTimeout;
michael@0 344 return NS_OK;
michael@0 345 }
michael@0 346
michael@0 347 NS_IMETHODIMP
michael@0 348 nsThreadPool::SetIdleThreadTimeout(uint32_t value)
michael@0 349 {
michael@0 350 ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
michael@0 351 uint32_t oldTimeout = mIdleThreadTimeout;
michael@0 352 mIdleThreadTimeout = value;
michael@0 353
michael@0 354 // Do we need to notify any idle threads that their sleep time has shortened?
michael@0 355 if (mIdleThreadTimeout < oldTimeout && mIdleCount > 0) {
michael@0 356 mon.NotifyAll(); // wake up threads so they observe this change
michael@0 357 }
michael@0 358 return NS_OK;
michael@0 359 }
michael@0 360
michael@0 361 NS_IMETHODIMP
michael@0 362 nsThreadPool::GetThreadStackSize(uint32_t* value)
michael@0 363 {
michael@0 364 ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
michael@0 365 *value = mStackSize;
michael@0 366 return NS_OK;
michael@0 367 }
michael@0 368
michael@0 369 NS_IMETHODIMP
michael@0 370 nsThreadPool::SetThreadStackSize(uint32_t value)
michael@0 371 {
michael@0 372 ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
michael@0 373 mStackSize = value;
michael@0 374 return NS_OK;
michael@0 375 }
michael@0 376
michael@0 377 NS_IMETHODIMP
michael@0 378 nsThreadPool::GetListener(nsIThreadPoolListener** aListener)
michael@0 379 {
michael@0 380 ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
michael@0 381 NS_IF_ADDREF(*aListener = mListener);
michael@0 382 return NS_OK;
michael@0 383 }
michael@0 384
michael@0 385 NS_IMETHODIMP
michael@0 386 nsThreadPool::SetListener(nsIThreadPoolListener* aListener)
michael@0 387 {
michael@0 388 nsCOMPtr<nsIThreadPoolListener> swappedListener(aListener);
michael@0 389 {
michael@0 390 ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
michael@0 391 mListener.swap(swappedListener);
michael@0 392 }
michael@0 393 return NS_OK;
michael@0 394 }
michael@0 395
michael@0 396 NS_IMETHODIMP
michael@0 397 nsThreadPool::SetName(const nsACString& aName)
michael@0 398 {
michael@0 399 {
michael@0 400 ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
michael@0 401 if (mThreads.Count())
michael@0 402 return NS_ERROR_NOT_AVAILABLE;
michael@0 403 }
michael@0 404
michael@0 405 mName = aName;
michael@0 406 return NS_OK;
michael@0 407 }

mercurial