xpcom/threads/nsThread.cpp

changeset 0
6474c204b198
     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 +}

mercurial