mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
69d1f32ff7
- Bug 1268085 - Remove unused post barrier callbacks r=terrence (0ab13411c9) - Bug 1267699 - Move some public types to the right namespace; r=sfink (3d5008e610) - Bug 1267550 (part 1) - Rename MOZ_MUST_USE as MOZ_MUST_USE_TYPE. r=ehsan. (6f47375796) - Bug 1259021 - Rename Vector::extractRawBuffer to extractOrCopyRawBuffer r=Waldo (97ca94495b) - Bug 1259021 - Add Vector::extractRawBuffer method that doesn't copy the buffer r=Waldo (e58deec48f) - Bug 1265892 - Change Vector to use Impl::new_ consistently. r=Waldo (7a52d21b29) - Bug 1267912 - Rename nsNetUtil.inl as nsNetUtilInlines.h. r=valentin. (548a41b293) - Bug 1265690 part 1 - Mark StringBuffer methods WARN_UNUSED_RESULT, fix OOM issues. r=jonco (0d7e6837e3) - Bug 1265690 part 2 - Fix some more OOM issues in TypedObject code. r=jonco (b60902453e) - Bug 1263490 - Part 2: Add GetFirstDollarIndex intrinsic and use it inRegExpReplace. r=till (4ba19db8c4) - Bug 1263490 - Part 3: Inline GetFirstDollarIndex intrinsic. r=h4writer (e7d9b5d1cc) - Bug 1263490 - Part 4: Fold GetFirstDollarIndex into a integer constant. r=h4writer (3479c7d1af) - Bug 1267269 - Make MIRType an enum class. r=bbouvier (d580ef372a) - Bug 1259295 - BaldrMonkey: Postorder (r=luke) (6ef7a77663) - Bug 1254142: BaldrMonkey: make br_table yield (r=luke) (80e7635e58) - Bug 1263202 - BaldrMonkey: switch to arities on branches, calls and return (r=bbouvier) (f5a0358634) - Bug 1236358 - Improper reading of string16 in Pickle::ReadString16. r=jld (8370ba6a0b) - Bug 1263205 - BaldrMonkey: Update section headers for proposed spec changes (r=luke) (0def2e6bc2) - Bug 1263205 - BaldrMonkey: Update for proposed new section names (r=luke) (e57f0e3367) - Bug 1263205 - BaldrMonkey: Add 'form' field to types section (r=bbouvier) (794edc890f) - Bug 1259021 - Use in-place storage in AutoStableStringChars to avoid allocation for short strings r=jandem r=Waldo (ffb53cbcf4) - Bug 1267550 (part 2) - Rename MOZ_WARN_UNUSED_RESULT as MOZ_MUST_USE. r=froydnj. (47bc674b86) - Bug 1268518: Baldr: implement int32/int64 rotations; r=luke (0d5eedccce) - Bug 1255008: IonMonkey - Add a by default disabled flow sensitive alias analysis pass, r=jandem (521c585d75) - Bug 1266781: Baldr: implement proper checked truncations to integer types; r=sunfish (46078fb3d3) - Bug 1266781: Rename MTruncateToInt64 into MWasmTruncateInt64; r=sunfish (c7d7d1ac11) - Bug 1266781: Add new traps; r=luke (b7ed3d44e6) - Bug 1268024: Pass the atomic attribute down to EmitHeapAccess; r=luke (6195f7d7a3) - Bug 1268024: A few cleanups related to loads/stores; r=luke (88141e3a01) - Bug 1258312 - Make Pickle::Resize infallible r=jld (241ee9b60d) - Bug 1162772, part 1 - Allow CompartmentCreationOptions to store Secure Context state. r=jorendorff (ff666384cf) - Bug 1162772, part 2 - Expose whether SEC_FORCE_INHERIT_PRINCIPAL was dropped from an nsILoadInfo. r=bz (ada46f86bf) - Bug 1162772, part 3 - Add a getChannelResultPrincipalIfNotSandboxed method to nsIScriptSecurityManager. r=bz (5b1d9f6807) - Bug 1162772, part 4 - Implement nsGlobalWindow::IsSecureContext. r=bz (f392f439c9) - Bug 1162772, part 5 - Expose Window.isSecureContext to content. r=bz (e7296e2cf1) - Bug 1267509 - Make nsContentSecurityManager::IsURIPotentiallyTrustworthy act on an nsIPrincipal. r=bz (83de80350a) - Bug 1219098 - Use UniquePtr in UncompressedSourceCache, for it is good (r=jandem) (b68769c729) - Bug 1244279 - Part 1: Take a bit in ObjectElements::Flags to indicate whether the object is in the whole cell store buffer. r=terrence (968cf373f9) - Bug 1244279 - Part 0: Add a GC ubench for large arrays with both elements and properties. r=terrence (ec76b48323) - Bug 1255925 - Give a name to getters/setters and integer-named methods. r=efaust (f978cc6916) - Bug 888969 - Make the getPrototypeOf/setPrototypeOf traps scriptable. r=efaust, r=bholley (eb2325a9ea) - Bug 1267557 part 0 - Move JS poison constants to jsutil.h. r=jonco (65afc690d2) - Bug 1267557 part 1 - Also poison bytes allocated before the actual jitcode. r=nbp (70f0b327d3) - Bug 1267557 part 2 - Use different jitcode poison values. r=nbp (08008ab9dc) - Bug 1267557 part 3 - Define JS_SWEPT_CODE_PATTERN for mips. r=nbp (17e894d59d) - Bug 1267449 - Do not infinite loop in js_fputs; r=jimb (67f961b6cd) - Bug 1219098 - Reenable compression on large sources, but revert to uncompressed if decompression happens (r=jandem) (b44ee8d77d) - Bug 1267551 (part 1) - Use MOZ_MUST_USE more in jsnum.h. r=jonco. (d2476bf8f4) - Bug 1267551 (part 2) - Use MOZ_MUST_USE more in js/src/ds/. r=jonco. (4ff5d9aa88) - Bug 1267412 - Use MutableHandleValue instead of pointer-to-AutoValueVector; r=sfink (3f6dd284bb) - Bug 1266406 - Use EnumSet<AllocKind> to simplify GC sweeping phase information r=terrence (64811500e7) - Bug 1266457 - Update pointers in GC things in two phases when compacting r=terrence (f6f5bc4e4d) - Bug 1266457 - Simplify typed object trace hook r=terence (3b06c8d1e5) - Bug 1268541 - Compact arenas containing base shapes r=terrence (b458b92eea) - Bug 1268805 - Implement PrivateGCThingValue. (r=terrence) (deec9a83ae) - Bug 1268415: Initialize members in UpdatePointerTasks; r=jonco (6cb219005a) - Bug 1268501 - Release the GC lock periodically when releasing arenas on the backgound thread r=terrence (37f0997682) - Bug 1263572 - Wait for background sweeping to finish before checking base shapes r=terrence (354801a411) - Bug 1266887 - Store Rooted heads on the Zone; r=sfink (91c0101ee3) - Bug 1266402 - Add iteration to EnumSet<T> so that it can be used in range-based for loops r=Waldo (e9507a2524) - Bug 1266404 - Allow construction of an EnumSet<T> using an initializer list r=Waldo (1b6d340e99) - Bug 1254020 - Always compute theme scaling factor when per-monitor dpi aware, even if only a single display is currently present. r=emk (a00cda21f4) - Bug 1263525 - Add dedicated function for std_Array self-hosted intrinsic. r=efaust (449d8bb7eb) - Bug 1255925 - Change JSFunction::name to return a JSAtom. r=efaust (5ab396ce83) - Bug 888969 - Make our tree's sole implementation of nsIRemoteTagService.getRemoteObjectTag not depend upon the infallibility of [[GetPrototypeOf]] on the object provided to it. r=bz (f388f4bf1f) - Bug 1264896 - Kill off nsIRemoteTagService and do what it does, in its sole caller, in far-faster C++. r=billm (5ed3fb103d) - Bug 1268246 - Add a simple Poison class lifetime checker. r=froydnj (7b237bc70e) - Bug 1249496 - Don't apply dpi-based scaling for window titlebar dimensions when on a secondary display, because windows doesn't scale it. r=emk (64dd706dbc) - Bug 1164518 - Avoid unnecessary DB updates when caching Safe Browsing results. r=gcp (3cafd9a4df) - Bug 1264472 - Use nsRunnables in FIDO U2F. r=keeler (3aa9570132) - Bug 1236060 - Dispatch error should advance queue. r=smaug (74155b75dd) - Bug 1251697 part 1. Thread an ErrorResult reference through the worker XHR WorkerThreadProxySyncRunnable implementations. r=khuey (77804cbb7c) - Bug 1251697 part 2. Have WorkerThreadProxySyncRunnable hand the ErrorResult reference it holds to its ResponseRunnable so it can report exceptions on there instead of on a JSContext. r=khuey (355c9ee313) - Bug 1251697 part 3. Remove the JSContext argument of StopSyncLoopRunnable::MaybeSetException. r=khuey (010f5b1058) - Bug 1155328. r=smaug (e1f8dac304) - Bug 1265927: Move nsRunnable to mozilla::Runnable, CancelableRunnable to mozilla::CancelableRunnable. r=froydnj (f83bfcae02) - Bug 1239946 - Change test to return error on Speak. r=eeejay (1d402beb02) - Bug 1254378 - Update synth tests and introduce no voiceschanged test. r=smaug (f5823bb70e) - Bug 1251627. Fix XMLHttpRequest.send() to follow the spec better in terms of the exceptions it throws. r=khuey (cd0e321948) - Bug 1268868: [MSE] P1. Re-enable gap detection within a media segment. r=gerald (b8b8df4bc2) - Bug 1268868: [MSE] P2. Reset longest duration after keyframe is seen. r=gerald (2b1401465c) - Bug 1268868: [MSE] P3. Prevent crash should gap be detected in content. r=gerald (063d9376fc) - Bug 1254378 - Implement nsISynthVoiceRegistry.notifyVoicesChanged. r=smaug (4b63b1c360) - Bug 1266804 - Un-inline js::Unbox(); r=jorendorff (0f288b6173) - Bug 1268863 - Report ScriptSources that are only reachable via AsmJSModule (r=njn) (5ba40acb64) - bump version to 45.1b1 (1414db0ca8) - Bug 1262062 - remove old futex names. r=bbouvier (62662bdd2e) - memory: build fix after renaming MOZ_WARN_UNUSED_RESULT (7254dc8d53) - import from mozilla: - Bug 1268725 - BaldrMonkey: Refactor away the internal storage from ExprIter. r=luke (1931bd636f17) - Bug 1268725 - BaldrMonkey: Convert default arguments into explicit arguments. r=luke (c8a11b8b6bbd) (867ec715d6)
1225 lines
34 KiB
C++
1225 lines
34 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "nsThread.h"
|
|
|
|
#include "base/message_loop.h"
|
|
|
|
// Chromium's logging can sometimes leak through...
|
|
#ifdef LOG
|
|
#undef LOG
|
|
#endif
|
|
|
|
#include "mozilla/ReentrantMonitor.h"
|
|
#include "nsMemoryPressure.h"
|
|
#include "nsThreadManager.h"
|
|
#include "nsIClassInfoImpl.h"
|
|
#include "nsAutoPtr.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsQueryObject.h"
|
|
#include "pratom.h"
|
|
#include "mozilla/CycleCollectedJSRuntime.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "nsIObserverService.h"
|
|
#include "mozilla/HangMonitor.h"
|
|
#include "mozilla/IOInterposer.h"
|
|
#include "mozilla/ipc/MessageChannel.h"
|
|
#include "mozilla/ipc/BackgroundChild.h"
|
|
#include "mozilla/Services.h"
|
|
#include "nsXPCOMPrivate.h"
|
|
#include "mozilla/ChaosMode.h"
|
|
#include "mozilla/TimeStamp.h"
|
|
#include "mozilla/unused.h"
|
|
#include "nsThreadSyncDispatch.h"
|
|
#include "LeakRefPtr.h"
|
|
|
|
#ifdef MOZ_CRASHREPORTER
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsICrashReporter.h"
|
|
#endif
|
|
|
|
#ifdef MOZ_NUWA_PROCESS
|
|
#include "private/pprthred.h"
|
|
#endif
|
|
|
|
#ifdef XP_LINUX
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#include <sched.h>
|
|
#endif
|
|
|
|
#define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 || \
|
|
_XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) && \
|
|
!(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
|
|
|
|
#if defined(XP_LINUX) && !defined(ANDROID) && defined(_GNU_SOURCE)
|
|
#define HAVE_SCHED_SETAFFINITY
|
|
#endif
|
|
|
|
#ifdef XP_MACOSX
|
|
#include <mach/mach.h>
|
|
#include <mach/thread_policy.h>
|
|
#endif
|
|
|
|
#ifdef MOZ_CANARY
|
|
# include <unistd.h>
|
|
# include <execinfo.h>
|
|
# include <signal.h>
|
|
# include <fcntl.h>
|
|
# include "nsXULAppAPI.h"
|
|
#endif
|
|
|
|
#if defined(NS_FUNCTION_TIMER) && defined(_MSC_VER)
|
|
#include "nsTimerImpl.h"
|
|
#include "mozilla/StackWalk.h"
|
|
#endif
|
|
#ifdef NS_FUNCTION_TIMER
|
|
#include "nsCRT.h"
|
|
#endif
|
|
|
|
#ifdef MOZ_TASK_TRACER
|
|
#include "GeckoTaskTracer.h"
|
|
#include "TracedTaskCommon.h"
|
|
using namespace mozilla::tasktracer;
|
|
#endif
|
|
|
|
using namespace mozilla;
|
|
|
|
static LazyLogModule sThreadLog("nsThread");
|
|
#ifdef LOG
|
|
#undef LOG
|
|
#endif
|
|
#define LOG(args) MOZ_LOG(sThreadLog, mozilla::LogLevel::Debug, args)
|
|
|
|
NS_DECL_CI_INTERFACE_GETTER(nsThread)
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Because we do not have our own nsIFactory, we have to implement nsIClassInfo
|
|
// somewhat manually.
|
|
|
|
class nsThreadClassInfo : public nsIClassInfo
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS_INHERITED // no mRefCnt
|
|
NS_DECL_NSICLASSINFO
|
|
|
|
nsThreadClassInfo()
|
|
{
|
|
}
|
|
};
|
|
|
|
NS_IMETHODIMP_(MozExternalRefCountType)
|
|
nsThreadClassInfo::AddRef()
|
|
{
|
|
return 2;
|
|
}
|
|
NS_IMETHODIMP_(MozExternalRefCountType)
|
|
nsThreadClassInfo::Release()
|
|
{
|
|
return 1;
|
|
}
|
|
NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo, nsIClassInfo)
|
|
|
|
NS_IMETHODIMP
|
|
nsThreadClassInfo::GetInterfaces(uint32_t* aCount, nsIID*** aArray)
|
|
{
|
|
return NS_CI_INTERFACE_GETTER_NAME(nsThread)(aCount, aArray);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThreadClassInfo::GetScriptableHelper(nsIXPCScriptable** aResult)
|
|
{
|
|
*aResult = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThreadClassInfo::GetContractID(char** aResult)
|
|
{
|
|
*aResult = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThreadClassInfo::GetClassDescription(char** aResult)
|
|
{
|
|
*aResult = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThreadClassInfo::GetClassID(nsCID** aResult)
|
|
{
|
|
*aResult = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThreadClassInfo::GetFlags(uint32_t* aResult)
|
|
{
|
|
*aResult = THREADSAFE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThreadClassInfo::GetClassIDNoAlloc(nsCID* aResult)
|
|
{
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
NS_IMPL_ADDREF(nsThread)
|
|
NS_IMPL_RELEASE(nsThread)
|
|
NS_INTERFACE_MAP_BEGIN(nsThread)
|
|
NS_INTERFACE_MAP_ENTRY(nsIThread)
|
|
NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
|
|
NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
|
|
NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
|
|
if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
|
|
static nsThreadClassInfo sThreadClassInfo;
|
|
foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
|
|
} else
|
|
NS_INTERFACE_MAP_END
|
|
NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal,
|
|
nsIEventTarget, nsISupportsPriority)
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class nsThreadStartupEvent : public Runnable
|
|
{
|
|
public:
|
|
nsThreadStartupEvent()
|
|
: mMon("nsThreadStartupEvent.mMon")
|
|
, mInitialized(false)
|
|
{
|
|
}
|
|
|
|
// This method does not return until the thread startup object is in the
|
|
// completion state.
|
|
void Wait()
|
|
{
|
|
if (mInitialized) {
|
|
// Maybe avoid locking...
|
|
return;
|
|
}
|
|
|
|
ReentrantMonitorAutoEnter mon(mMon);
|
|
while (!mInitialized) {
|
|
mon.Wait();
|
|
}
|
|
}
|
|
|
|
// This method needs to be public to support older compilers (xlC_r on AIX).
|
|
// It should be called directly as this class type is reference counted.
|
|
virtual ~nsThreadStartupEvent() {}
|
|
|
|
private:
|
|
NS_IMETHOD Run()
|
|
{
|
|
ReentrantMonitorAutoEnter mon(mMon);
|
|
mInitialized = true;
|
|
mon.Notify();
|
|
return NS_OK;
|
|
}
|
|
|
|
ReentrantMonitor mMon;
|
|
bool mInitialized;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
struct nsThreadShutdownContext
|
|
{
|
|
nsThreadShutdownContext()
|
|
{
|
|
MOZ_COUNT_CTOR(nsThreadShutdownContext);
|
|
}
|
|
~nsThreadShutdownContext()
|
|
{
|
|
MOZ_COUNT_DTOR(nsThreadShutdownContext);
|
|
}
|
|
|
|
// NB: This will be the last reference.
|
|
RefPtr<nsThread> terminatingThread;
|
|
nsThread* joiningThread;
|
|
bool awaitingShutdownAck;
|
|
};
|
|
|
|
// This event is responsible for notifying nsThread::Shutdown that it is time
|
|
// to call PR_JoinThread. It implements nsICancelableRunnable so that it can
|
|
// run on a DOM Worker thread (where all events must implement
|
|
// nsICancelableRunnable.)
|
|
class nsThreadShutdownAckEvent : public CancelableRunnable
|
|
{
|
|
public:
|
|
explicit nsThreadShutdownAckEvent(nsThreadShutdownContext* aCtx)
|
|
: mShutdownContext(aCtx)
|
|
{
|
|
}
|
|
NS_IMETHOD Run() override
|
|
{
|
|
mShutdownContext->terminatingThread->ShutdownComplete(mShutdownContext);
|
|
return NS_OK;
|
|
}
|
|
nsresult Cancel() override
|
|
{
|
|
return Run();
|
|
}
|
|
private:
|
|
virtual ~nsThreadShutdownAckEvent() { }
|
|
|
|
nsThreadShutdownContext* mShutdownContext;
|
|
};
|
|
|
|
// This event is responsible for setting mShutdownContext
|
|
class nsThreadShutdownEvent : public Runnable
|
|
{
|
|
public:
|
|
nsThreadShutdownEvent(nsThread* aThr, nsThreadShutdownContext* aCtx)
|
|
: mThread(aThr)
|
|
, mShutdownContext(aCtx)
|
|
{
|
|
}
|
|
NS_IMETHOD Run()
|
|
{
|
|
mThread->mShutdownContext = mShutdownContext;
|
|
MessageLoop::current()->Quit();
|
|
return NS_OK;
|
|
}
|
|
private:
|
|
RefPtr<nsThread> mThread;
|
|
nsThreadShutdownContext* mShutdownContext;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static void
|
|
SetThreadAffinity(unsigned int cpu)
|
|
{
|
|
#ifdef HAVE_SCHED_SETAFFINITY
|
|
cpu_set_t cpus;
|
|
CPU_ZERO(&cpus);
|
|
CPU_SET(cpu, &cpus);
|
|
sched_setaffinity(0, sizeof(cpus), &cpus);
|
|
// Don't assert sched_setaffinity's return value because it intermittently (?)
|
|
// fails with EINVAL on Linux x64 try runs.
|
|
#elif defined(XP_MACOSX)
|
|
// OS X does not provide APIs to pin threads to specific processors, but you
|
|
// can tag threads as belonging to the same "affinity set" and the OS will try
|
|
// to run them on the same processor. To run threads on different processors,
|
|
// tag them as belonging to different affinity sets. Tag 0, the default, means
|
|
// "no affinity" so let's pretend each CPU has its own tag `cpu+1`.
|
|
thread_affinity_policy_data_t policy;
|
|
policy.affinity_tag = cpu + 1;
|
|
MOZ_ALWAYS_TRUE(thread_policy_set(mach_thread_self(), THREAD_AFFINITY_POLICY,
|
|
&policy.affinity_tag, 1) == KERN_SUCCESS);
|
|
#elif defined(XP_WIN)
|
|
MOZ_ALWAYS_TRUE(SetThreadIdealProcessor(GetCurrentThread(), cpu) != -1);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
SetupCurrentThreadForChaosMode()
|
|
{
|
|
if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
|
|
return;
|
|
}
|
|
|
|
#ifdef XP_LINUX
|
|
// PR_SetThreadPriority doesn't really work since priorities >
|
|
// PR_PRIORITY_NORMAL can't be set by non-root users. Instead we'll just use
|
|
// setpriority(2) to set random 'nice values'. In regular Linux this is only
|
|
// a dynamic adjustment so it still doesn't really do what we want, but tools
|
|
// like 'rr' can be more aggressive about honoring these values.
|
|
// Some of these calls may fail due to trying to lower the priority
|
|
// (e.g. something may have already called setpriority() for this thread).
|
|
// This makes it hard to have non-main threads with higher priority than the
|
|
// main thread, but that's hard to fix. Tools like rr can choose to honor the
|
|
// requested values anyway.
|
|
// Use just 4 priorities so there's a reasonable chance of any two threads
|
|
// having equal priority.
|
|
setpriority(PRIO_PROCESS, 0, ChaosMode::randomUint32LessThan(4));
|
|
#else
|
|
// We should set the affinity here but NSPR doesn't provide a way to expose it.
|
|
uint32_t priority = ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST + 1);
|
|
PR_SetThreadPriority(PR_GetCurrentThread(), PRThreadPriority(priority));
|
|
#endif
|
|
|
|
// Force half the threads to CPU 0 so they compete for CPU
|
|
if (ChaosMode::randomUint32LessThan(2)) {
|
|
SetThreadAffinity(0);
|
|
}
|
|
}
|
|
|
|
/*static*/ void
|
|
nsThread::ThreadFunc(void* aArg)
|
|
{
|
|
using mozilla::ipc::BackgroundChild;
|
|
|
|
nsThread* self = static_cast<nsThread*>(aArg); // strong reference
|
|
self->mThread = PR_GetCurrentThread();
|
|
SetupCurrentThreadForChaosMode();
|
|
|
|
// Inform the ThreadManager
|
|
nsThreadManager::get()->RegisterCurrentThread(self);
|
|
|
|
mozilla::IOInterposer::RegisterCurrentThread();
|
|
|
|
// Wait for and process startup event
|
|
nsCOMPtr<nsIRunnable> event;
|
|
{
|
|
MutexAutoLock lock(self->mLock);
|
|
if (!self->mEvents->GetEvent(true, getter_AddRefs(event), lock)) {
|
|
NS_WARNING("failed waiting for thread startup event");
|
|
return;
|
|
}
|
|
}
|
|
event->Run(); // unblocks nsThread::Init
|
|
event = nullptr;
|
|
|
|
{
|
|
// Scope for MessageLoop.
|
|
nsAutoPtr<MessageLoop> loop(
|
|
new MessageLoop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD, self));
|
|
|
|
// Now, process incoming events...
|
|
loop->Run();
|
|
|
|
BackgroundChild::CloseForCurrentThread();
|
|
|
|
// NB: The main thread does not shut down here! It shuts down via
|
|
// nsThreadManager::Shutdown.
|
|
|
|
// Do NS_ProcessPendingEvents but with special handling to set
|
|
// mEventsAreDoomed atomically with the removal of the last event. The key
|
|
// invariant here is that we will never permit PutEvent to succeed if the
|
|
// event would be left in the queue after our final call to
|
|
// NS_ProcessPendingEvents. We also have to keep processing events as long
|
|
// as we have outstanding mRequestedShutdownContexts.
|
|
while (true) {
|
|
// Check and see if we're waiting on any threads.
|
|
self->WaitForAllAsynchronousShutdowns();
|
|
|
|
{
|
|
MutexAutoLock lock(self->mLock);
|
|
if (!self->mEvents->HasPendingEvent(lock)) {
|
|
// No events in the queue, so we will stop now. Don't let any more
|
|
// events be added, since they won't be processed. It is critical
|
|
// that no PutEvent can occur between testing that the event queue is
|
|
// empty and setting mEventsAreDoomed!
|
|
self->mEventsAreDoomed = true;
|
|
break;
|
|
}
|
|
}
|
|
NS_ProcessPendingEvents(self);
|
|
}
|
|
}
|
|
|
|
mozilla::IOInterposer::UnregisterCurrentThread();
|
|
|
|
// Inform the threadmanager that this thread is going away
|
|
nsThreadManager::get()->UnregisterCurrentThread(self);
|
|
|
|
// Dispatch shutdown ACK
|
|
MOZ_ASSERT(self->mShutdownContext->terminatingThread == self);
|
|
event = do_QueryObject(new nsThreadShutdownAckEvent(self->mShutdownContext));
|
|
self->mShutdownContext->joiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
|
|
|
// Release any observer of the thread here.
|
|
self->SetObserver(nullptr);
|
|
|
|
#ifdef MOZ_TASK_TRACER
|
|
FreeTraceInfo();
|
|
#endif
|
|
|
|
NS_RELEASE(self);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifdef MOZ_CRASHREPORTER
|
|
// Tell the crash reporter to save a memory report if our heuristics determine
|
|
// that an OOM failure is likely to occur soon.
|
|
static bool SaveMemoryReportNearOOM()
|
|
{
|
|
bool needMemoryReport = false;
|
|
|
|
#ifdef XP_WIN // XXX implement on other platforms as needed
|
|
const size_t LOWMEM_THRESHOLD_VIRTUAL = 200 * 1024 * 1024;
|
|
MEMORYSTATUSEX statex;
|
|
statex.dwLength = sizeof(statex);
|
|
if (GlobalMemoryStatusEx(&statex)) {
|
|
if (statex.ullAvailVirtual < LOWMEM_THRESHOLD_VIRTUAL) {
|
|
needMemoryReport = true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (needMemoryReport) {
|
|
nsCOMPtr<nsICrashReporter> cr =
|
|
do_GetService("@mozilla.org/toolkit/crash-reporter;1");
|
|
cr->SaveMemoryReport();
|
|
}
|
|
|
|
return needMemoryReport;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MOZ_CANARY
|
|
int sCanaryOutputFD = -1;
|
|
#endif
|
|
|
|
nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize)
|
|
: mLock("nsThread.mLock")
|
|
, mScriptObserver(nullptr)
|
|
, mEvents(&mEventsRoot)
|
|
, mEventsRoot(mLock)
|
|
, mPriority(PRIORITY_NORMAL)
|
|
, mThread(nullptr)
|
|
, mNestedEventLoopDepth(0)
|
|
, mStackSize(aStackSize)
|
|
, mShutdownContext(nullptr)
|
|
, mShutdownRequired(false)
|
|
, mEventsAreDoomed(false)
|
|
, mIsMainThread(aMainThread)
|
|
{
|
|
}
|
|
|
|
nsThread::~nsThread()
|
|
{
|
|
NS_ASSERTION(mRequestedShutdownContexts.IsEmpty(),
|
|
"shouldn't be waiting on other threads to shutdown");
|
|
#ifdef DEBUG
|
|
// We deliberately leak these so they can be tracked by the leak checker.
|
|
// If you're having nsThreadShutdownContext leaks, you can set:
|
|
// XPCOM_MEM_LOG_CLASSES=nsThreadShutdownContext
|
|
// during a test run and that will at least tell you what thread is
|
|
// requesting shutdown on another, which can be helpful for diagnosing
|
|
// the leak.
|
|
for (size_t i = 0; i < mRequestedShutdownContexts.Length(); ++i) {
|
|
Unused << mRequestedShutdownContexts[i].forget();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
nsresult
|
|
nsThread::Init()
|
|
{
|
|
// spawn thread and wait until it is fully setup
|
|
RefPtr<nsThreadStartupEvent> startup = new nsThreadStartupEvent();
|
|
|
|
NS_ADDREF_THIS();
|
|
|
|
mShutdownRequired = true;
|
|
|
|
// ThreadFunc is responsible for setting mThread
|
|
PRThread* thr = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
|
|
PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
|
|
PR_JOINABLE_THREAD, mStackSize);
|
|
if (!thr) {
|
|
NS_RELEASE_THIS();
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// ThreadFunc will wait for this event to be run before it tries to access
|
|
// mThread. By delaying insertion of this event into the queue, we ensure
|
|
// that mThread is set properly.
|
|
{
|
|
MutexAutoLock lock(mLock);
|
|
mEventsRoot.PutEvent(startup, lock); // retain a reference
|
|
}
|
|
|
|
// Wait for thread to call ThreadManager::SetupCurrentThread, which completes
|
|
// initialization of ThreadFunc.
|
|
startup->Wait();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsThread::InitCurrentThread()
|
|
{
|
|
mThread = PR_GetCurrentThread();
|
|
SetupCurrentThreadForChaosMode();
|
|
|
|
nsThreadManager::get()->RegisterCurrentThread(this);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsThread::PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget)
|
|
{
|
|
nsCOMPtr<nsIRunnable> event(aEvent);
|
|
return PutEvent(event.forget(), aTarget);
|
|
}
|
|
|
|
nsresult
|
|
nsThread::PutEvent(already_AddRefed<nsIRunnable>&& aEvent, nsNestedEventTarget* aTarget)
|
|
{
|
|
// We want to leak the reference when we fail to dispatch it, so that
|
|
// we won't release the event in a wrong thread.
|
|
LeakRefPtr<nsIRunnable> event(Move(aEvent));
|
|
nsCOMPtr<nsIThreadObserver> obs;
|
|
|
|
#ifdef MOZ_NUWA_PROCESS
|
|
// On debug build or when tests are enabled, assert that we are not about to
|
|
// create a deadlock in the Nuwa process.
|
|
NuwaAssertNotFrozen(PR_GetThreadID(mThread), PR_GetThreadName(mThread));
|
|
#endif
|
|
|
|
{
|
|
MutexAutoLock lock(mLock);
|
|
nsChainedEventQueue* queue = aTarget ? aTarget->mQueue : &mEventsRoot;
|
|
if (!queue || (queue == &mEventsRoot && mEventsAreDoomed)) {
|
|
NS_WARNING("An event was posted to a thread that will never run it (rejected)");
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
queue->PutEvent(event.take(), lock);
|
|
|
|
// Make sure to grab the observer before dropping the lock, otherwise the
|
|
// event that we just placed into the queue could run and eventually delete
|
|
// this nsThread before the calling thread is scheduled again. We would then
|
|
// crash while trying to access a dead nsThread.
|
|
obs = mObserver;
|
|
}
|
|
|
|
if (obs) {
|
|
obs->OnDispatchedEvent(this);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsThread::DispatchInternal(already_AddRefed<nsIRunnable>&& aEvent, uint32_t aFlags,
|
|
nsNestedEventTarget* aTarget)
|
|
{
|
|
// We want to leak the reference when we fail to dispatch it, so that
|
|
// we won't release the event in a wrong thread.
|
|
LeakRefPtr<nsIRunnable> event(Move(aEvent));
|
|
if (NS_WARN_IF(!event)) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
if (gXPCOMThreadsShutDown && MAIN_THREAD != mIsMainThread && !aTarget) {
|
|
NS_ASSERTION(false, "Failed Dispatch after xpcom-shutdown-threads");
|
|
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
|
|
}
|
|
|
|
#ifdef MOZ_TASK_TRACER
|
|
nsCOMPtr<nsIRunnable> tracedRunnable = CreateTracedRunnable(event.take());
|
|
(static_cast<TracedRunnable*>(tracedRunnable.get()))->DispatchTask();
|
|
// XXX tracedRunnable will always leaked when we fail to disptch.
|
|
event = tracedRunnable.forget();
|
|
#endif
|
|
|
|
if (aFlags & DISPATCH_SYNC) {
|
|
nsThread* thread = nsThreadManager::get()->GetCurrentThread();
|
|
if (NS_WARN_IF(!thread)) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
// XXX we should be able to do something better here... we should
|
|
// be able to monitor the slot occupied by this event and use
|
|
// that to tell us when the event has been processed.
|
|
|
|
RefPtr<nsThreadSyncDispatch> wrapper =
|
|
new nsThreadSyncDispatch(thread, event.take());
|
|
nsresult rv = PutEvent(wrapper, aTarget); // hold a ref
|
|
// Don't wait for the event to finish if we didn't dispatch it...
|
|
if (NS_FAILED(rv)) {
|
|
// PutEvent leaked the wrapper runnable object on failure, so we
|
|
// explicitly release this object once for that. Note that this
|
|
// object will be released again soon because it exits the scope.
|
|
wrapper.get()->Release();
|
|
return rv;
|
|
}
|
|
|
|
// Allows waiting; ensure no locks are held that would deadlock us!
|
|
while (wrapper->IsPending()) {
|
|
NS_ProcessNextEvent(thread, true);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
|
|
return PutEvent(event.take(), aTarget);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsIEventTarget
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
|
|
{
|
|
nsCOMPtr<nsIRunnable> event(aEvent);
|
|
return Dispatch(event.forget(), aFlags);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::Dispatch(already_AddRefed<nsIRunnable>&& aEvent, uint32_t aFlags)
|
|
{
|
|
LOG(("THRD(%p) Dispatch [%p %x]\n", this, /* XXX aEvent */nullptr, aFlags));
|
|
|
|
return DispatchInternal(Move(aEvent), aFlags, nullptr);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::IsOnCurrentThread(bool* aResult)
|
|
{
|
|
*aResult = (PR_GetCurrentThread() == mThread);
|
|
return NS_OK;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsIThread
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::GetPRThread(PRThread** aResult)
|
|
{
|
|
*aResult = mThread;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::AsyncShutdown()
|
|
{
|
|
LOG(("THRD(%p) async shutdown\n", this));
|
|
|
|
// XXX If we make this warn, then we hit that warning at xpcom shutdown while
|
|
// shutting down a thread in a thread pool. That happens b/c the thread
|
|
// in the thread pool is already shutdown by the thread manager.
|
|
if (!mThread) {
|
|
return NS_OK;
|
|
}
|
|
|
|
return !!ShutdownInternal(/* aSync = */ false) ? NS_OK : NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
nsThreadShutdownContext*
|
|
nsThread::ShutdownInternal(bool aSync)
|
|
{
|
|
MOZ_ASSERT(mThread);
|
|
|
|
if (NS_WARN_IF(mThread == PR_GetCurrentThread())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Prevent multiple calls to this method
|
|
{
|
|
MutexAutoLock lock(mLock);
|
|
if (!mShutdownRequired) {
|
|
return nullptr;
|
|
}
|
|
mShutdownRequired = false;
|
|
}
|
|
|
|
nsThread* currentThread = nsThreadManager::get()->GetCurrentThread();
|
|
MOZ_ASSERT(currentThread);
|
|
|
|
nsAutoPtr<nsThreadShutdownContext>& context =
|
|
*currentThread->mRequestedShutdownContexts.AppendElement();
|
|
context = new nsThreadShutdownContext();
|
|
|
|
context->terminatingThread = this;
|
|
context->joiningThread = currentThread;
|
|
context->awaitingShutdownAck = aSync;
|
|
|
|
// Set mShutdownContext and wake up the thread in case it is waiting for
|
|
// events to process.
|
|
nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, context);
|
|
// XXXroc What if posting the event fails due to OOM?
|
|
PutEvent(event.forget(), nullptr);
|
|
|
|
// We could still end up with other events being added after the shutdown
|
|
// task, but that's okay because we process pending events in ThreadFunc
|
|
// after setting mShutdownContext just before exiting.
|
|
return context;
|
|
}
|
|
|
|
void
|
|
nsThread::ShutdownComplete(nsThreadShutdownContext* aContext)
|
|
{
|
|
MOZ_ASSERT(mThread);
|
|
MOZ_ASSERT(aContext->terminatingThread == this);
|
|
|
|
if (aContext->awaitingShutdownAck) {
|
|
// We're in a synchronous shutdown, so tell whatever is up the stack that
|
|
// we're done and unwind the stack so it can call us again.
|
|
aContext->awaitingShutdownAck = false;
|
|
return;
|
|
}
|
|
|
|
// Now, it should be safe to join without fear of dead-locking.
|
|
|
|
PR_JoinThread(mThread);
|
|
mThread = nullptr;
|
|
|
|
// We hold strong references to our event observers, and once the thread is
|
|
// shut down the observers can't easily unregister themselves. Do it here
|
|
// to avoid leaking.
|
|
ClearObservers();
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
MutexAutoLock lock(mLock);
|
|
MOZ_ASSERT(!mObserver, "Should have been cleared at shutdown!");
|
|
}
|
|
#endif
|
|
|
|
// Delete aContext.
|
|
MOZ_ALWAYS_TRUE(
|
|
aContext->joiningThread->mRequestedShutdownContexts.RemoveElement(aContext));
|
|
}
|
|
|
|
void
|
|
nsThread::WaitForAllAsynchronousShutdowns()
|
|
{
|
|
while (mRequestedShutdownContexts.Length()) {
|
|
NS_ProcessNextEvent(this, true);
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::Shutdown()
|
|
{
|
|
LOG(("THRD(%p) sync shutdown\n", this));
|
|
|
|
// XXX If we make this warn, then we hit that warning at xpcom shutdown while
|
|
// shutting down a thread in a thread pool. That happens b/c the thread
|
|
// in the thread pool is already shutdown by the thread manager.
|
|
if (!mThread) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsThreadShutdownContext* context = ShutdownInternal(/* aSync = */ true);
|
|
NS_ENSURE_TRUE(context, NS_ERROR_UNEXPECTED);
|
|
|
|
// Process events on the current thread until we receive a shutdown ACK.
|
|
// Allows waiting; ensure no locks are held that would deadlock us!
|
|
while (context->awaitingShutdownAck) {
|
|
NS_ProcessNextEvent(context->joiningThread, true);
|
|
}
|
|
|
|
ShutdownComplete(context);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::HasPendingEvents(bool* aResult)
|
|
{
|
|
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
|
|
return NS_ERROR_NOT_SAME_THREAD;
|
|
}
|
|
|
|
{
|
|
MutexAutoLock lock(mLock);
|
|
*aResult = mEvents->HasPendingEvent(lock);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
#ifdef MOZ_CANARY
|
|
void canary_alarm_handler(int signum);
|
|
|
|
class Canary
|
|
{
|
|
//XXX ToDo: support nested loops
|
|
public:
|
|
Canary()
|
|
{
|
|
if (sCanaryOutputFD > 0 && EventLatencyIsImportant()) {
|
|
signal(SIGALRM, canary_alarm_handler);
|
|
ualarm(15000, 0);
|
|
}
|
|
}
|
|
|
|
~Canary()
|
|
{
|
|
if (sCanaryOutputFD != 0 && EventLatencyIsImportant()) {
|
|
ualarm(0, 0);
|
|
}
|
|
}
|
|
|
|
static bool EventLatencyIsImportant()
|
|
{
|
|
return NS_IsMainThread() && XRE_IsParentProcess();
|
|
}
|
|
};
|
|
|
|
void canary_alarm_handler(int signum)
|
|
{
|
|
void* array[30];
|
|
const char msg[29] = "event took too long to run:\n";
|
|
// use write to be safe in the signal handler
|
|
write(sCanaryOutputFD, msg, sizeof(msg));
|
|
backtrace_symbols_fd(array, backtrace(array, 30), sCanaryOutputFD);
|
|
}
|
|
|
|
#endif
|
|
|
|
#define NOTIFY_EVENT_OBSERVERS(func_, params_) \
|
|
PR_BEGIN_MACRO \
|
|
if (!mEventObservers.IsEmpty()) { \
|
|
nsAutoTObserverArray<nsCOMPtr<nsIThreadObserver>, 2>::ForwardIterator \
|
|
iter_(mEventObservers); \
|
|
nsCOMPtr<nsIThreadObserver> obs_; \
|
|
while (iter_.HasMore()) { \
|
|
obs_ = iter_.GetNext(); \
|
|
obs_ -> func_ params_ ; \
|
|
} \
|
|
} \
|
|
PR_END_MACRO
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::ProcessNextEvent(bool aMayWait, bool* aResult)
|
|
{
|
|
LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, aMayWait,
|
|
mNestedEventLoopDepth));
|
|
|
|
// If we're on the main thread, we shouldn't be dispatching CPOWs.
|
|
if (mIsMainThread == MAIN_THREAD) {
|
|
ipc::CancelCPOWs();
|
|
}
|
|
|
|
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
|
|
return NS_ERROR_NOT_SAME_THREAD;
|
|
}
|
|
|
|
// The toplevel event loop normally blocks waiting for the next event, but
|
|
// if we're trying to shut this thread down, we must exit the event loop when
|
|
// the event queue is empty.
|
|
// This only applys to the toplevel event loop! Nested event loops (e.g.
|
|
// during sync dispatch) are waiting for some state change and must be able
|
|
// to block even if something has requested shutdown of the thread. Otherwise
|
|
// we'll just busywait as we endlessly look for an event, fail to find one,
|
|
// and repeat the nested event loop since its state change hasn't happened yet.
|
|
bool reallyWait = aMayWait && (mNestedEventLoopDepth > 0 || !ShuttingDown());
|
|
|
|
if (MAIN_THREAD == mIsMainThread && reallyWait) {
|
|
HangMonitor::Suspend();
|
|
}
|
|
|
|
// Fire a memory pressure notification, if we're the main thread and one is
|
|
// pending.
|
|
if (MAIN_THREAD == mIsMainThread && !ShuttingDown()) {
|
|
MemoryPressureState mpPending = NS_GetPendingMemoryPressure();
|
|
if (mpPending != MemPressure_None) {
|
|
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
|
|
|
// Use no-forward to prevent the notifications from being transferred to
|
|
// the children of this process.
|
|
NS_NAMED_LITERAL_STRING(lowMem, "low-memory-no-forward");
|
|
NS_NAMED_LITERAL_STRING(lowMemOngoing, "low-memory-ongoing-no-forward");
|
|
|
|
if (os) {
|
|
os->NotifyObservers(nullptr, "memory-pressure",
|
|
mpPending == MemPressure_New ? lowMem.get() :
|
|
lowMemOngoing.get());
|
|
} else {
|
|
NS_WARNING("Can't get observer service!");
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef MOZ_CRASHREPORTER
|
|
if (MAIN_THREAD == mIsMainThread && !ShuttingDown()) {
|
|
// Keep an eye on memory usage (cheap, ~7ms) somewhat frequently,
|
|
// but save memory reports (expensive, ~75ms) less frequently.
|
|
const size_t LOW_MEMORY_CHECK_SECONDS = 30;
|
|
const size_t LOW_MEMORY_SAVE_SECONDS = 3 * 60;
|
|
|
|
static TimeStamp nextCheck = TimeStamp::NowLoRes()
|
|
+ TimeDuration::FromSeconds(LOW_MEMORY_CHECK_SECONDS);
|
|
|
|
TimeStamp now = TimeStamp::NowLoRes();
|
|
if (now >= nextCheck) {
|
|
if (SaveMemoryReportNearOOM()) {
|
|
nextCheck = now + TimeDuration::FromSeconds(LOW_MEMORY_SAVE_SECONDS);
|
|
} else {
|
|
nextCheck = now + TimeDuration::FromSeconds(LOW_MEMORY_CHECK_SECONDS);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
++mNestedEventLoopDepth;
|
|
|
|
bool callScriptObserver = !!mScriptObserver;
|
|
if (callScriptObserver) {
|
|
mScriptObserver->BeforeProcessTask(reallyWait);
|
|
}
|
|
|
|
nsCOMPtr<nsIThreadObserver> obs = mObserver;
|
|
if (obs) {
|
|
obs->OnProcessNextEvent(this, reallyWait);
|
|
}
|
|
|
|
NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent, (this, reallyWait));
|
|
|
|
#ifdef MOZ_CANARY
|
|
Canary canary;
|
|
#endif
|
|
nsresult rv = NS_OK;
|
|
|
|
{
|
|
// Scope for |event| to make sure that its destructor fires while
|
|
// mNestedEventLoopDepth has been incremented, since that destructor can
|
|
// also do work.
|
|
|
|
// If we are shutting down, then do not wait for new events.
|
|
nsCOMPtr<nsIRunnable> event;
|
|
{
|
|
MutexAutoLock lock(mLock);
|
|
mEvents->GetEvent(reallyWait, getter_AddRefs(event), lock);
|
|
}
|
|
|
|
*aResult = (event.get() != nullptr);
|
|
|
|
if (event) {
|
|
LOG(("THRD(%p) running [%p]\n", this, event.get()));
|
|
if (MAIN_THREAD == mIsMainThread) {
|
|
HangMonitor::NotifyActivity();
|
|
}
|
|
event->Run();
|
|
} else if (aMayWait) {
|
|
MOZ_ASSERT(ShuttingDown(),
|
|
"This should only happen when shutting down");
|
|
rv = NS_ERROR_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent, (this, *aResult));
|
|
|
|
if (obs) {
|
|
obs->AfterProcessNextEvent(this, *aResult);
|
|
}
|
|
|
|
if (callScriptObserver && mScriptObserver) {
|
|
mScriptObserver->AfterProcessTask(mNestedEventLoopDepth);
|
|
}
|
|
|
|
--mNestedEventLoopDepth;
|
|
|
|
return rv;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsISupportsPriority
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::GetPriority(int32_t* aPriority)
|
|
{
|
|
*aPriority = mPriority;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::SetPriority(int32_t aPriority)
|
|
{
|
|
if (NS_WARN_IF(!mThread)) {
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
// NSPR defines the following four thread priorities:
|
|
// PR_PRIORITY_LOW
|
|
// PR_PRIORITY_NORMAL
|
|
// PR_PRIORITY_HIGH
|
|
// PR_PRIORITY_URGENT
|
|
// We map the priority values defined on nsISupportsPriority to these values.
|
|
|
|
mPriority = aPriority;
|
|
|
|
PRThreadPriority pri;
|
|
if (mPriority <= PRIORITY_HIGHEST) {
|
|
pri = PR_PRIORITY_URGENT;
|
|
} else if (mPriority < PRIORITY_NORMAL) {
|
|
pri = PR_PRIORITY_HIGH;
|
|
} else if (mPriority > PRIORITY_NORMAL) {
|
|
pri = PR_PRIORITY_LOW;
|
|
} else {
|
|
pri = PR_PRIORITY_NORMAL;
|
|
}
|
|
// If chaos mode is active, retain the randomly chosen priority
|
|
if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
|
|
PR_SetThreadPriority(mThread, pri);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::AdjustPriority(int32_t aDelta)
|
|
{
|
|
return SetPriority(mPriority + aDelta);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsIThreadInternal
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::GetObserver(nsIThreadObserver** aObs)
|
|
{
|
|
MutexAutoLock lock(mLock);
|
|
NS_IF_ADDREF(*aObs = mObserver);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::SetObserver(nsIThreadObserver* aObs)
|
|
{
|
|
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
|
|
return NS_ERROR_NOT_SAME_THREAD;
|
|
}
|
|
|
|
MutexAutoLock lock(mLock);
|
|
mObserver = aObs;
|
|
return NS_OK;
|
|
}
|
|
|
|
uint32_t
|
|
nsThread::RecursionDepth() const
|
|
{
|
|
MOZ_ASSERT(PR_GetCurrentThread() == mThread);
|
|
return mNestedEventLoopDepth;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::AddObserver(nsIThreadObserver* aObserver)
|
|
{
|
|
if (NS_WARN_IF(!aObserver)) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
|
|
return NS_ERROR_NOT_SAME_THREAD;
|
|
}
|
|
|
|
NS_WARN_IF_FALSE(!mEventObservers.Contains(aObserver),
|
|
"Adding an observer twice!");
|
|
|
|
if (!mEventObservers.AppendElement(aObserver)) {
|
|
NS_WARNING("Out of memory!");
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::RemoveObserver(nsIThreadObserver* aObserver)
|
|
{
|
|
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
|
|
return NS_ERROR_NOT_SAME_THREAD;
|
|
}
|
|
|
|
if (aObserver && !mEventObservers.RemoveElement(aObserver)) {
|
|
NS_WARNING("Removing an observer that was never added!");
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::PushEventQueue(nsIEventTarget** aResult)
|
|
{
|
|
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
|
|
return NS_ERROR_NOT_SAME_THREAD;
|
|
}
|
|
|
|
nsChainedEventQueue* queue = new nsChainedEventQueue(mLock);
|
|
queue->mEventTarget = new nsNestedEventTarget(this, queue);
|
|
|
|
{
|
|
MutexAutoLock lock(mLock);
|
|
queue->mNext = mEvents;
|
|
mEvents = queue;
|
|
}
|
|
|
|
NS_ADDREF(*aResult = queue->mEventTarget);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::PopEventQueue(nsIEventTarget* aInnermostTarget)
|
|
{
|
|
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
|
|
return NS_ERROR_NOT_SAME_THREAD;
|
|
}
|
|
|
|
if (NS_WARN_IF(!aInnermostTarget)) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
// Don't delete or release anything while holding the lock.
|
|
nsAutoPtr<nsChainedEventQueue> queue;
|
|
RefPtr<nsNestedEventTarget> target;
|
|
|
|
{
|
|
MutexAutoLock lock(mLock);
|
|
|
|
// Make sure we're popping the innermost event target.
|
|
if (NS_WARN_IF(mEvents->mEventTarget != aInnermostTarget)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
MOZ_ASSERT(mEvents != &mEventsRoot);
|
|
|
|
queue = mEvents;
|
|
mEvents = mEvents->mNext;
|
|
|
|
nsCOMPtr<nsIRunnable> event;
|
|
while (queue->GetEvent(false, getter_AddRefs(event), lock)) {
|
|
mEvents->PutEvent(event.forget(), lock);
|
|
}
|
|
|
|
// Don't let the event target post any more events.
|
|
queue->mEventTarget.swap(target);
|
|
target->mQueue = nullptr;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsThread::SetScriptObserver(mozilla::CycleCollectedJSRuntime* aScriptObserver)
|
|
{
|
|
if (!aScriptObserver) {
|
|
mScriptObserver = nullptr;
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(!mScriptObserver);
|
|
mScriptObserver = aScriptObserver;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
NS_IMPL_ISUPPORTS(nsThread::nsNestedEventTarget, nsIEventTarget)
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::nsNestedEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
|
|
{
|
|
nsCOMPtr<nsIRunnable> event(aEvent);
|
|
return Dispatch(event.forget(), aFlags);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::nsNestedEventTarget::Dispatch(already_AddRefed<nsIRunnable>&& aEvent, uint32_t aFlags)
|
|
{
|
|
LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread.get(), /*XXX aEvent*/ nullptr,
|
|
aFlags, this));
|
|
|
|
return mThread->DispatchInternal(Move(aEvent), aFlags, this);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsThread::nsNestedEventTarget::IsOnCurrentThread(bool* aResult)
|
|
{
|
|
return mThread->IsOnCurrentThread(aResult);
|
|
}
|