зеркало из https://github.com/mozilla/pjs.git
Bug 676376 - 'prevent multi-threaded JSRuntime access in new web worker memory reporters'. r=luke+sicking.
This commit is contained in:
Родитель
e857a8f4e1
Коммит
378f7469eb
|
@ -291,16 +291,16 @@ CreateJSContextForWorker(WorkerPrivate* aWorkerPrivate)
|
||||||
|
|
||||||
class WorkerMemoryReporter : public nsIMemoryMultiReporter
|
class WorkerMemoryReporter : public nsIMemoryMultiReporter
|
||||||
{
|
{
|
||||||
JSRuntime* mRuntime;
|
WorkerPrivate* mWorkerPrivate;
|
||||||
nsCString mPathPrefix;
|
nsCString mPathPrefix;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NS_DECL_ISUPPORTS
|
NS_DECL_ISUPPORTS
|
||||||
|
|
||||||
WorkerMemoryReporter(WorkerPrivate* aWorkerPrivate, JSRuntime* aRuntime)
|
WorkerMemoryReporter(WorkerPrivate* aWorkerPrivate)
|
||||||
: mRuntime(aRuntime)
|
: mWorkerPrivate(aWorkerPrivate)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||||
|
|
||||||
nsCString escapedDomain(aWorkerPrivate->Domain());
|
nsCString escapedDomain(aWorkerPrivate->Domain());
|
||||||
escapedDomain.ReplaceChar('/', '\\');
|
escapedDomain.ReplaceChar('/', '\\');
|
||||||
|
@ -331,10 +331,8 @@ public:
|
||||||
{
|
{
|
||||||
AssertIsOnMainThread();
|
AssertIsOnMainThread();
|
||||||
|
|
||||||
JS_TriggerAllOperationCallbacks(mRuntime);
|
|
||||||
|
|
||||||
IterateData data;
|
IterateData data;
|
||||||
if (!CollectCompartmentStatsForRuntime(mRuntime, &data)) {
|
if (!mWorkerPrivate->BlockAndCollectRuntimeStats(&data)) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,16 +370,17 @@ public:
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSRuntime* rt = JS_GetRuntime(cx);
|
|
||||||
|
|
||||||
nsRefPtr<WorkerMemoryReporter> reporter =
|
nsRefPtr<WorkerMemoryReporter> reporter =
|
||||||
new WorkerMemoryReporter(workerPrivate, rt);
|
new WorkerMemoryReporter(workerPrivate);
|
||||||
if (NS_FAILED(NS_RegisterMemoryMultiReporter(reporter))) {
|
if (NS_FAILED(NS_RegisterMemoryMultiReporter(reporter))) {
|
||||||
NS_WARNING("Failed to register memory reporter!");
|
NS_WARNING("Failed to register memory reporter!");
|
||||||
reporter = nsnull;
|
reporter = nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
JSAutoRequest ar(cx);
|
||||||
workerPrivate->DoRunLoop(cx);
|
workerPrivate->DoRunLoop(cx);
|
||||||
|
}
|
||||||
|
|
||||||
if (reporter) {
|
if (reporter) {
|
||||||
if (NS_FAILED(NS_UnregisterMemoryMultiReporter(reporter))) {
|
if (NS_FAILED(NS_UnregisterMemoryMultiReporter(reporter))) {
|
||||||
|
@ -390,6 +389,8 @@ public:
|
||||||
reporter = nsnull;
|
reporter = nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSRuntime* rt = JS_GetRuntime(cx);
|
||||||
|
|
||||||
// XXX Bug 666963 - CTypes can create another JSContext for use with
|
// XXX Bug 666963 - CTypes can create another JSContext for use with
|
||||||
// closures, and then it holds that context in a reserved slot on the CType
|
// closures, and then it holds that context in a reserved slot on the CType
|
||||||
// prototype object. We have to destroy that context before we can destroy
|
// prototype object. We have to destroy that context before we can destroy
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
#include "nsJSUtils.h"
|
#include "nsJSUtils.h"
|
||||||
#include "nsNetUtil.h"
|
#include "nsNetUtil.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
|
#include "xpcpublic.h"
|
||||||
|
|
||||||
#include "Events.h"
|
#include "Events.h"
|
||||||
#include "Exceptions.h"
|
#include "Exceptions.h"
|
||||||
|
@ -83,6 +84,7 @@ using mozilla::MutexAutoLock;
|
||||||
using mozilla::TimeDuration;
|
using mozilla::TimeDuration;
|
||||||
using mozilla::TimeStamp;
|
using mozilla::TimeStamp;
|
||||||
using mozilla::dom::workers::exceptions::ThrowDOMExceptionForCode;
|
using mozilla::dom::workers::exceptions::ThrowDOMExceptionForCode;
|
||||||
|
using mozilla::xpconnect::memory::IterateData;
|
||||||
|
|
||||||
USING_WORKERS_NAMESPACE
|
USING_WORKERS_NAMESPACE
|
||||||
|
|
||||||
|
@ -1070,6 +1072,60 @@ public:
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class CollectRuntimeStatsRunnable : public WorkerControlRunnable
|
||||||
|
{
|
||||||
|
typedef mozilla::Mutex Mutex;
|
||||||
|
typedef mozilla::CondVar CondVar;
|
||||||
|
|
||||||
|
Mutex* mMutex;
|
||||||
|
CondVar* mCondVar;
|
||||||
|
volatile bool* mDoneFlag;
|
||||||
|
IterateData* mData;
|
||||||
|
volatile bool* mSucceeded;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CollectRuntimeStatsRunnable(WorkerPrivate* aWorkerPrivate, Mutex* aMutex,
|
||||||
|
CondVar* aCondVar, volatile bool* aDoneFlag,
|
||||||
|
IterateData* aData, volatile bool* aSucceeded)
|
||||||
|
: WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
|
||||||
|
mMutex(aMutex), mCondVar(aCondVar), mDoneFlag(aDoneFlag), mData(aData),
|
||||||
|
mSucceeded(aSucceeded)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool
|
||||||
|
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||||
|
{
|
||||||
|
AssertIsOnMainThread();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||||
|
bool aDispatchResult)
|
||||||
|
{
|
||||||
|
AssertIsOnMainThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||||
|
{
|
||||||
|
JSAutoSuspendRequest asr(aCx);
|
||||||
|
|
||||||
|
*mSucceeded = CollectCompartmentStatsForRuntime(JS_GetRuntime(aCx), mData);
|
||||||
|
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(*mMutex);
|
||||||
|
|
||||||
|
NS_ASSERTION(!*mDoneFlag, "Should be false!");
|
||||||
|
|
||||||
|
*mDoneFlag = true;
|
||||||
|
mCondVar->Notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} /* anonymous namespace */
|
} /* anonymous namespace */
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
@ -2042,8 +2098,6 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
|
||||||
{
|
{
|
||||||
MutexAutoUnlock unlock(mMutex);
|
MutexAutoUnlock unlock(mMutex);
|
||||||
|
|
||||||
JSAutoRequest ar(aCx);
|
|
||||||
|
|
||||||
#ifdef EXTRA_GC
|
#ifdef EXTRA_GC
|
||||||
// Find GC bugs...
|
// Find GC bugs...
|
||||||
JS_GC(aCx);
|
JS_GC(aCx);
|
||||||
|
@ -2056,8 +2110,6 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
|
||||||
currentStatus = mStatus;
|
currentStatus = mStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSAutoRequest ar(aCx);
|
|
||||||
|
|
||||||
#ifdef EXTRA_GC
|
#ifdef EXTRA_GC
|
||||||
// Find GC bugs...
|
// Find GC bugs...
|
||||||
JS_GC(aCx);
|
JS_GC(aCx);
|
||||||
|
@ -2097,8 +2149,6 @@ WorkerPrivate::OperationCallback(JSContext* aCx)
|
||||||
{
|
{
|
||||||
AssertIsOnWorkerThread();
|
AssertIsOnWorkerThread();
|
||||||
|
|
||||||
JS_YieldRequest(aCx);
|
|
||||||
|
|
||||||
bool mayContinue = true;
|
bool mayContinue = true;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
@ -2133,7 +2183,6 @@ WorkerPrivate::OperationCallback(JSContext* aCx)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSAutoSuspendRequest asr(aCx);
|
|
||||||
mCondVar.Wait(PR_MillisecondsToInterval(RemainingRunTimeMS()));
|
mCondVar.Wait(PR_MillisecondsToInterval(RemainingRunTimeMS()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2184,6 +2233,36 @@ WorkerPrivate::ScheduleDeletion(bool aWasPending)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
WorkerPrivate::BlockAndCollectRuntimeStats(IterateData* aData)
|
||||||
|
{
|
||||||
|
AssertIsOnMainThread();
|
||||||
|
mMutex.AssertNotCurrentThreadOwns();
|
||||||
|
NS_ASSERTION(aData, "Null data!");
|
||||||
|
|
||||||
|
mozilla::Mutex mutex("BlockAndCollectRuntimeStats mutex");
|
||||||
|
mozilla::CondVar condvar(mutex, "BlockAndCollectRuntimeStats condvar");
|
||||||
|
volatile bool doneFlag = false;
|
||||||
|
volatile bool succeeded = false;
|
||||||
|
|
||||||
|
nsRefPtr<CollectRuntimeStatsRunnable> runnable =
|
||||||
|
new CollectRuntimeStatsRunnable(this, &mutex, &condvar, &doneFlag, aData,
|
||||||
|
&succeeded);
|
||||||
|
if (!runnable->Dispatch(nsnull)) {
|
||||||
|
NS_WARNING("Failed to dispatch runnable!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(mutex);
|
||||||
|
while (!doneFlag) {
|
||||||
|
condvar.Wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return succeeded;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
WorkerPrivate::Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue)
|
WorkerPrivate::Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue)
|
||||||
{
|
{
|
||||||
|
@ -2488,7 +2567,6 @@ WorkerPrivate::RunSyncLoop(JSContext* aCx, PRUint32 aSyncLoopKey)
|
||||||
MutexAutoLock lock(mMutex);
|
MutexAutoLock lock(mMutex);
|
||||||
|
|
||||||
while (!mControlQueue.Pop(event) && !syncQueue->mQueue.Pop(event)) {
|
while (!mControlQueue.Pop(event) && !syncQueue->mQueue.Pop(event)) {
|
||||||
JSAutoSuspendRequest asr(aCx);
|
|
||||||
mCondVar.Wait();
|
mCondVar.Wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,16 @@ class nsIURI;
|
||||||
class nsPIDOMWindow;
|
class nsPIDOMWindow;
|
||||||
class nsITimer;
|
class nsITimer;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace xpconnect {
|
||||||
|
namespace memory {
|
||||||
|
|
||||||
|
struct IterateData;
|
||||||
|
|
||||||
|
} // namespace memory
|
||||||
|
} // namespace xpconnect
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
BEGIN_WORKERS_NAMESPACE
|
BEGIN_WORKERS_NAMESPACE
|
||||||
|
|
||||||
class WorkerPrivate;
|
class WorkerPrivate;
|
||||||
|
@ -640,6 +650,9 @@ public:
|
||||||
void
|
void
|
||||||
ScheduleDeletion(bool aWasPending);
|
ScheduleDeletion(bool aWasPending);
|
||||||
|
|
||||||
|
bool
|
||||||
|
BlockAndCollectRuntimeStats(mozilla::xpconnect::memory::IterateData* aData);
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
#ifdef JS_GC_ZEAL
|
||||||
void
|
void
|
||||||
UpdateGCZealInternal(JSContext* aCx, PRUint8 aGCZeal);
|
UpdateGCZealInternal(JSContext* aCx, PRUint8 aGCZeal);
|
||||||
|
|
|
@ -1323,8 +1323,8 @@ CompartmentCallback(JSContext *cx, void *vdata, JSCompartment *compartment)
|
||||||
// Append a new CompartmentStats to the vector.
|
// Append a new CompartmentStats to the vector.
|
||||||
IterateData *data = static_cast<IterateData *>(vdata);
|
IterateData *data = static_cast<IterateData *>(vdata);
|
||||||
CompartmentStats compartmentStats(cx, compartment);
|
CompartmentStats compartmentStats(cx, compartment);
|
||||||
data->compartmentStatsVector.infallibleAppend(compartmentStats);
|
CompartmentStats *curr =
|
||||||
CompartmentStats *curr = data->compartmentStatsVector.end() - 1;
|
data->compartmentStatsVector.AppendElement(compartmentStats);
|
||||||
data->currCompartmentStats = curr;
|
data->currCompartmentStats = curr;
|
||||||
|
|
||||||
// Get the compartment-level numbers.
|
// Get the compartment-level numbers.
|
||||||
|
@ -1610,8 +1610,7 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data)
|
||||||
{
|
{
|
||||||
JSAutoRequest ar(cx);
|
JSAutoRequest ar(cx);
|
||||||
|
|
||||||
if (!data->compartmentStatsVector.reserve(rt->compartments.length()))
|
data->compartmentStatsVector.SetCapacity(rt->compartments.length());
|
||||||
return false;
|
|
||||||
|
|
||||||
data->gcHeapChunkCleanUnused =
|
data->gcHeapChunkCleanUnused =
|
||||||
PRInt64(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) *
|
PRInt64(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) *
|
||||||
|
@ -1635,17 +1634,19 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data)
|
||||||
data->gcHeapChunkCleanUnused;
|
data->gcHeapChunkCleanUnused;
|
||||||
data->gcHeapArenaUnused = 0;
|
data->gcHeapArenaUnused = 0;
|
||||||
|
|
||||||
for(CompartmentStats *stats = data->compartmentStatsVector.begin();
|
for(PRUint32 index = 0;
|
||||||
stats != data->compartmentStatsVector.end();
|
index < data->compartmentStatsVector.Length();
|
||||||
++stats)
|
index++)
|
||||||
{
|
{
|
||||||
data->gcHeapChunkDirtyUnused -=
|
CompartmentStats &stats = data->compartmentStatsVector[index];
|
||||||
stats->gcHeapArenaHeaders + stats->gcHeapArenaPadding +
|
|
||||||
stats->gcHeapArenaUnused +
|
|
||||||
stats->gcHeapObjects + stats->gcHeapStrings +
|
|
||||||
stats->gcHeapShapes + stats->gcHeapXml;
|
|
||||||
|
|
||||||
data->gcHeapArenaUnused += stats->gcHeapArenaUnused;
|
data->gcHeapChunkDirtyUnused -=
|
||||||
|
stats.gcHeapArenaHeaders + stats.gcHeapArenaPadding +
|
||||||
|
stats.gcHeapArenaUnused +
|
||||||
|
stats.gcHeapObjects + stats.gcHeapStrings +
|
||||||
|
stats.gcHeapShapes + stats.gcHeapXml;
|
||||||
|
|
||||||
|
data->gcHeapArenaUnused += stats.gcHeapArenaUnused;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t numDirtyChunks = (data->gcHeapChunkTotal -
|
size_t numDirtyChunks = (data->gcHeapChunkTotal -
|
||||||
|
@ -1808,11 +1809,12 @@ ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix,
|
||||||
nsIMemoryMultiReporterCallback *callback,
|
nsIMemoryMultiReporterCallback *callback,
|
||||||
nsISupports *closure)
|
nsISupports *closure)
|
||||||
{
|
{
|
||||||
for(const CompartmentStats *stats = data.compartmentStatsVector.begin();
|
for(PRUint32 index = 0;
|
||||||
stats != data.compartmentStatsVector.end();
|
index < data.compartmentStatsVector.Length();
|
||||||
++stats)
|
index++)
|
||||||
{
|
{
|
||||||
ReportCompartmentStats(*stats, pathPrefix, callback, closure);
|
ReportCompartmentStats(data.compartmentStatsVector[index], pathPrefix,
|
||||||
|
callback, closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("stack"),
|
ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("stack"),
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
#include "nsIPrincipal.h"
|
#include "nsIPrincipal.h"
|
||||||
#include "nsWrapperCache.h"
|
#include "nsWrapperCache.h"
|
||||||
#include "nsStringGlue.h"
|
#include "nsStringGlue.h"
|
||||||
|
#include "nsTArray.h"
|
||||||
|
|
||||||
class nsIPrincipal;
|
class nsIPrincipal;
|
||||||
|
|
||||||
|
@ -242,7 +243,7 @@ struct IterateData
|
||||||
PRInt64 gcHeapChunkAdmin;
|
PRInt64 gcHeapChunkAdmin;
|
||||||
PRInt64 gcHeapUnusedPercentage;
|
PRInt64 gcHeapUnusedPercentage;
|
||||||
|
|
||||||
js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> compartmentStatsVector;
|
nsTArray<CompartmentStats> compartmentStatsVector;
|
||||||
CompartmentStats *currCompartmentStats;
|
CompartmentStats *currCompartmentStats;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче