1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/threads/nsThread.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,952 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsThread.h" 1.11 + 1.12 +#include "base/message_loop.h" 1.13 + 1.14 +// Chromium's logging can sometimes leak through... 1.15 +#ifdef LOG 1.16 +#undef LOG 1.17 +#endif 1.18 + 1.19 +#include "mozilla/ReentrantMonitor.h" 1.20 +#include "nsMemoryPressure.h" 1.21 +#include "nsThreadManager.h" 1.22 +#include "nsIClassInfoImpl.h" 1.23 +#include "nsIProgrammingLanguage.h" 1.24 +#include "nsAutoPtr.h" 1.25 +#include "nsCOMPtr.h" 1.26 +#include "pratom.h" 1.27 +#include "prlog.h" 1.28 +#include "nsIObserverService.h" 1.29 +#include "mozilla/HangMonitor.h" 1.30 +#include "mozilla/IOInterposer.h" 1.31 +#include "mozilla/Services.h" 1.32 +#include "nsXPCOMPrivate.h" 1.33 +#include "mozilla/ChaosMode.h" 1.34 + 1.35 +#ifdef XP_LINUX 1.36 +#include <sys/time.h> 1.37 +#include <sys/resource.h> 1.38 +#include <sched.h> 1.39 +#endif 1.40 + 1.41 +#define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 || \ 1.42 + _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) && \ 1.43 + !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700) 1.44 + 1.45 +#if defined(XP_LINUX) && !defined(ANDROID) && defined(_GNU_SOURCE) 1.46 +#define HAVE_SCHED_SETAFFINITY 1.47 +#endif 1.48 + 1.49 +#ifdef MOZ_CANARY 1.50 +# include <unistd.h> 1.51 +# include <execinfo.h> 1.52 +# include <signal.h> 1.53 +# include <fcntl.h> 1.54 +# include "nsXULAppAPI.h" 1.55 +#endif 1.56 + 1.57 +#if defined(NS_FUNCTION_TIMER) && defined(_MSC_VER) 1.58 +#include "nsTimerImpl.h" 1.59 +#include "nsStackWalk.h" 1.60 +#endif 1.61 +#ifdef NS_FUNCTION_TIMER 1.62 +#include "nsCRT.h" 1.63 +#endif 1.64 + 1.65 +#ifdef MOZ_TASK_TRACER 1.66 +#include "GeckoTaskTracer.h" 1.67 +using namespace mozilla::tasktracer; 1.68 +#endif 1.69 + 1.70 +using namespace mozilla; 1.71 + 1.72 +#ifdef PR_LOGGING 1.73 +static PRLogModuleInfo * 1.74 +GetThreadLog() 1.75 +{ 1.76 + static PRLogModuleInfo *sLog; 1.77 + if (!sLog) 1.78 + sLog = PR_NewLogModule("nsThread"); 1.79 + return sLog; 1.80 +} 1.81 +#endif 1.82 +#ifdef LOG 1.83 +#undef LOG 1.84 +#endif 1.85 +#define LOG(args) PR_LOG(GetThreadLog(), PR_LOG_DEBUG, args) 1.86 + 1.87 +NS_DECL_CI_INTERFACE_GETTER(nsThread) 1.88 + 1.89 +nsIThreadObserver* nsThread::sMainThreadObserver = nullptr; 1.90 + 1.91 +//----------------------------------------------------------------------------- 1.92 +// Because we do not have our own nsIFactory, we have to implement nsIClassInfo 1.93 +// somewhat manually. 1.94 + 1.95 +class nsThreadClassInfo : public nsIClassInfo { 1.96 +public: 1.97 + NS_DECL_ISUPPORTS_INHERITED // no mRefCnt 1.98 + NS_DECL_NSICLASSINFO 1.99 + 1.100 + nsThreadClassInfo() {} 1.101 +}; 1.102 + 1.103 +NS_IMETHODIMP_(MozExternalRefCountType) nsThreadClassInfo::AddRef() { return 2; } 1.104 +NS_IMETHODIMP_(MozExternalRefCountType) nsThreadClassInfo::Release() { return 1; } 1.105 +NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo, nsIClassInfo) 1.106 + 1.107 +NS_IMETHODIMP 1.108 +nsThreadClassInfo::GetInterfaces(uint32_t *count, nsIID ***array) 1.109 +{ 1.110 + return NS_CI_INTERFACE_GETTER_NAME(nsThread)(count, array); 1.111 +} 1.112 + 1.113 +NS_IMETHODIMP 1.114 +nsThreadClassInfo::GetHelperForLanguage(uint32_t lang, nsISupports **result) 1.115 +{ 1.116 + *result = nullptr; 1.117 + return NS_OK; 1.118 +} 1.119 + 1.120 +NS_IMETHODIMP 1.121 +nsThreadClassInfo::GetContractID(char **result) 1.122 +{ 1.123 + *result = nullptr; 1.124 + return NS_OK; 1.125 +} 1.126 + 1.127 +NS_IMETHODIMP 1.128 +nsThreadClassInfo::GetClassDescription(char **result) 1.129 +{ 1.130 + *result = nullptr; 1.131 + return NS_OK; 1.132 +} 1.133 + 1.134 +NS_IMETHODIMP 1.135 +nsThreadClassInfo::GetClassID(nsCID **result) 1.136 +{ 1.137 + *result = nullptr; 1.138 + return NS_OK; 1.139 +} 1.140 + 1.141 +NS_IMETHODIMP 1.142 +nsThreadClassInfo::GetImplementationLanguage(uint32_t *result) 1.143 +{ 1.144 + *result = nsIProgrammingLanguage::CPLUSPLUS; 1.145 + return NS_OK; 1.146 +} 1.147 + 1.148 +NS_IMETHODIMP 1.149 +nsThreadClassInfo::GetFlags(uint32_t *result) 1.150 +{ 1.151 + *result = THREADSAFE; 1.152 + return NS_OK; 1.153 +} 1.154 + 1.155 +NS_IMETHODIMP 1.156 +nsThreadClassInfo::GetClassIDNoAlloc(nsCID *result) 1.157 +{ 1.158 + return NS_ERROR_NOT_AVAILABLE; 1.159 +} 1.160 + 1.161 +//----------------------------------------------------------------------------- 1.162 + 1.163 +NS_IMPL_ADDREF(nsThread) 1.164 +NS_IMPL_RELEASE(nsThread) 1.165 +NS_INTERFACE_MAP_BEGIN(nsThread) 1.166 + NS_INTERFACE_MAP_ENTRY(nsIThread) 1.167 + NS_INTERFACE_MAP_ENTRY(nsIThreadInternal) 1.168 + NS_INTERFACE_MAP_ENTRY(nsIEventTarget) 1.169 + NS_INTERFACE_MAP_ENTRY(nsISupportsPriority) 1.170 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread) 1.171 + if (aIID.Equals(NS_GET_IID(nsIClassInfo))) { 1.172 + static nsThreadClassInfo sThreadClassInfo; 1.173 + foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo); 1.174 + } else 1.175 +NS_INTERFACE_MAP_END 1.176 +NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal, 1.177 + nsIEventTarget, nsISupportsPriority) 1.178 + 1.179 +//----------------------------------------------------------------------------- 1.180 + 1.181 +class nsThreadStartupEvent : public nsRunnable { 1.182 +public: 1.183 + nsThreadStartupEvent() 1.184 + : mMon("nsThreadStartupEvent.mMon") 1.185 + , mInitialized(false) { 1.186 + } 1.187 + 1.188 + // This method does not return until the thread startup object is in the 1.189 + // completion state. 1.190 + void Wait() { 1.191 + if (mInitialized) // Maybe avoid locking... 1.192 + return; 1.193 + ReentrantMonitorAutoEnter mon(mMon); 1.194 + while (!mInitialized) 1.195 + mon.Wait(); 1.196 + } 1.197 + 1.198 + // This method needs to be public to support older compilers (xlC_r on AIX). 1.199 + // It should be called directly as this class type is reference counted. 1.200 + virtual ~nsThreadStartupEvent() { 1.201 + } 1.202 + 1.203 +private: 1.204 + NS_IMETHOD Run() { 1.205 + ReentrantMonitorAutoEnter mon(mMon); 1.206 + mInitialized = true; 1.207 + mon.Notify(); 1.208 + return NS_OK; 1.209 + } 1.210 + 1.211 + ReentrantMonitor mMon; 1.212 + bool mInitialized; 1.213 +}; 1.214 + 1.215 +//----------------------------------------------------------------------------- 1.216 + 1.217 +struct nsThreadShutdownContext { 1.218 + nsThread *joiningThread; 1.219 + bool shutdownAck; 1.220 +}; 1.221 + 1.222 +// This event is responsible for notifying nsThread::Shutdown that it is time 1.223 +// to call PR_JoinThread. 1.224 +class nsThreadShutdownAckEvent : public nsRunnable { 1.225 +public: 1.226 + nsThreadShutdownAckEvent(nsThreadShutdownContext *ctx) 1.227 + : mShutdownContext(ctx) { 1.228 + } 1.229 + NS_IMETHOD Run() { 1.230 + mShutdownContext->shutdownAck = true; 1.231 + return NS_OK; 1.232 + } 1.233 +private: 1.234 + nsThreadShutdownContext *mShutdownContext; 1.235 +}; 1.236 + 1.237 +// This event is responsible for setting mShutdownContext 1.238 +class nsThreadShutdownEvent : public nsRunnable { 1.239 +public: 1.240 + nsThreadShutdownEvent(nsThread *thr, nsThreadShutdownContext *ctx) 1.241 + : mThread(thr), mShutdownContext(ctx) { 1.242 + } 1.243 + NS_IMETHOD Run() { 1.244 + mThread->mShutdownContext = mShutdownContext; 1.245 + MessageLoop::current()->Quit(); 1.246 + return NS_OK; 1.247 + } 1.248 +private: 1.249 + nsRefPtr<nsThread> mThread; 1.250 + nsThreadShutdownContext *mShutdownContext; 1.251 +}; 1.252 + 1.253 +//----------------------------------------------------------------------------- 1.254 + 1.255 +static void 1.256 +SetupCurrentThreadForChaosMode() 1.257 +{ 1.258 + if (!ChaosMode::isActive()) { 1.259 + return; 1.260 + } 1.261 + 1.262 +#ifdef XP_LINUX 1.263 + // PR_SetThreadPriority doesn't really work since priorities > 1.264 + // PR_PRIORITY_NORMAL can't be set by non-root users. Instead we'll just use 1.265 + // setpriority(2) to set random 'nice values'. In regular Linux this is only 1.266 + // a dynamic adjustment so it still doesn't really do what we want, but tools 1.267 + // like 'rr' can be more aggressive about honoring these values. 1.268 + // Some of these calls may fail due to trying to lower the priority 1.269 + // (e.g. something may have already called setpriority() for this thread). 1.270 + // This makes it hard to have non-main threads with higher priority than the 1.271 + // main thread, but that's hard to fix. Tools like rr can choose to honor the 1.272 + // requested values anyway. 1.273 + // Use just 4 priorities so there's a reasonable chance of any two threads 1.274 + // having equal priority. 1.275 + setpriority(PRIO_PROCESS, 0, ChaosMode::randomUint32LessThan(4)); 1.276 +#else 1.277 + // We should set the affinity here but NSPR doesn't provide a way to expose it. 1.278 + PR_SetThreadPriority(PR_GetCurrentThread(), 1.279 + PRThreadPriority(ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST + 1))); 1.280 +#endif 1.281 + 1.282 +#ifdef HAVE_SCHED_SETAFFINITY 1.283 + // Force half the threads to CPU 0 so they compete for CPU 1.284 + if (ChaosMode::randomUint32LessThan(2)) { 1.285 + cpu_set_t cpus; 1.286 + CPU_ZERO(&cpus); 1.287 + CPU_SET(0, &cpus); 1.288 + sched_setaffinity(0, sizeof(cpus), &cpus); 1.289 + } 1.290 +#endif 1.291 +} 1.292 + 1.293 +/*static*/ void 1.294 +nsThread::ThreadFunc(void *arg) 1.295 +{ 1.296 + nsThread *self = static_cast<nsThread *>(arg); // strong reference 1.297 + self->mThread = PR_GetCurrentThread(); 1.298 + SetupCurrentThreadForChaosMode(); 1.299 + 1.300 + // Inform the ThreadManager 1.301 + nsThreadManager::get()->RegisterCurrentThread(self); 1.302 + 1.303 + mozilla::IOInterposer::RegisterCurrentThread(); 1.304 + 1.305 + // Wait for and process startup event 1.306 + nsCOMPtr<nsIRunnable> event; 1.307 + if (!self->GetEvent(true, getter_AddRefs(event))) { 1.308 + NS_WARNING("failed waiting for thread startup event"); 1.309 + return; 1.310 + } 1.311 + event->Run(); // unblocks nsThread::Init 1.312 + event = nullptr; 1.313 + 1.314 + { // Scope for MessageLoop. 1.315 + nsAutoPtr<MessageLoop> loop( 1.316 + new MessageLoop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD)); 1.317 + 1.318 + // Now, process incoming events... 1.319 + loop->Run(); 1.320 + 1.321 + // Do NS_ProcessPendingEvents but with special handling to set 1.322 + // mEventsAreDoomed atomically with the removal of the last event. The key 1.323 + // invariant here is that we will never permit PutEvent to succeed if the 1.324 + // event would be left in the queue after our final call to 1.325 + // NS_ProcessPendingEvents. 1.326 + while (true) { 1.327 + { 1.328 + MutexAutoLock lock(self->mLock); 1.329 + if (!self->mEvents->HasPendingEvent()) { 1.330 + // No events in the queue, so we will stop now. Don't let any more 1.331 + // events be added, since they won't be processed. It is critical 1.332 + // that no PutEvent can occur between testing that the event queue is 1.333 + // empty and setting mEventsAreDoomed! 1.334 + self->mEventsAreDoomed = true; 1.335 + break; 1.336 + } 1.337 + } 1.338 + NS_ProcessPendingEvents(self); 1.339 + } 1.340 + } 1.341 + 1.342 + mozilla::IOInterposer::UnregisterCurrentThread(); 1.343 + 1.344 + // Inform the threadmanager that this thread is going away 1.345 + nsThreadManager::get()->UnregisterCurrentThread(self); 1.346 + 1.347 + // Dispatch shutdown ACK 1.348 + event = new nsThreadShutdownAckEvent(self->mShutdownContext); 1.349 + self->mShutdownContext->joiningThread->Dispatch(event, NS_DISPATCH_NORMAL); 1.350 + 1.351 + // Release any observer of the thread here. 1.352 + self->SetObserver(nullptr); 1.353 + 1.354 +#ifdef MOZ_TASK_TRACER 1.355 + FreeTraceInfo(); 1.356 +#endif 1.357 + 1.358 + NS_RELEASE(self); 1.359 +} 1.360 + 1.361 +//----------------------------------------------------------------------------- 1.362 + 1.363 +#ifdef MOZ_CANARY 1.364 +int sCanaryOutputFD = -1; 1.365 +#endif 1.366 + 1.367 +nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize) 1.368 + : mLock("nsThread.mLock") 1.369 + , mEvents(&mEventsRoot) 1.370 + , mPriority(PRIORITY_NORMAL) 1.371 + , mThread(nullptr) 1.372 + , mRunningEvent(0) 1.373 + , mStackSize(aStackSize) 1.374 + , mShutdownContext(nullptr) 1.375 + , mShutdownRequired(false) 1.376 + , mEventsAreDoomed(false) 1.377 + , mIsMainThread(aMainThread) 1.378 +{ 1.379 +} 1.380 + 1.381 +nsThread::~nsThread() 1.382 +{ 1.383 +} 1.384 + 1.385 +nsresult 1.386 +nsThread::Init() 1.387 +{ 1.388 + // spawn thread and wait until it is fully setup 1.389 + nsRefPtr<nsThreadStartupEvent> startup = new nsThreadStartupEvent(); 1.390 + 1.391 + NS_ADDREF_THIS(); 1.392 + 1.393 + mShutdownRequired = true; 1.394 + 1.395 + // ThreadFunc is responsible for setting mThread 1.396 + PRThread *thr = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this, 1.397 + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, 1.398 + PR_JOINABLE_THREAD, mStackSize); 1.399 + if (!thr) { 1.400 + NS_RELEASE_THIS(); 1.401 + return NS_ERROR_OUT_OF_MEMORY; 1.402 + } 1.403 + 1.404 + // ThreadFunc will wait for this event to be run before it tries to access 1.405 + // mThread. By delaying insertion of this event into the queue, we ensure 1.406 + // that mThread is set properly. 1.407 + { 1.408 + MutexAutoLock lock(mLock); 1.409 + mEventsRoot.PutEvent(startup); 1.410 + } 1.411 + 1.412 + // Wait for thread to call ThreadManager::SetupCurrentThread, which completes 1.413 + // initialization of ThreadFunc. 1.414 + startup->Wait(); 1.415 + return NS_OK; 1.416 +} 1.417 + 1.418 +nsresult 1.419 +nsThread::InitCurrentThread() 1.420 +{ 1.421 + mThread = PR_GetCurrentThread(); 1.422 + SetupCurrentThreadForChaosMode(); 1.423 + 1.424 + nsThreadManager::get()->RegisterCurrentThread(this); 1.425 + return NS_OK; 1.426 +} 1.427 + 1.428 +nsresult 1.429 +nsThread::PutEvent(nsIRunnable *event, nsNestedEventTarget *target) 1.430 +{ 1.431 + { 1.432 + MutexAutoLock lock(mLock); 1.433 + nsChainedEventQueue *queue = target ? target->mQueue : &mEventsRoot; 1.434 + if (!queue || (queue == &mEventsRoot && mEventsAreDoomed)) { 1.435 + NS_WARNING("An event was posted to a thread that will never run it (rejected)"); 1.436 + return NS_ERROR_UNEXPECTED; 1.437 + } 1.438 + if (!queue->PutEvent(event)) 1.439 + return NS_ERROR_OUT_OF_MEMORY; 1.440 + } 1.441 + 1.442 + nsCOMPtr<nsIThreadObserver> obs = GetObserver(); 1.443 + if (obs) 1.444 + obs->OnDispatchedEvent(this); 1.445 + 1.446 + return NS_OK; 1.447 +} 1.448 + 1.449 +nsresult 1.450 +nsThread::DispatchInternal(nsIRunnable *event, uint32_t flags, 1.451 + nsNestedEventTarget *target) 1.452 +{ 1.453 + if (NS_WARN_IF(!event)) 1.454 + return NS_ERROR_INVALID_ARG; 1.455 + 1.456 + if (gXPCOMThreadsShutDown && MAIN_THREAD != mIsMainThread && !target) { 1.457 + return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; 1.458 + } 1.459 + 1.460 +#ifdef MOZ_TASK_TRACER 1.461 + nsRefPtr<nsIRunnable> tracedRunnable = CreateTracedRunnable(event); 1.462 + event = tracedRunnable; 1.463 +#endif 1.464 + 1.465 + if (flags & DISPATCH_SYNC) { 1.466 + nsThread *thread = nsThreadManager::get()->GetCurrentThread(); 1.467 + if (NS_WARN_IF(!thread)) 1.468 + return NS_ERROR_NOT_AVAILABLE; 1.469 + 1.470 + // XXX we should be able to do something better here... we should 1.471 + // be able to monitor the slot occupied by this event and use 1.472 + // that to tell us when the event has been processed. 1.473 + 1.474 + nsRefPtr<nsThreadSyncDispatch> wrapper = 1.475 + new nsThreadSyncDispatch(thread, event); 1.476 + if (!wrapper) 1.477 + return NS_ERROR_OUT_OF_MEMORY; 1.478 + nsresult rv = PutEvent(wrapper, target); 1.479 + // Don't wait for the event to finish if we didn't dispatch it... 1.480 + if (NS_FAILED(rv)) 1.481 + return rv; 1.482 + 1.483 + // Allows waiting; ensure no locks are held that would deadlock us! 1.484 + while (wrapper->IsPending()) 1.485 + NS_ProcessNextEvent(thread, true); 1.486 + return wrapper->Result(); 1.487 + } 1.488 + 1.489 + NS_ASSERTION(flags == NS_DISPATCH_NORMAL, "unexpected dispatch flags"); 1.490 + return PutEvent(event, target); 1.491 +} 1.492 + 1.493 +//----------------------------------------------------------------------------- 1.494 +// nsIEventTarget 1.495 + 1.496 +NS_IMETHODIMP 1.497 +nsThread::Dispatch(nsIRunnable *event, uint32_t flags) 1.498 +{ 1.499 + LOG(("THRD(%p) Dispatch [%p %x]\n", this, event, flags)); 1.500 + 1.501 + return DispatchInternal(event, flags, nullptr); 1.502 +} 1.503 + 1.504 +NS_IMETHODIMP 1.505 +nsThread::IsOnCurrentThread(bool *result) 1.506 +{ 1.507 + *result = (PR_GetCurrentThread() == mThread); 1.508 + return NS_OK; 1.509 +} 1.510 + 1.511 +//----------------------------------------------------------------------------- 1.512 +// nsIThread 1.513 + 1.514 +NS_IMETHODIMP 1.515 +nsThread::GetPRThread(PRThread **result) 1.516 +{ 1.517 + *result = mThread; 1.518 + return NS_OK; 1.519 +} 1.520 + 1.521 +NS_IMETHODIMP 1.522 +nsThread::Shutdown() 1.523 +{ 1.524 + LOG(("THRD(%p) shutdown\n", this)); 1.525 + 1.526 + // XXX If we make this warn, then we hit that warning at xpcom shutdown while 1.527 + // shutting down a thread in a thread pool. That happens b/c the thread 1.528 + // in the thread pool is already shutdown by the thread manager. 1.529 + if (!mThread) 1.530 + return NS_OK; 1.531 + 1.532 + if (NS_WARN_IF(mThread == PR_GetCurrentThread())) 1.533 + return NS_ERROR_UNEXPECTED; 1.534 + 1.535 + // Prevent multiple calls to this method 1.536 + { 1.537 + MutexAutoLock lock(mLock); 1.538 + if (!mShutdownRequired) 1.539 + return NS_ERROR_UNEXPECTED; 1.540 + mShutdownRequired = false; 1.541 + } 1.542 + 1.543 + nsThreadShutdownContext context; 1.544 + context.joiningThread = nsThreadManager::get()->GetCurrentThread(); 1.545 + context.shutdownAck = false; 1.546 + 1.547 + // Set mShutdownContext and wake up the thread in case it is waiting for 1.548 + // events to process. 1.549 + nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, &context); 1.550 + if (!event) 1.551 + return NS_ERROR_OUT_OF_MEMORY; 1.552 + // XXXroc What if posting the event fails due to OOM? 1.553 + PutEvent(event, nullptr); 1.554 + 1.555 + // We could still end up with other events being added after the shutdown 1.556 + // task, but that's okay because we process pending events in ThreadFunc 1.557 + // after setting mShutdownContext just before exiting. 1.558 + 1.559 + // Process events on the current thread until we receive a shutdown ACK. 1.560 + // Allows waiting; ensure no locks are held that would deadlock us! 1.561 + while (!context.shutdownAck) 1.562 + NS_ProcessNextEvent(context.joiningThread, true); 1.563 + 1.564 + // Now, it should be safe to join without fear of dead-locking. 1.565 + 1.566 + PR_JoinThread(mThread); 1.567 + mThread = nullptr; 1.568 + 1.569 + // We hold strong references to our event observers, and once the thread is 1.570 + // shut down the observers can't easily unregister themselves. Do it here 1.571 + // to avoid leaking. 1.572 + ClearObservers(); 1.573 + 1.574 +#ifdef DEBUG 1.575 + { 1.576 + MutexAutoLock lock(mLock); 1.577 + MOZ_ASSERT(!mObserver, "Should have been cleared at shutdown!"); 1.578 + } 1.579 +#endif 1.580 + 1.581 + return NS_OK; 1.582 +} 1.583 + 1.584 +NS_IMETHODIMP 1.585 +nsThread::HasPendingEvents(bool *result) 1.586 +{ 1.587 + if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) 1.588 + return NS_ERROR_NOT_SAME_THREAD; 1.589 + 1.590 + *result = mEvents->GetEvent(false, nullptr); 1.591 + return NS_OK; 1.592 +} 1.593 + 1.594 +#ifdef MOZ_CANARY 1.595 +void canary_alarm_handler (int signum); 1.596 + 1.597 +class Canary { 1.598 +//XXX ToDo: support nested loops 1.599 +public: 1.600 + Canary() { 1.601 + if (sCanaryOutputFD > 0 && EventLatencyIsImportant()) { 1.602 + signal(SIGALRM, canary_alarm_handler); 1.603 + ualarm(15000, 0); 1.604 + } 1.605 + } 1.606 + 1.607 + ~Canary() { 1.608 + if (sCanaryOutputFD != 0 && EventLatencyIsImportant()) 1.609 + ualarm(0, 0); 1.610 + } 1.611 + 1.612 + static bool EventLatencyIsImportant() { 1.613 + return NS_IsMainThread() && XRE_GetProcessType() == GeckoProcessType_Default; 1.614 + } 1.615 +}; 1.616 + 1.617 +void canary_alarm_handler (int signum) 1.618 +{ 1.619 + void *array[30]; 1.620 + const char msg[29] = "event took too long to run:\n"; 1.621 + // use write to be safe in the signal handler 1.622 + write(sCanaryOutputFD, msg, sizeof(msg)); 1.623 + backtrace_symbols_fd(array, backtrace(array, 30), sCanaryOutputFD); 1.624 +} 1.625 + 1.626 +#endif 1.627 + 1.628 +#define NOTIFY_EVENT_OBSERVERS(func_, params_) \ 1.629 + PR_BEGIN_MACRO \ 1.630 + if (!mEventObservers.IsEmpty()) { \ 1.631 + nsAutoTObserverArray<nsCOMPtr<nsIThreadObserver>, 2>::ForwardIterator \ 1.632 + iter_(mEventObservers); \ 1.633 + nsCOMPtr<nsIThreadObserver> obs_; \ 1.634 + while (iter_.HasMore()) { \ 1.635 + obs_ = iter_.GetNext(); \ 1.636 + obs_ -> func_ params_ ; \ 1.637 + } \ 1.638 + } \ 1.639 + PR_END_MACRO 1.640 + 1.641 +NS_IMETHODIMP 1.642 +nsThread::ProcessNextEvent(bool mayWait, bool *result) 1.643 +{ 1.644 + LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, mayWait, mRunningEvent)); 1.645 + 1.646 + if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) 1.647 + return NS_ERROR_NOT_SAME_THREAD; 1.648 + 1.649 + // The toplevel event loop normally blocks waiting for the next event, but 1.650 + // if we're trying to shut this thread down, we must exit the event loop when 1.651 + // the event queue is empty. 1.652 + // This only applys to the toplevel event loop! Nested event loops (e.g. 1.653 + // during sync dispatch) are waiting for some state change and must be able 1.654 + // to block even if something has requested shutdown of the thread. Otherwise 1.655 + // we'll just busywait as we endlessly look for an event, fail to find one, 1.656 + // and repeat the nested event loop since its state change hasn't happened yet. 1.657 + bool reallyWait = mayWait && (mRunningEvent > 0 || !ShuttingDown()); 1.658 + 1.659 + if (MAIN_THREAD == mIsMainThread && reallyWait) 1.660 + HangMonitor::Suspend(); 1.661 + 1.662 + // Fire a memory pressure notification, if we're the main thread and one is 1.663 + // pending. 1.664 + if (MAIN_THREAD == mIsMainThread && !ShuttingDown()) { 1.665 + MemoryPressureState mpPending = NS_GetPendingMemoryPressure(); 1.666 + if (mpPending != MemPressure_None) { 1.667 + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); 1.668 + 1.669 + // Use no-forward to prevent the notifications from being transferred to 1.670 + // the children of this process. 1.671 + NS_NAMED_LITERAL_STRING(lowMem, "low-memory-no-forward"); 1.672 + NS_NAMED_LITERAL_STRING(lowMemOngoing, "low-memory-ongoing-no-forward"); 1.673 + 1.674 + if (os) { 1.675 + os->NotifyObservers(nullptr, "memory-pressure", 1.676 + mpPending == MemPressure_New ? lowMem.get() : 1.677 + lowMemOngoing.get()); 1.678 + } else { 1.679 + NS_WARNING("Can't get observer service!"); 1.680 + } 1.681 + } 1.682 + } 1.683 + 1.684 + bool notifyMainThreadObserver = 1.685 + (MAIN_THREAD == mIsMainThread) && sMainThreadObserver; 1.686 + if (notifyMainThreadObserver) 1.687 + sMainThreadObserver->OnProcessNextEvent(this, reallyWait, mRunningEvent); 1.688 + 1.689 + nsCOMPtr<nsIThreadObserver> obs = mObserver; 1.690 + if (obs) 1.691 + obs->OnProcessNextEvent(this, reallyWait, mRunningEvent); 1.692 + 1.693 + NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent, 1.694 + (this, reallyWait, mRunningEvent)); 1.695 + 1.696 + ++mRunningEvent; 1.697 + 1.698 +#ifdef MOZ_CANARY 1.699 + Canary canary; 1.700 +#endif 1.701 + nsresult rv = NS_OK; 1.702 + 1.703 + { 1.704 + // Scope for |event| to make sure that its destructor fires while 1.705 + // mRunningEvent has been incremented, since that destructor can 1.706 + // also do work. 1.707 + 1.708 + // If we are shutting down, then do not wait for new events. 1.709 + nsCOMPtr<nsIRunnable> event; 1.710 + mEvents->GetEvent(reallyWait, getter_AddRefs(event)); 1.711 + 1.712 + *result = (event.get() != nullptr); 1.713 + 1.714 + if (event) { 1.715 + LOG(("THRD(%p) running [%p]\n", this, event.get())); 1.716 + if (MAIN_THREAD == mIsMainThread) 1.717 + HangMonitor::NotifyActivity(); 1.718 + event->Run(); 1.719 + } else if (mayWait) { 1.720 + MOZ_ASSERT(ShuttingDown(), 1.721 + "This should only happen when shutting down"); 1.722 + rv = NS_ERROR_UNEXPECTED; 1.723 + } 1.724 + } 1.725 + 1.726 + --mRunningEvent; 1.727 + 1.728 + NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent, 1.729 + (this, mRunningEvent, *result)); 1.730 + 1.731 + if (obs) 1.732 + obs->AfterProcessNextEvent(this, mRunningEvent, *result); 1.733 + 1.734 + if (notifyMainThreadObserver && sMainThreadObserver) 1.735 + sMainThreadObserver->AfterProcessNextEvent(this, mRunningEvent, *result); 1.736 + 1.737 + return rv; 1.738 +} 1.739 + 1.740 +//----------------------------------------------------------------------------- 1.741 +// nsISupportsPriority 1.742 + 1.743 +NS_IMETHODIMP 1.744 +nsThread::GetPriority(int32_t *priority) 1.745 +{ 1.746 + *priority = mPriority; 1.747 + return NS_OK; 1.748 +} 1.749 + 1.750 +NS_IMETHODIMP 1.751 +nsThread::SetPriority(int32_t priority) 1.752 +{ 1.753 + if (NS_WARN_IF(!mThread)) 1.754 + return NS_ERROR_NOT_INITIALIZED; 1.755 + 1.756 + // NSPR defines the following four thread priorities: 1.757 + // PR_PRIORITY_LOW 1.758 + // PR_PRIORITY_NORMAL 1.759 + // PR_PRIORITY_HIGH 1.760 + // PR_PRIORITY_URGENT 1.761 + // We map the priority values defined on nsISupportsPriority to these values. 1.762 + 1.763 + mPriority = priority; 1.764 + 1.765 + PRThreadPriority pri; 1.766 + if (mPriority <= PRIORITY_HIGHEST) { 1.767 + pri = PR_PRIORITY_URGENT; 1.768 + } else if (mPriority < PRIORITY_NORMAL) { 1.769 + pri = PR_PRIORITY_HIGH; 1.770 + } else if (mPriority > PRIORITY_NORMAL) { 1.771 + pri = PR_PRIORITY_LOW; 1.772 + } else { 1.773 + pri = PR_PRIORITY_NORMAL; 1.774 + } 1.775 + // If chaos mode is active, retain the randomly chosen priority 1.776 + if (!ChaosMode::isActive()) { 1.777 + PR_SetThreadPriority(mThread, pri); 1.778 + } 1.779 + 1.780 + return NS_OK; 1.781 +} 1.782 + 1.783 +NS_IMETHODIMP 1.784 +nsThread::AdjustPriority(int32_t delta) 1.785 +{ 1.786 + return SetPriority(mPriority + delta); 1.787 +} 1.788 + 1.789 +//----------------------------------------------------------------------------- 1.790 +// nsIThreadInternal 1.791 + 1.792 +NS_IMETHODIMP 1.793 +nsThread::GetObserver(nsIThreadObserver **obs) 1.794 +{ 1.795 + MutexAutoLock lock(mLock); 1.796 + NS_IF_ADDREF(*obs = mObserver); 1.797 + return NS_OK; 1.798 +} 1.799 + 1.800 +NS_IMETHODIMP 1.801 +nsThread::SetObserver(nsIThreadObserver *obs) 1.802 +{ 1.803 + if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) 1.804 + return NS_ERROR_NOT_SAME_THREAD; 1.805 + 1.806 + MutexAutoLock lock(mLock); 1.807 + mObserver = obs; 1.808 + return NS_OK; 1.809 +} 1.810 + 1.811 +NS_IMETHODIMP 1.812 +nsThread::GetRecursionDepth(uint32_t *depth) 1.813 +{ 1.814 + if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) 1.815 + return NS_ERROR_NOT_SAME_THREAD; 1.816 + 1.817 + *depth = mRunningEvent; 1.818 + return NS_OK; 1.819 +} 1.820 + 1.821 +NS_IMETHODIMP 1.822 +nsThread::AddObserver(nsIThreadObserver *observer) 1.823 +{ 1.824 + if (NS_WARN_IF(!observer)) 1.825 + return NS_ERROR_INVALID_ARG; 1.826 + if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) 1.827 + return NS_ERROR_NOT_SAME_THREAD; 1.828 + 1.829 + NS_WARN_IF_FALSE(!mEventObservers.Contains(observer), 1.830 + "Adding an observer twice!"); 1.831 + 1.832 + if (!mEventObservers.AppendElement(observer)) { 1.833 + NS_WARNING("Out of memory!"); 1.834 + return NS_ERROR_OUT_OF_MEMORY; 1.835 + } 1.836 + 1.837 + return NS_OK; 1.838 +} 1.839 + 1.840 +NS_IMETHODIMP 1.841 +nsThread::RemoveObserver(nsIThreadObserver *observer) 1.842 +{ 1.843 + if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) 1.844 + return NS_ERROR_NOT_SAME_THREAD; 1.845 + 1.846 + if (observer && !mEventObservers.RemoveElement(observer)) { 1.847 + NS_WARNING("Removing an observer that was never added!"); 1.848 + } 1.849 + 1.850 + return NS_OK; 1.851 +} 1.852 + 1.853 +NS_IMETHODIMP 1.854 +nsThread::PushEventQueue(nsIEventTarget **result) 1.855 +{ 1.856 + if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) 1.857 + return NS_ERROR_NOT_SAME_THREAD; 1.858 + 1.859 + nsChainedEventQueue *queue = new nsChainedEventQueue(); 1.860 + queue->mEventTarget = new nsNestedEventTarget(this, queue); 1.861 + 1.862 + { 1.863 + MutexAutoLock lock(mLock); 1.864 + queue->mNext = mEvents; 1.865 + mEvents = queue; 1.866 + } 1.867 + 1.868 + NS_ADDREF(*result = queue->mEventTarget); 1.869 + return NS_OK; 1.870 +} 1.871 + 1.872 +NS_IMETHODIMP 1.873 +nsThread::PopEventQueue(nsIEventTarget *innermostTarget) 1.874 +{ 1.875 + if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) 1.876 + return NS_ERROR_NOT_SAME_THREAD; 1.877 + 1.878 + if (NS_WARN_IF(!innermostTarget)) 1.879 + return NS_ERROR_NULL_POINTER; 1.880 + 1.881 + // Don't delete or release anything while holding the lock. 1.882 + nsAutoPtr<nsChainedEventQueue> queue; 1.883 + nsRefPtr<nsNestedEventTarget> target; 1.884 + 1.885 + { 1.886 + MutexAutoLock lock(mLock); 1.887 + 1.888 + // Make sure we're popping the innermost event target. 1.889 + if (NS_WARN_IF(mEvents->mEventTarget != innermostTarget)) 1.890 + return NS_ERROR_UNEXPECTED; 1.891 + 1.892 + MOZ_ASSERT(mEvents != &mEventsRoot); 1.893 + 1.894 + queue = mEvents; 1.895 + mEvents = mEvents->mNext; 1.896 + 1.897 + nsCOMPtr<nsIRunnable> event; 1.898 + while (queue->GetEvent(false, getter_AddRefs(event))) 1.899 + mEvents->PutEvent(event); 1.900 + 1.901 + // Don't let the event target post any more events. 1.902 + queue->mEventTarget.swap(target); 1.903 + target->mQueue = nullptr; 1.904 + } 1.905 + 1.906 + return NS_OK; 1.907 +} 1.908 + 1.909 +nsresult 1.910 +nsThread::SetMainThreadObserver(nsIThreadObserver* aObserver) 1.911 +{ 1.912 + if (aObserver && nsThread::sMainThreadObserver) { 1.913 + return NS_ERROR_NOT_AVAILABLE; 1.914 + } 1.915 + 1.916 + if (!NS_IsMainThread()) { 1.917 + return NS_ERROR_UNEXPECTED; 1.918 + } 1.919 + 1.920 + nsThread::sMainThreadObserver = aObserver; 1.921 + return NS_OK; 1.922 +} 1.923 + 1.924 +//----------------------------------------------------------------------------- 1.925 + 1.926 +NS_IMETHODIMP 1.927 +nsThreadSyncDispatch::Run() 1.928 +{ 1.929 + if (mSyncTask) { 1.930 + mResult = mSyncTask->Run(); 1.931 + mSyncTask = nullptr; 1.932 + // unblock the origin thread 1.933 + mOrigin->Dispatch(this, NS_DISPATCH_NORMAL); 1.934 + } 1.935 + return NS_OK; 1.936 +} 1.937 + 1.938 +//----------------------------------------------------------------------------- 1.939 + 1.940 +NS_IMPL_ISUPPORTS(nsThread::nsNestedEventTarget, nsIEventTarget) 1.941 + 1.942 +NS_IMETHODIMP 1.943 +nsThread::nsNestedEventTarget::Dispatch(nsIRunnable *event, uint32_t flags) 1.944 +{ 1.945 + LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread.get(), event, 1.946 + flags, this)); 1.947 + 1.948 + return mThread->DispatchInternal(event, flags, this); 1.949 +} 1.950 + 1.951 +NS_IMETHODIMP 1.952 +nsThread::nsNestedEventTarget::IsOnCurrentThread(bool *result) 1.953 +{ 1.954 + return mThread->IsOnCurrentThread(result); 1.955 +}