xpcom/threads/nsThread.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 "nsThread.h"
michael@0 8
michael@0 9 #include "base/message_loop.h"
michael@0 10
michael@0 11 // Chromium's logging can sometimes leak through...
michael@0 12 #ifdef LOG
michael@0 13 #undef LOG
michael@0 14 #endif
michael@0 15
michael@0 16 #include "mozilla/ReentrantMonitor.h"
michael@0 17 #include "nsMemoryPressure.h"
michael@0 18 #include "nsThreadManager.h"
michael@0 19 #include "nsIClassInfoImpl.h"
michael@0 20 #include "nsIProgrammingLanguage.h"
michael@0 21 #include "nsAutoPtr.h"
michael@0 22 #include "nsCOMPtr.h"
michael@0 23 #include "pratom.h"
michael@0 24 #include "prlog.h"
michael@0 25 #include "nsIObserverService.h"
michael@0 26 #include "mozilla/HangMonitor.h"
michael@0 27 #include "mozilla/IOInterposer.h"
michael@0 28 #include "mozilla/Services.h"
michael@0 29 #include "nsXPCOMPrivate.h"
michael@0 30 #include "mozilla/ChaosMode.h"
michael@0 31
michael@0 32 #ifdef XP_LINUX
michael@0 33 #include <sys/time.h>
michael@0 34 #include <sys/resource.h>
michael@0 35 #include <sched.h>
michael@0 36 #endif
michael@0 37
michael@0 38 #define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 || \
michael@0 39 _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) && \
michael@0 40 !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
michael@0 41
michael@0 42 #if defined(XP_LINUX) && !defined(ANDROID) && defined(_GNU_SOURCE)
michael@0 43 #define HAVE_SCHED_SETAFFINITY
michael@0 44 #endif
michael@0 45
michael@0 46 #ifdef MOZ_CANARY
michael@0 47 # include <unistd.h>
michael@0 48 # include <execinfo.h>
michael@0 49 # include <signal.h>
michael@0 50 # include <fcntl.h>
michael@0 51 # include "nsXULAppAPI.h"
michael@0 52 #endif
michael@0 53
michael@0 54 #if defined(NS_FUNCTION_TIMER) && defined(_MSC_VER)
michael@0 55 #include "nsTimerImpl.h"
michael@0 56 #include "nsStackWalk.h"
michael@0 57 #endif
michael@0 58 #ifdef NS_FUNCTION_TIMER
michael@0 59 #include "nsCRT.h"
michael@0 60 #endif
michael@0 61
michael@0 62 #ifdef MOZ_TASK_TRACER
michael@0 63 #include "GeckoTaskTracer.h"
michael@0 64 using namespace mozilla::tasktracer;
michael@0 65 #endif
michael@0 66
michael@0 67 using namespace mozilla;
michael@0 68
michael@0 69 #ifdef PR_LOGGING
michael@0 70 static PRLogModuleInfo *
michael@0 71 GetThreadLog()
michael@0 72 {
michael@0 73 static PRLogModuleInfo *sLog;
michael@0 74 if (!sLog)
michael@0 75 sLog = PR_NewLogModule("nsThread");
michael@0 76 return sLog;
michael@0 77 }
michael@0 78 #endif
michael@0 79 #ifdef LOG
michael@0 80 #undef LOG
michael@0 81 #endif
michael@0 82 #define LOG(args) PR_LOG(GetThreadLog(), PR_LOG_DEBUG, args)
michael@0 83
michael@0 84 NS_DECL_CI_INTERFACE_GETTER(nsThread)
michael@0 85
michael@0 86 nsIThreadObserver* nsThread::sMainThreadObserver = nullptr;
michael@0 87
michael@0 88 //-----------------------------------------------------------------------------
michael@0 89 // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
michael@0 90 // somewhat manually.
michael@0 91
michael@0 92 class nsThreadClassInfo : public nsIClassInfo {
michael@0 93 public:
michael@0 94 NS_DECL_ISUPPORTS_INHERITED // no mRefCnt
michael@0 95 NS_DECL_NSICLASSINFO
michael@0 96
michael@0 97 nsThreadClassInfo() {}
michael@0 98 };
michael@0 99
michael@0 100 NS_IMETHODIMP_(MozExternalRefCountType) nsThreadClassInfo::AddRef() { return 2; }
michael@0 101 NS_IMETHODIMP_(MozExternalRefCountType) nsThreadClassInfo::Release() { return 1; }
michael@0 102 NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo, nsIClassInfo)
michael@0 103
michael@0 104 NS_IMETHODIMP
michael@0 105 nsThreadClassInfo::GetInterfaces(uint32_t *count, nsIID ***array)
michael@0 106 {
michael@0 107 return NS_CI_INTERFACE_GETTER_NAME(nsThread)(count, array);
michael@0 108 }
michael@0 109
michael@0 110 NS_IMETHODIMP
michael@0 111 nsThreadClassInfo::GetHelperForLanguage(uint32_t lang, nsISupports **result)
michael@0 112 {
michael@0 113 *result = nullptr;
michael@0 114 return NS_OK;
michael@0 115 }
michael@0 116
michael@0 117 NS_IMETHODIMP
michael@0 118 nsThreadClassInfo::GetContractID(char **result)
michael@0 119 {
michael@0 120 *result = nullptr;
michael@0 121 return NS_OK;
michael@0 122 }
michael@0 123
michael@0 124 NS_IMETHODIMP
michael@0 125 nsThreadClassInfo::GetClassDescription(char **result)
michael@0 126 {
michael@0 127 *result = nullptr;
michael@0 128 return NS_OK;
michael@0 129 }
michael@0 130
michael@0 131 NS_IMETHODIMP
michael@0 132 nsThreadClassInfo::GetClassID(nsCID **result)
michael@0 133 {
michael@0 134 *result = nullptr;
michael@0 135 return NS_OK;
michael@0 136 }
michael@0 137
michael@0 138 NS_IMETHODIMP
michael@0 139 nsThreadClassInfo::GetImplementationLanguage(uint32_t *result)
michael@0 140 {
michael@0 141 *result = nsIProgrammingLanguage::CPLUSPLUS;
michael@0 142 return NS_OK;
michael@0 143 }
michael@0 144
michael@0 145 NS_IMETHODIMP
michael@0 146 nsThreadClassInfo::GetFlags(uint32_t *result)
michael@0 147 {
michael@0 148 *result = THREADSAFE;
michael@0 149 return NS_OK;
michael@0 150 }
michael@0 151
michael@0 152 NS_IMETHODIMP
michael@0 153 nsThreadClassInfo::GetClassIDNoAlloc(nsCID *result)
michael@0 154 {
michael@0 155 return NS_ERROR_NOT_AVAILABLE;
michael@0 156 }
michael@0 157
michael@0 158 //-----------------------------------------------------------------------------
michael@0 159
michael@0 160 NS_IMPL_ADDREF(nsThread)
michael@0 161 NS_IMPL_RELEASE(nsThread)
michael@0 162 NS_INTERFACE_MAP_BEGIN(nsThread)
michael@0 163 NS_INTERFACE_MAP_ENTRY(nsIThread)
michael@0 164 NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
michael@0 165 NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
michael@0 166 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
michael@0 167 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
michael@0 168 if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
michael@0 169 static nsThreadClassInfo sThreadClassInfo;
michael@0 170 foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
michael@0 171 } else
michael@0 172 NS_INTERFACE_MAP_END
michael@0 173 NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal,
michael@0 174 nsIEventTarget, nsISupportsPriority)
michael@0 175
michael@0 176 //-----------------------------------------------------------------------------
michael@0 177
michael@0 178 class nsThreadStartupEvent : public nsRunnable {
michael@0 179 public:
michael@0 180 nsThreadStartupEvent()
michael@0 181 : mMon("nsThreadStartupEvent.mMon")
michael@0 182 , mInitialized(false) {
michael@0 183 }
michael@0 184
michael@0 185 // This method does not return until the thread startup object is in the
michael@0 186 // completion state.
michael@0 187 void Wait() {
michael@0 188 if (mInitialized) // Maybe avoid locking...
michael@0 189 return;
michael@0 190 ReentrantMonitorAutoEnter mon(mMon);
michael@0 191 while (!mInitialized)
michael@0 192 mon.Wait();
michael@0 193 }
michael@0 194
michael@0 195 // This method needs to be public to support older compilers (xlC_r on AIX).
michael@0 196 // It should be called directly as this class type is reference counted.
michael@0 197 virtual ~nsThreadStartupEvent() {
michael@0 198 }
michael@0 199
michael@0 200 private:
michael@0 201 NS_IMETHOD Run() {
michael@0 202 ReentrantMonitorAutoEnter mon(mMon);
michael@0 203 mInitialized = true;
michael@0 204 mon.Notify();
michael@0 205 return NS_OK;
michael@0 206 }
michael@0 207
michael@0 208 ReentrantMonitor mMon;
michael@0 209 bool mInitialized;
michael@0 210 };
michael@0 211
michael@0 212 //-----------------------------------------------------------------------------
michael@0 213
michael@0 214 struct nsThreadShutdownContext {
michael@0 215 nsThread *joiningThread;
michael@0 216 bool shutdownAck;
michael@0 217 };
michael@0 218
michael@0 219 // This event is responsible for notifying nsThread::Shutdown that it is time
michael@0 220 // to call PR_JoinThread.
michael@0 221 class nsThreadShutdownAckEvent : public nsRunnable {
michael@0 222 public:
michael@0 223 nsThreadShutdownAckEvent(nsThreadShutdownContext *ctx)
michael@0 224 : mShutdownContext(ctx) {
michael@0 225 }
michael@0 226 NS_IMETHOD Run() {
michael@0 227 mShutdownContext->shutdownAck = true;
michael@0 228 return NS_OK;
michael@0 229 }
michael@0 230 private:
michael@0 231 nsThreadShutdownContext *mShutdownContext;
michael@0 232 };
michael@0 233
michael@0 234 // This event is responsible for setting mShutdownContext
michael@0 235 class nsThreadShutdownEvent : public nsRunnable {
michael@0 236 public:
michael@0 237 nsThreadShutdownEvent(nsThread *thr, nsThreadShutdownContext *ctx)
michael@0 238 : mThread(thr), mShutdownContext(ctx) {
michael@0 239 }
michael@0 240 NS_IMETHOD Run() {
michael@0 241 mThread->mShutdownContext = mShutdownContext;
michael@0 242 MessageLoop::current()->Quit();
michael@0 243 return NS_OK;
michael@0 244 }
michael@0 245 private:
michael@0 246 nsRefPtr<nsThread> mThread;
michael@0 247 nsThreadShutdownContext *mShutdownContext;
michael@0 248 };
michael@0 249
michael@0 250 //-----------------------------------------------------------------------------
michael@0 251
michael@0 252 static void
michael@0 253 SetupCurrentThreadForChaosMode()
michael@0 254 {
michael@0 255 if (!ChaosMode::isActive()) {
michael@0 256 return;
michael@0 257 }
michael@0 258
michael@0 259 #ifdef XP_LINUX
michael@0 260 // PR_SetThreadPriority doesn't really work since priorities >
michael@0 261 // PR_PRIORITY_NORMAL can't be set by non-root users. Instead we'll just use
michael@0 262 // setpriority(2) to set random 'nice values'. In regular Linux this is only
michael@0 263 // a dynamic adjustment so it still doesn't really do what we want, but tools
michael@0 264 // like 'rr' can be more aggressive about honoring these values.
michael@0 265 // Some of these calls may fail due to trying to lower the priority
michael@0 266 // (e.g. something may have already called setpriority() for this thread).
michael@0 267 // This makes it hard to have non-main threads with higher priority than the
michael@0 268 // main thread, but that's hard to fix. Tools like rr can choose to honor the
michael@0 269 // requested values anyway.
michael@0 270 // Use just 4 priorities so there's a reasonable chance of any two threads
michael@0 271 // having equal priority.
michael@0 272 setpriority(PRIO_PROCESS, 0, ChaosMode::randomUint32LessThan(4));
michael@0 273 #else
michael@0 274 // We should set the affinity here but NSPR doesn't provide a way to expose it.
michael@0 275 PR_SetThreadPriority(PR_GetCurrentThread(),
michael@0 276 PRThreadPriority(ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST + 1)));
michael@0 277 #endif
michael@0 278
michael@0 279 #ifdef HAVE_SCHED_SETAFFINITY
michael@0 280 // Force half the threads to CPU 0 so they compete for CPU
michael@0 281 if (ChaosMode::randomUint32LessThan(2)) {
michael@0 282 cpu_set_t cpus;
michael@0 283 CPU_ZERO(&cpus);
michael@0 284 CPU_SET(0, &cpus);
michael@0 285 sched_setaffinity(0, sizeof(cpus), &cpus);
michael@0 286 }
michael@0 287 #endif
michael@0 288 }
michael@0 289
michael@0 290 /*static*/ void
michael@0 291 nsThread::ThreadFunc(void *arg)
michael@0 292 {
michael@0 293 nsThread *self = static_cast<nsThread *>(arg); // strong reference
michael@0 294 self->mThread = PR_GetCurrentThread();
michael@0 295 SetupCurrentThreadForChaosMode();
michael@0 296
michael@0 297 // Inform the ThreadManager
michael@0 298 nsThreadManager::get()->RegisterCurrentThread(self);
michael@0 299
michael@0 300 mozilla::IOInterposer::RegisterCurrentThread();
michael@0 301
michael@0 302 // Wait for and process startup event
michael@0 303 nsCOMPtr<nsIRunnable> event;
michael@0 304 if (!self->GetEvent(true, getter_AddRefs(event))) {
michael@0 305 NS_WARNING("failed waiting for thread startup event");
michael@0 306 return;
michael@0 307 }
michael@0 308 event->Run(); // unblocks nsThread::Init
michael@0 309 event = nullptr;
michael@0 310
michael@0 311 { // Scope for MessageLoop.
michael@0 312 nsAutoPtr<MessageLoop> loop(
michael@0 313 new MessageLoop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD));
michael@0 314
michael@0 315 // Now, process incoming events...
michael@0 316 loop->Run();
michael@0 317
michael@0 318 // Do NS_ProcessPendingEvents but with special handling to set
michael@0 319 // mEventsAreDoomed atomically with the removal of the last event. The key
michael@0 320 // invariant here is that we will never permit PutEvent to succeed if the
michael@0 321 // event would be left in the queue after our final call to
michael@0 322 // NS_ProcessPendingEvents.
michael@0 323 while (true) {
michael@0 324 {
michael@0 325 MutexAutoLock lock(self->mLock);
michael@0 326 if (!self->mEvents->HasPendingEvent()) {
michael@0 327 // No events in the queue, so we will stop now. Don't let any more
michael@0 328 // events be added, since they won't be processed. It is critical
michael@0 329 // that no PutEvent can occur between testing that the event queue is
michael@0 330 // empty and setting mEventsAreDoomed!
michael@0 331 self->mEventsAreDoomed = true;
michael@0 332 break;
michael@0 333 }
michael@0 334 }
michael@0 335 NS_ProcessPendingEvents(self);
michael@0 336 }
michael@0 337 }
michael@0 338
michael@0 339 mozilla::IOInterposer::UnregisterCurrentThread();
michael@0 340
michael@0 341 // Inform the threadmanager that this thread is going away
michael@0 342 nsThreadManager::get()->UnregisterCurrentThread(self);
michael@0 343
michael@0 344 // Dispatch shutdown ACK
michael@0 345 event = new nsThreadShutdownAckEvent(self->mShutdownContext);
michael@0 346 self->mShutdownContext->joiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
michael@0 347
michael@0 348 // Release any observer of the thread here.
michael@0 349 self->SetObserver(nullptr);
michael@0 350
michael@0 351 #ifdef MOZ_TASK_TRACER
michael@0 352 FreeTraceInfo();
michael@0 353 #endif
michael@0 354
michael@0 355 NS_RELEASE(self);
michael@0 356 }
michael@0 357
michael@0 358 //-----------------------------------------------------------------------------
michael@0 359
michael@0 360 #ifdef MOZ_CANARY
michael@0 361 int sCanaryOutputFD = -1;
michael@0 362 #endif
michael@0 363
michael@0 364 nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize)
michael@0 365 : mLock("nsThread.mLock")
michael@0 366 , mEvents(&mEventsRoot)
michael@0 367 , mPriority(PRIORITY_NORMAL)
michael@0 368 , mThread(nullptr)
michael@0 369 , mRunningEvent(0)
michael@0 370 , mStackSize(aStackSize)
michael@0 371 , mShutdownContext(nullptr)
michael@0 372 , mShutdownRequired(false)
michael@0 373 , mEventsAreDoomed(false)
michael@0 374 , mIsMainThread(aMainThread)
michael@0 375 {
michael@0 376 }
michael@0 377
michael@0 378 nsThread::~nsThread()
michael@0 379 {
michael@0 380 }
michael@0 381
michael@0 382 nsresult
michael@0 383 nsThread::Init()
michael@0 384 {
michael@0 385 // spawn thread and wait until it is fully setup
michael@0 386 nsRefPtr<nsThreadStartupEvent> startup = new nsThreadStartupEvent();
michael@0 387
michael@0 388 NS_ADDREF_THIS();
michael@0 389
michael@0 390 mShutdownRequired = true;
michael@0 391
michael@0 392 // ThreadFunc is responsible for setting mThread
michael@0 393 PRThread *thr = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
michael@0 394 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
michael@0 395 PR_JOINABLE_THREAD, mStackSize);
michael@0 396 if (!thr) {
michael@0 397 NS_RELEASE_THIS();
michael@0 398 return NS_ERROR_OUT_OF_MEMORY;
michael@0 399 }
michael@0 400
michael@0 401 // ThreadFunc will wait for this event to be run before it tries to access
michael@0 402 // mThread. By delaying insertion of this event into the queue, we ensure
michael@0 403 // that mThread is set properly.
michael@0 404 {
michael@0 405 MutexAutoLock lock(mLock);
michael@0 406 mEventsRoot.PutEvent(startup);
michael@0 407 }
michael@0 408
michael@0 409 // Wait for thread to call ThreadManager::SetupCurrentThread, which completes
michael@0 410 // initialization of ThreadFunc.
michael@0 411 startup->Wait();
michael@0 412 return NS_OK;
michael@0 413 }
michael@0 414
michael@0 415 nsresult
michael@0 416 nsThread::InitCurrentThread()
michael@0 417 {
michael@0 418 mThread = PR_GetCurrentThread();
michael@0 419 SetupCurrentThreadForChaosMode();
michael@0 420
michael@0 421 nsThreadManager::get()->RegisterCurrentThread(this);
michael@0 422 return NS_OK;
michael@0 423 }
michael@0 424
michael@0 425 nsresult
michael@0 426 nsThread::PutEvent(nsIRunnable *event, nsNestedEventTarget *target)
michael@0 427 {
michael@0 428 {
michael@0 429 MutexAutoLock lock(mLock);
michael@0 430 nsChainedEventQueue *queue = target ? target->mQueue : &mEventsRoot;
michael@0 431 if (!queue || (queue == &mEventsRoot && mEventsAreDoomed)) {
michael@0 432 NS_WARNING("An event was posted to a thread that will never run it (rejected)");
michael@0 433 return NS_ERROR_UNEXPECTED;
michael@0 434 }
michael@0 435 if (!queue->PutEvent(event))
michael@0 436 return NS_ERROR_OUT_OF_MEMORY;
michael@0 437 }
michael@0 438
michael@0 439 nsCOMPtr<nsIThreadObserver> obs = GetObserver();
michael@0 440 if (obs)
michael@0 441 obs->OnDispatchedEvent(this);
michael@0 442
michael@0 443 return NS_OK;
michael@0 444 }
michael@0 445
michael@0 446 nsresult
michael@0 447 nsThread::DispatchInternal(nsIRunnable *event, uint32_t flags,
michael@0 448 nsNestedEventTarget *target)
michael@0 449 {
michael@0 450 if (NS_WARN_IF(!event))
michael@0 451 return NS_ERROR_INVALID_ARG;
michael@0 452
michael@0 453 if (gXPCOMThreadsShutDown && MAIN_THREAD != mIsMainThread && !target) {
michael@0 454 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
michael@0 455 }
michael@0 456
michael@0 457 #ifdef MOZ_TASK_TRACER
michael@0 458 nsRefPtr<nsIRunnable> tracedRunnable = CreateTracedRunnable(event);
michael@0 459 event = tracedRunnable;
michael@0 460 #endif
michael@0 461
michael@0 462 if (flags & DISPATCH_SYNC) {
michael@0 463 nsThread *thread = nsThreadManager::get()->GetCurrentThread();
michael@0 464 if (NS_WARN_IF(!thread))
michael@0 465 return NS_ERROR_NOT_AVAILABLE;
michael@0 466
michael@0 467 // XXX we should be able to do something better here... we should
michael@0 468 // be able to monitor the slot occupied by this event and use
michael@0 469 // that to tell us when the event has been processed.
michael@0 470
michael@0 471 nsRefPtr<nsThreadSyncDispatch> wrapper =
michael@0 472 new nsThreadSyncDispatch(thread, event);
michael@0 473 if (!wrapper)
michael@0 474 return NS_ERROR_OUT_OF_MEMORY;
michael@0 475 nsresult rv = PutEvent(wrapper, target);
michael@0 476 // Don't wait for the event to finish if we didn't dispatch it...
michael@0 477 if (NS_FAILED(rv))
michael@0 478 return rv;
michael@0 479
michael@0 480 // Allows waiting; ensure no locks are held that would deadlock us!
michael@0 481 while (wrapper->IsPending())
michael@0 482 NS_ProcessNextEvent(thread, true);
michael@0 483 return wrapper->Result();
michael@0 484 }
michael@0 485
michael@0 486 NS_ASSERTION(flags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
michael@0 487 return PutEvent(event, target);
michael@0 488 }
michael@0 489
michael@0 490 //-----------------------------------------------------------------------------
michael@0 491 // nsIEventTarget
michael@0 492
michael@0 493 NS_IMETHODIMP
michael@0 494 nsThread::Dispatch(nsIRunnable *event, uint32_t flags)
michael@0 495 {
michael@0 496 LOG(("THRD(%p) Dispatch [%p %x]\n", this, event, flags));
michael@0 497
michael@0 498 return DispatchInternal(event, flags, nullptr);
michael@0 499 }
michael@0 500
michael@0 501 NS_IMETHODIMP
michael@0 502 nsThread::IsOnCurrentThread(bool *result)
michael@0 503 {
michael@0 504 *result = (PR_GetCurrentThread() == mThread);
michael@0 505 return NS_OK;
michael@0 506 }
michael@0 507
michael@0 508 //-----------------------------------------------------------------------------
michael@0 509 // nsIThread
michael@0 510
michael@0 511 NS_IMETHODIMP
michael@0 512 nsThread::GetPRThread(PRThread **result)
michael@0 513 {
michael@0 514 *result = mThread;
michael@0 515 return NS_OK;
michael@0 516 }
michael@0 517
michael@0 518 NS_IMETHODIMP
michael@0 519 nsThread::Shutdown()
michael@0 520 {
michael@0 521 LOG(("THRD(%p) shutdown\n", this));
michael@0 522
michael@0 523 // XXX If we make this warn, then we hit that warning at xpcom shutdown while
michael@0 524 // shutting down a thread in a thread pool. That happens b/c the thread
michael@0 525 // in the thread pool is already shutdown by the thread manager.
michael@0 526 if (!mThread)
michael@0 527 return NS_OK;
michael@0 528
michael@0 529 if (NS_WARN_IF(mThread == PR_GetCurrentThread()))
michael@0 530 return NS_ERROR_UNEXPECTED;
michael@0 531
michael@0 532 // Prevent multiple calls to this method
michael@0 533 {
michael@0 534 MutexAutoLock lock(mLock);
michael@0 535 if (!mShutdownRequired)
michael@0 536 return NS_ERROR_UNEXPECTED;
michael@0 537 mShutdownRequired = false;
michael@0 538 }
michael@0 539
michael@0 540 nsThreadShutdownContext context;
michael@0 541 context.joiningThread = nsThreadManager::get()->GetCurrentThread();
michael@0 542 context.shutdownAck = false;
michael@0 543
michael@0 544 // Set mShutdownContext and wake up the thread in case it is waiting for
michael@0 545 // events to process.
michael@0 546 nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, &context);
michael@0 547 if (!event)
michael@0 548 return NS_ERROR_OUT_OF_MEMORY;
michael@0 549 // XXXroc What if posting the event fails due to OOM?
michael@0 550 PutEvent(event, nullptr);
michael@0 551
michael@0 552 // We could still end up with other events being added after the shutdown
michael@0 553 // task, but that's okay because we process pending events in ThreadFunc
michael@0 554 // after setting mShutdownContext just before exiting.
michael@0 555
michael@0 556 // Process events on the current thread until we receive a shutdown ACK.
michael@0 557 // Allows waiting; ensure no locks are held that would deadlock us!
michael@0 558 while (!context.shutdownAck)
michael@0 559 NS_ProcessNextEvent(context.joiningThread, true);
michael@0 560
michael@0 561 // Now, it should be safe to join without fear of dead-locking.
michael@0 562
michael@0 563 PR_JoinThread(mThread);
michael@0 564 mThread = nullptr;
michael@0 565
michael@0 566 // We hold strong references to our event observers, and once the thread is
michael@0 567 // shut down the observers can't easily unregister themselves. Do it here
michael@0 568 // to avoid leaking.
michael@0 569 ClearObservers();
michael@0 570
michael@0 571 #ifdef DEBUG
michael@0 572 {
michael@0 573 MutexAutoLock lock(mLock);
michael@0 574 MOZ_ASSERT(!mObserver, "Should have been cleared at shutdown!");
michael@0 575 }
michael@0 576 #endif
michael@0 577
michael@0 578 return NS_OK;
michael@0 579 }
michael@0 580
michael@0 581 NS_IMETHODIMP
michael@0 582 nsThread::HasPendingEvents(bool *result)
michael@0 583 {
michael@0 584 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
michael@0 585 return NS_ERROR_NOT_SAME_THREAD;
michael@0 586
michael@0 587 *result = mEvents->GetEvent(false, nullptr);
michael@0 588 return NS_OK;
michael@0 589 }
michael@0 590
michael@0 591 #ifdef MOZ_CANARY
michael@0 592 void canary_alarm_handler (int signum);
michael@0 593
michael@0 594 class Canary {
michael@0 595 //XXX ToDo: support nested loops
michael@0 596 public:
michael@0 597 Canary() {
michael@0 598 if (sCanaryOutputFD > 0 && EventLatencyIsImportant()) {
michael@0 599 signal(SIGALRM, canary_alarm_handler);
michael@0 600 ualarm(15000, 0);
michael@0 601 }
michael@0 602 }
michael@0 603
michael@0 604 ~Canary() {
michael@0 605 if (sCanaryOutputFD != 0 && EventLatencyIsImportant())
michael@0 606 ualarm(0, 0);
michael@0 607 }
michael@0 608
michael@0 609 static bool EventLatencyIsImportant() {
michael@0 610 return NS_IsMainThread() && XRE_GetProcessType() == GeckoProcessType_Default;
michael@0 611 }
michael@0 612 };
michael@0 613
michael@0 614 void canary_alarm_handler (int signum)
michael@0 615 {
michael@0 616 void *array[30];
michael@0 617 const char msg[29] = "event took too long to run:\n";
michael@0 618 // use write to be safe in the signal handler
michael@0 619 write(sCanaryOutputFD, msg, sizeof(msg));
michael@0 620 backtrace_symbols_fd(array, backtrace(array, 30), sCanaryOutputFD);
michael@0 621 }
michael@0 622
michael@0 623 #endif
michael@0 624
michael@0 625 #define NOTIFY_EVENT_OBSERVERS(func_, params_) \
michael@0 626 PR_BEGIN_MACRO \
michael@0 627 if (!mEventObservers.IsEmpty()) { \
michael@0 628 nsAutoTObserverArray<nsCOMPtr<nsIThreadObserver>, 2>::ForwardIterator \
michael@0 629 iter_(mEventObservers); \
michael@0 630 nsCOMPtr<nsIThreadObserver> obs_; \
michael@0 631 while (iter_.HasMore()) { \
michael@0 632 obs_ = iter_.GetNext(); \
michael@0 633 obs_ -> func_ params_ ; \
michael@0 634 } \
michael@0 635 } \
michael@0 636 PR_END_MACRO
michael@0 637
michael@0 638 NS_IMETHODIMP
michael@0 639 nsThread::ProcessNextEvent(bool mayWait, bool *result)
michael@0 640 {
michael@0 641 LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, mayWait, mRunningEvent));
michael@0 642
michael@0 643 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
michael@0 644 return NS_ERROR_NOT_SAME_THREAD;
michael@0 645
michael@0 646 // The toplevel event loop normally blocks waiting for the next event, but
michael@0 647 // if we're trying to shut this thread down, we must exit the event loop when
michael@0 648 // the event queue is empty.
michael@0 649 // This only applys to the toplevel event loop! Nested event loops (e.g.
michael@0 650 // during sync dispatch) are waiting for some state change and must be able
michael@0 651 // to block even if something has requested shutdown of the thread. Otherwise
michael@0 652 // we'll just busywait as we endlessly look for an event, fail to find one,
michael@0 653 // and repeat the nested event loop since its state change hasn't happened yet.
michael@0 654 bool reallyWait = mayWait && (mRunningEvent > 0 || !ShuttingDown());
michael@0 655
michael@0 656 if (MAIN_THREAD == mIsMainThread && reallyWait)
michael@0 657 HangMonitor::Suspend();
michael@0 658
michael@0 659 // Fire a memory pressure notification, if we're the main thread and one is
michael@0 660 // pending.
michael@0 661 if (MAIN_THREAD == mIsMainThread && !ShuttingDown()) {
michael@0 662 MemoryPressureState mpPending = NS_GetPendingMemoryPressure();
michael@0 663 if (mpPending != MemPressure_None) {
michael@0 664 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
michael@0 665
michael@0 666 // Use no-forward to prevent the notifications from being transferred to
michael@0 667 // the children of this process.
michael@0 668 NS_NAMED_LITERAL_STRING(lowMem, "low-memory-no-forward");
michael@0 669 NS_NAMED_LITERAL_STRING(lowMemOngoing, "low-memory-ongoing-no-forward");
michael@0 670
michael@0 671 if (os) {
michael@0 672 os->NotifyObservers(nullptr, "memory-pressure",
michael@0 673 mpPending == MemPressure_New ? lowMem.get() :
michael@0 674 lowMemOngoing.get());
michael@0 675 } else {
michael@0 676 NS_WARNING("Can't get observer service!");
michael@0 677 }
michael@0 678 }
michael@0 679 }
michael@0 680
michael@0 681 bool notifyMainThreadObserver =
michael@0 682 (MAIN_THREAD == mIsMainThread) && sMainThreadObserver;
michael@0 683 if (notifyMainThreadObserver)
michael@0 684 sMainThreadObserver->OnProcessNextEvent(this, reallyWait, mRunningEvent);
michael@0 685
michael@0 686 nsCOMPtr<nsIThreadObserver> obs = mObserver;
michael@0 687 if (obs)
michael@0 688 obs->OnProcessNextEvent(this, reallyWait, mRunningEvent);
michael@0 689
michael@0 690 NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent,
michael@0 691 (this, reallyWait, mRunningEvent));
michael@0 692
michael@0 693 ++mRunningEvent;
michael@0 694
michael@0 695 #ifdef MOZ_CANARY
michael@0 696 Canary canary;
michael@0 697 #endif
michael@0 698 nsresult rv = NS_OK;
michael@0 699
michael@0 700 {
michael@0 701 // Scope for |event| to make sure that its destructor fires while
michael@0 702 // mRunningEvent has been incremented, since that destructor can
michael@0 703 // also do work.
michael@0 704
michael@0 705 // If we are shutting down, then do not wait for new events.
michael@0 706 nsCOMPtr<nsIRunnable> event;
michael@0 707 mEvents->GetEvent(reallyWait, getter_AddRefs(event));
michael@0 708
michael@0 709 *result = (event.get() != nullptr);
michael@0 710
michael@0 711 if (event) {
michael@0 712 LOG(("THRD(%p) running [%p]\n", this, event.get()));
michael@0 713 if (MAIN_THREAD == mIsMainThread)
michael@0 714 HangMonitor::NotifyActivity();
michael@0 715 event->Run();
michael@0 716 } else if (mayWait) {
michael@0 717 MOZ_ASSERT(ShuttingDown(),
michael@0 718 "This should only happen when shutting down");
michael@0 719 rv = NS_ERROR_UNEXPECTED;
michael@0 720 }
michael@0 721 }
michael@0 722
michael@0 723 --mRunningEvent;
michael@0 724
michael@0 725 NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent,
michael@0 726 (this, mRunningEvent, *result));
michael@0 727
michael@0 728 if (obs)
michael@0 729 obs->AfterProcessNextEvent(this, mRunningEvent, *result);
michael@0 730
michael@0 731 if (notifyMainThreadObserver && sMainThreadObserver)
michael@0 732 sMainThreadObserver->AfterProcessNextEvent(this, mRunningEvent, *result);
michael@0 733
michael@0 734 return rv;
michael@0 735 }
michael@0 736
michael@0 737 //-----------------------------------------------------------------------------
michael@0 738 // nsISupportsPriority
michael@0 739
michael@0 740 NS_IMETHODIMP
michael@0 741 nsThread::GetPriority(int32_t *priority)
michael@0 742 {
michael@0 743 *priority = mPriority;
michael@0 744 return NS_OK;
michael@0 745 }
michael@0 746
michael@0 747 NS_IMETHODIMP
michael@0 748 nsThread::SetPriority(int32_t priority)
michael@0 749 {
michael@0 750 if (NS_WARN_IF(!mThread))
michael@0 751 return NS_ERROR_NOT_INITIALIZED;
michael@0 752
michael@0 753 // NSPR defines the following four thread priorities:
michael@0 754 // PR_PRIORITY_LOW
michael@0 755 // PR_PRIORITY_NORMAL
michael@0 756 // PR_PRIORITY_HIGH
michael@0 757 // PR_PRIORITY_URGENT
michael@0 758 // We map the priority values defined on nsISupportsPriority to these values.
michael@0 759
michael@0 760 mPriority = priority;
michael@0 761
michael@0 762 PRThreadPriority pri;
michael@0 763 if (mPriority <= PRIORITY_HIGHEST) {
michael@0 764 pri = PR_PRIORITY_URGENT;
michael@0 765 } else if (mPriority < PRIORITY_NORMAL) {
michael@0 766 pri = PR_PRIORITY_HIGH;
michael@0 767 } else if (mPriority > PRIORITY_NORMAL) {
michael@0 768 pri = PR_PRIORITY_LOW;
michael@0 769 } else {
michael@0 770 pri = PR_PRIORITY_NORMAL;
michael@0 771 }
michael@0 772 // If chaos mode is active, retain the randomly chosen priority
michael@0 773 if (!ChaosMode::isActive()) {
michael@0 774 PR_SetThreadPriority(mThread, pri);
michael@0 775 }
michael@0 776
michael@0 777 return NS_OK;
michael@0 778 }
michael@0 779
michael@0 780 NS_IMETHODIMP
michael@0 781 nsThread::AdjustPriority(int32_t delta)
michael@0 782 {
michael@0 783 return SetPriority(mPriority + delta);
michael@0 784 }
michael@0 785
michael@0 786 //-----------------------------------------------------------------------------
michael@0 787 // nsIThreadInternal
michael@0 788
michael@0 789 NS_IMETHODIMP
michael@0 790 nsThread::GetObserver(nsIThreadObserver **obs)
michael@0 791 {
michael@0 792 MutexAutoLock lock(mLock);
michael@0 793 NS_IF_ADDREF(*obs = mObserver);
michael@0 794 return NS_OK;
michael@0 795 }
michael@0 796
michael@0 797 NS_IMETHODIMP
michael@0 798 nsThread::SetObserver(nsIThreadObserver *obs)
michael@0 799 {
michael@0 800 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
michael@0 801 return NS_ERROR_NOT_SAME_THREAD;
michael@0 802
michael@0 803 MutexAutoLock lock(mLock);
michael@0 804 mObserver = obs;
michael@0 805 return NS_OK;
michael@0 806 }
michael@0 807
michael@0 808 NS_IMETHODIMP
michael@0 809 nsThread::GetRecursionDepth(uint32_t *depth)
michael@0 810 {
michael@0 811 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
michael@0 812 return NS_ERROR_NOT_SAME_THREAD;
michael@0 813
michael@0 814 *depth = mRunningEvent;
michael@0 815 return NS_OK;
michael@0 816 }
michael@0 817
michael@0 818 NS_IMETHODIMP
michael@0 819 nsThread::AddObserver(nsIThreadObserver *observer)
michael@0 820 {
michael@0 821 if (NS_WARN_IF(!observer))
michael@0 822 return NS_ERROR_INVALID_ARG;
michael@0 823 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
michael@0 824 return NS_ERROR_NOT_SAME_THREAD;
michael@0 825
michael@0 826 NS_WARN_IF_FALSE(!mEventObservers.Contains(observer),
michael@0 827 "Adding an observer twice!");
michael@0 828
michael@0 829 if (!mEventObservers.AppendElement(observer)) {
michael@0 830 NS_WARNING("Out of memory!");
michael@0 831 return NS_ERROR_OUT_OF_MEMORY;
michael@0 832 }
michael@0 833
michael@0 834 return NS_OK;
michael@0 835 }
michael@0 836
michael@0 837 NS_IMETHODIMP
michael@0 838 nsThread::RemoveObserver(nsIThreadObserver *observer)
michael@0 839 {
michael@0 840 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
michael@0 841 return NS_ERROR_NOT_SAME_THREAD;
michael@0 842
michael@0 843 if (observer && !mEventObservers.RemoveElement(observer)) {
michael@0 844 NS_WARNING("Removing an observer that was never added!");
michael@0 845 }
michael@0 846
michael@0 847 return NS_OK;
michael@0 848 }
michael@0 849
michael@0 850 NS_IMETHODIMP
michael@0 851 nsThread::PushEventQueue(nsIEventTarget **result)
michael@0 852 {
michael@0 853 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
michael@0 854 return NS_ERROR_NOT_SAME_THREAD;
michael@0 855
michael@0 856 nsChainedEventQueue *queue = new nsChainedEventQueue();
michael@0 857 queue->mEventTarget = new nsNestedEventTarget(this, queue);
michael@0 858
michael@0 859 {
michael@0 860 MutexAutoLock lock(mLock);
michael@0 861 queue->mNext = mEvents;
michael@0 862 mEvents = queue;
michael@0 863 }
michael@0 864
michael@0 865 NS_ADDREF(*result = queue->mEventTarget);
michael@0 866 return NS_OK;
michael@0 867 }
michael@0 868
michael@0 869 NS_IMETHODIMP
michael@0 870 nsThread::PopEventQueue(nsIEventTarget *innermostTarget)
michael@0 871 {
michael@0 872 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
michael@0 873 return NS_ERROR_NOT_SAME_THREAD;
michael@0 874
michael@0 875 if (NS_WARN_IF(!innermostTarget))
michael@0 876 return NS_ERROR_NULL_POINTER;
michael@0 877
michael@0 878 // Don't delete or release anything while holding the lock.
michael@0 879 nsAutoPtr<nsChainedEventQueue> queue;
michael@0 880 nsRefPtr<nsNestedEventTarget> target;
michael@0 881
michael@0 882 {
michael@0 883 MutexAutoLock lock(mLock);
michael@0 884
michael@0 885 // Make sure we're popping the innermost event target.
michael@0 886 if (NS_WARN_IF(mEvents->mEventTarget != innermostTarget))
michael@0 887 return NS_ERROR_UNEXPECTED;
michael@0 888
michael@0 889 MOZ_ASSERT(mEvents != &mEventsRoot);
michael@0 890
michael@0 891 queue = mEvents;
michael@0 892 mEvents = mEvents->mNext;
michael@0 893
michael@0 894 nsCOMPtr<nsIRunnable> event;
michael@0 895 while (queue->GetEvent(false, getter_AddRefs(event)))
michael@0 896 mEvents->PutEvent(event);
michael@0 897
michael@0 898 // Don't let the event target post any more events.
michael@0 899 queue->mEventTarget.swap(target);
michael@0 900 target->mQueue = nullptr;
michael@0 901 }
michael@0 902
michael@0 903 return NS_OK;
michael@0 904 }
michael@0 905
michael@0 906 nsresult
michael@0 907 nsThread::SetMainThreadObserver(nsIThreadObserver* aObserver)
michael@0 908 {
michael@0 909 if (aObserver && nsThread::sMainThreadObserver) {
michael@0 910 return NS_ERROR_NOT_AVAILABLE;
michael@0 911 }
michael@0 912
michael@0 913 if (!NS_IsMainThread()) {
michael@0 914 return NS_ERROR_UNEXPECTED;
michael@0 915 }
michael@0 916
michael@0 917 nsThread::sMainThreadObserver = aObserver;
michael@0 918 return NS_OK;
michael@0 919 }
michael@0 920
michael@0 921 //-----------------------------------------------------------------------------
michael@0 922
michael@0 923 NS_IMETHODIMP
michael@0 924 nsThreadSyncDispatch::Run()
michael@0 925 {
michael@0 926 if (mSyncTask) {
michael@0 927 mResult = mSyncTask->Run();
michael@0 928 mSyncTask = nullptr;
michael@0 929 // unblock the origin thread
michael@0 930 mOrigin->Dispatch(this, NS_DISPATCH_NORMAL);
michael@0 931 }
michael@0 932 return NS_OK;
michael@0 933 }
michael@0 934
michael@0 935 //-----------------------------------------------------------------------------
michael@0 936
michael@0 937 NS_IMPL_ISUPPORTS(nsThread::nsNestedEventTarget, nsIEventTarget)
michael@0 938
michael@0 939 NS_IMETHODIMP
michael@0 940 nsThread::nsNestedEventTarget::Dispatch(nsIRunnable *event, uint32_t flags)
michael@0 941 {
michael@0 942 LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread.get(), event,
michael@0 943 flags, this));
michael@0 944
michael@0 945 return mThread->DispatchInternal(event, flags, this);
michael@0 946 }
michael@0 947
michael@0 948 NS_IMETHODIMP
michael@0 949 nsThread::nsNestedEventTarget::IsOnCurrentThread(bool *result)
michael@0 950 {
michael@0 951 return mThread->IsOnCurrentThread(result);
michael@0 952 }

mercurial