mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 04:41:11 +00:00
Bug 1910021 - Using Mutex to protect ConsoleCallData. r=dom-worker-reviewers,smaug
Disabled dom/console/tests/xpcshell/test_failing_console_listener for tsan build. Since the failing console mechanism causes too many additional deadlock checking then time out the test. Differential Revision: https://phabricator.services.mozilla.com/D224922
This commit is contained in:
parent
2e7917e4d3
commit
bfa9317a53
@ -33,6 +33,7 @@
|
|||||||
#include "mozilla/HoldDropJSObjects.h"
|
#include "mozilla/HoldDropJSObjects.h"
|
||||||
#include "mozilla/JSObjectHolder.h"
|
#include "mozilla/JSObjectHolder.h"
|
||||||
#include "mozilla/Maybe.h"
|
#include "mozilla/Maybe.h"
|
||||||
|
#include "mozilla/Mutex.h"
|
||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
#include "mozilla/StaticPrefs_devtools.h"
|
#include "mozilla/StaticPrefs_devtools.h"
|
||||||
#include "mozilla/StaticPrefs_dom.h"
|
#include "mozilla/StaticPrefs_dom.h"
|
||||||
@ -109,7 +110,8 @@ class ConsoleCallData final {
|
|||||||
|
|
||||||
ConsoleCallData(Console::MethodName aName, const nsAString& aString,
|
ConsoleCallData(Console::MethodName aName, const nsAString& aString,
|
||||||
Console* aConsole)
|
Console* aConsole)
|
||||||
: mConsoleID(aConsole->mConsoleID),
|
: mMutex("ConsoleCallData"),
|
||||||
|
mConsoleID(aConsole->mConsoleID),
|
||||||
mPrefix(aConsole->mPrefix),
|
mPrefix(aConsole->mPrefix),
|
||||||
mMethodName(aName),
|
mMethodName(aName),
|
||||||
mMicroSecondTimeStamp(JS_Now()),
|
mMicroSecondTimeStamp(JS_Now()),
|
||||||
@ -123,7 +125,7 @@ class ConsoleCallData final {
|
|||||||
mInnerIDNumber(0),
|
mInnerIDNumber(0),
|
||||||
mMethodString(aString) {}
|
mMethodString(aString) {}
|
||||||
|
|
||||||
void SetIDs(uint64_t aOuterID, uint64_t aInnerID) {
|
void SetIDs(uint64_t aOuterID, uint64_t aInnerID) MOZ_REQUIRES(mMutex) {
|
||||||
MOZ_ASSERT(mIDType == eUnknown);
|
MOZ_ASSERT(mIDType == eUnknown);
|
||||||
|
|
||||||
mOuterIDNumber = aOuterID;
|
mOuterIDNumber = aOuterID;
|
||||||
@ -131,7 +133,8 @@ class ConsoleCallData final {
|
|||||||
mIDType = eNumber;
|
mIDType = eNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetIDs(const nsAString& aOuterID, const nsAString& aInnerID) {
|
void SetIDs(const nsAString& aOuterID, const nsAString& aInnerID)
|
||||||
|
MOZ_REQUIRES(mMutex) {
|
||||||
MOZ_ASSERT(mIDType == eUnknown);
|
MOZ_ASSERT(mIDType == eUnknown);
|
||||||
|
|
||||||
mOuterIDString = aOuterID;
|
mOuterIDString = aOuterID;
|
||||||
@ -139,11 +142,12 @@ class ConsoleCallData final {
|
|||||||
mIDType = eString;
|
mIDType = eString;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetOriginAttributes(const OriginAttributes& aOriginAttributes) {
|
void SetOriginAttributes(const OriginAttributes& aOriginAttributes)
|
||||||
|
MOZ_REQUIRES(mMutex) {
|
||||||
mOriginAttributes = aOriginAttributes;
|
mOriginAttributes = aOriginAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetAddonId(nsIPrincipal* aPrincipal) {
|
void SetAddonId(nsIPrincipal* aPrincipal) MOZ_REQUIRES(mMutex) {
|
||||||
nsAutoString addonId;
|
nsAutoString addonId;
|
||||||
aPrincipal->GetAddonId(addonId);
|
aPrincipal->GetAddonId(addonId);
|
||||||
|
|
||||||
@ -154,11 +158,13 @@ class ConsoleCallData final {
|
|||||||
NS_ASSERT_OWNINGTHREAD(ConsoleCallData);
|
NS_ASSERT_OWNINGTHREAD(ConsoleCallData);
|
||||||
}
|
}
|
||||||
|
|
||||||
const nsString mConsoleID;
|
Mutex mMutex;
|
||||||
const nsString mPrefix;
|
|
||||||
|
|
||||||
const Console::MethodName mMethodName;
|
const nsString mConsoleID MOZ_GUARDED_BY(mMutex);
|
||||||
int64_t mMicroSecondTimeStamp;
|
const nsString mPrefix MOZ_GUARDED_BY(mMutex);
|
||||||
|
|
||||||
|
const Console::MethodName mMethodName MOZ_GUARDED_BY(mMutex);
|
||||||
|
int64_t mMicroSecondTimeStamp MOZ_GUARDED_BY(mMutex);
|
||||||
|
|
||||||
// These values are set in the owning thread and they contain the timestamp of
|
// These values are set in the owning thread and they contain the timestamp of
|
||||||
// when the new timer has started, the name of it and the status of the
|
// when the new timer has started, the name of it and the status of the
|
||||||
@ -168,9 +174,9 @@ class ConsoleCallData final {
|
|||||||
// They will be set on the owning thread and never touched again on that
|
// They will be set on the owning thread and never touched again on that
|
||||||
// thread. They will be used in order to create a ConsoleTimerStart dictionary
|
// thread. They will be used in order to create a ConsoleTimerStart dictionary
|
||||||
// when console.time() is used.
|
// when console.time() is used.
|
||||||
DOMHighResTimeStamp mStartTimerValue;
|
DOMHighResTimeStamp mStartTimerValue MOZ_GUARDED_BY(mMutex);
|
||||||
nsString mStartTimerLabel;
|
nsString mStartTimerLabel MOZ_GUARDED_BY(mMutex);
|
||||||
Console::TimerStatus mStartTimerStatus;
|
Console::TimerStatus mStartTimerStatus MOZ_GUARDED_BY(mMutex);
|
||||||
|
|
||||||
// These values are set in the owning thread and they contain the duration,
|
// These values are set in the owning thread and they contain the duration,
|
||||||
// the name and the status of the LogTimer method. If status is false,
|
// the name and the status of the LogTimer method. If status is false,
|
||||||
@ -178,16 +184,16 @@ class ConsoleCallData final {
|
|||||||
// touched again on that thread. They will be used in order to create a
|
// touched again on that thread. They will be used in order to create a
|
||||||
// ConsoleTimerLogOrEnd dictionary. This members are set when
|
// ConsoleTimerLogOrEnd dictionary. This members are set when
|
||||||
// console.timeEnd() or console.timeLog() are called.
|
// console.timeEnd() or console.timeLog() are called.
|
||||||
double mLogTimerDuration;
|
double mLogTimerDuration MOZ_GUARDED_BY(mMutex);
|
||||||
nsString mLogTimerLabel;
|
nsString mLogTimerLabel MOZ_GUARDED_BY(mMutex);
|
||||||
Console::TimerStatus mLogTimerStatus;
|
Console::TimerStatus mLogTimerStatus MOZ_GUARDED_BY(mMutex);
|
||||||
|
|
||||||
// These 2 values are set by IncreaseCounter or ResetCounter on the owning
|
// These 2 values are set by IncreaseCounter or ResetCounter on the owning
|
||||||
// thread and they are used by CreateCounterOrResetCounterValue.
|
// thread and they are used by CreateCounterOrResetCounterValue.
|
||||||
// These members are set when console.count() or console.countReset() are
|
// These members are set when console.count() or console.countReset() are
|
||||||
// called.
|
// called.
|
||||||
nsString mCountLabel;
|
nsString mCountLabel MOZ_GUARDED_BY(mMutex);
|
||||||
uint32_t mCountValue;
|
uint32_t mCountValue MOZ_GUARDED_BY(mMutex);
|
||||||
|
|
||||||
// The concept of outerID and innerID is misleading because when a
|
// The concept of outerID and innerID is misleading because when a
|
||||||
// ConsoleCallData is created from a window, these are the window IDs, but
|
// ConsoleCallData is created from a window, these are the window IDs, but
|
||||||
@ -195,19 +201,19 @@ class ConsoleCallData final {
|
|||||||
// subworker of a ChromeWorker these IDs are the type of worker and the
|
// subworker of a ChromeWorker these IDs are the type of worker and the
|
||||||
// filename of the callee.
|
// filename of the callee.
|
||||||
// In Console.sys.mjs the ID is 'jsm'.
|
// In Console.sys.mjs the ID is 'jsm'.
|
||||||
enum { eString, eNumber, eUnknown } mIDType;
|
enum { eString, eNumber, eUnknown } mIDType MOZ_GUARDED_BY(mMutex);
|
||||||
|
|
||||||
uint64_t mOuterIDNumber;
|
uint64_t mOuterIDNumber MOZ_GUARDED_BY(mMutex);
|
||||||
nsString mOuterIDString;
|
nsString mOuterIDString MOZ_GUARDED_BY(mMutex);
|
||||||
|
|
||||||
uint64_t mInnerIDNumber;
|
uint64_t mInnerIDNumber MOZ_GUARDED_BY(mMutex);
|
||||||
nsString mInnerIDString;
|
nsString mInnerIDString MOZ_GUARDED_BY(mMutex);
|
||||||
|
|
||||||
OriginAttributes mOriginAttributes;
|
OriginAttributes mOriginAttributes MOZ_GUARDED_BY(mMutex);
|
||||||
|
|
||||||
nsString mAddonId;
|
nsString mAddonId MOZ_GUARDED_BY(mMutex);
|
||||||
|
|
||||||
const nsString mMethodString;
|
const nsString mMethodString MOZ_GUARDED_BY(mMutex);
|
||||||
|
|
||||||
// Stack management is complicated, because we want to do it as
|
// Stack management is complicated, because we want to do it as
|
||||||
// lazily as possible. Therefore, we have the following behavior:
|
// lazily as possible. Therefore, we have the following behavior:
|
||||||
@ -215,9 +221,9 @@ class ConsoleCallData final {
|
|||||||
// 2) mReifiedStack is initialized if we're created in a worker.
|
// 2) mReifiedStack is initialized if we're created in a worker.
|
||||||
// 3) mStack is set (possibly to null if there is no JS on the stack) if
|
// 3) mStack is set (possibly to null if there is no JS on the stack) if
|
||||||
// we're created on main thread.
|
// we're created on main thread.
|
||||||
Maybe<ConsoleStackEntry> mTopStackFrame;
|
Maybe<ConsoleStackEntry> mTopStackFrame MOZ_GUARDED_BY(mMutex);
|
||||||
Maybe<nsTArray<ConsoleStackEntry>> mReifiedStack;
|
Maybe<nsTArray<ConsoleStackEntry>> mReifiedStack MOZ_GUARDED_BY(mMutex);
|
||||||
nsCOMPtr<nsIStackFrame> mStack;
|
nsCOMPtr<nsIStackFrame> mStack MOZ_GUARDED_BY(mMutex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~ConsoleCallData() = default;
|
~ConsoleCallData() = default;
|
||||||
@ -513,24 +519,27 @@ class ConsoleCallDataWorkletRunnable final : public ConsoleWorkletRunnable {
|
|||||||
jsapi.Init();
|
jsapi.Init();
|
||||||
JSContext* cx = jsapi.cx();
|
JSContext* cx = jsapi.cx();
|
||||||
|
|
||||||
JSObject* sandbox =
|
{
|
||||||
mConsoleData->GetOrCreateSandbox(cx, mWorkletImpl->Principal());
|
MutexAutoLock lock(mCallData->mMutex);
|
||||||
JS::Rooted<JSObject*> global(cx, sandbox);
|
|
||||||
if (NS_WARN_IF(!global)) {
|
JSObject* sandbox =
|
||||||
return NS_ERROR_FAILURE;
|
mConsoleData->GetOrCreateSandbox(cx, mWorkletImpl->Principal());
|
||||||
|
JS::Rooted<JSObject*> global(cx, sandbox);
|
||||||
|
if (NS_WARN_IF(!global)) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The CreateSandbox call returns a proxy to the actual sandbox object. We
|
||||||
|
// don't need a proxy here.
|
||||||
|
global = js::UncheckedUnwrap(global);
|
||||||
|
JSAutoRealm ar(cx, global);
|
||||||
|
|
||||||
|
// We don't need to set a parent object in mCallData bacause there are not
|
||||||
|
// DOM objects exposed to worklet.
|
||||||
|
|
||||||
|
ProcessCallData(cx, mConsoleData, mCallData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The CreateSandbox call returns a proxy to the actual sandbox object. We
|
|
||||||
// don't need a proxy here.
|
|
||||||
global = js::UncheckedUnwrap(global);
|
|
||||||
|
|
||||||
JSAutoRealm ar(cx, global);
|
|
||||||
|
|
||||||
// We don't need to set a parent object in mCallData bacause there are not
|
|
||||||
// DOM objects exposed to worklet.
|
|
||||||
|
|
||||||
ProcessCallData(cx, mConsoleData, mCallData);
|
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -674,35 +683,38 @@ class ConsoleCallDataWorkerRunnable final : public ConsoleWorkerRunnable {
|
|||||||
// The windows have to run in parallel.
|
// The windows have to run in parallel.
|
||||||
MOZ_ASSERT(!!aOuterWindow == !!aInnerWindow);
|
MOZ_ASSERT(!!aOuterWindow == !!aInnerWindow);
|
||||||
|
|
||||||
if (aOuterWindow) {
|
{
|
||||||
mCallData->SetIDs(aOuterWindow->WindowID(), aInnerWindow->WindowID());
|
MutexAutoLock lock(mCallData->mMutex);
|
||||||
} else {
|
if (aOuterWindow) {
|
||||||
ConsoleStackEntry frame;
|
mCallData->SetIDs(aOuterWindow->WindowID(), aInnerWindow->WindowID());
|
||||||
if (mCallData->mTopStackFrame) {
|
|
||||||
frame = *mCallData->mTopStackFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCString id = frame.mFilename;
|
|
||||||
nsString innerID;
|
|
||||||
if (aWorkerPrivate->IsSharedWorker()) {
|
|
||||||
innerID = u"SharedWorker"_ns;
|
|
||||||
} else if (aWorkerPrivate->IsServiceWorker()) {
|
|
||||||
innerID = u"ServiceWorker"_ns;
|
|
||||||
// Use scope as ID so the webconsole can decide if the message should
|
|
||||||
// show up per tab
|
|
||||||
id = aWorkerPrivate->ServiceWorkerScope();
|
|
||||||
} else {
|
} else {
|
||||||
innerID = u"Worker"_ns;
|
ConsoleStackEntry frame;
|
||||||
|
if (mCallData->mTopStackFrame) {
|
||||||
|
frame = *mCallData->mTopStackFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCString id = frame.mFilename;
|
||||||
|
nsString innerID;
|
||||||
|
if (aWorkerPrivate->IsSharedWorker()) {
|
||||||
|
innerID = u"SharedWorker"_ns;
|
||||||
|
} else if (aWorkerPrivate->IsServiceWorker()) {
|
||||||
|
innerID = u"ServiceWorker"_ns;
|
||||||
|
// Use scope as ID so the webconsole can decide if the message should
|
||||||
|
// show up per tab
|
||||||
|
id = aWorkerPrivate->ServiceWorkerScope();
|
||||||
|
} else {
|
||||||
|
innerID = u"Worker"_ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
mCallData->SetIDs(NS_ConvertUTF8toUTF16(id), innerID);
|
||||||
}
|
}
|
||||||
|
|
||||||
mCallData->SetIDs(NS_ConvertUTF8toUTF16(id), innerID);
|
mClonedData.mGlobal = aGlobal;
|
||||||
|
|
||||||
|
ProcessCallData(aCx, mConsoleData, mCallData);
|
||||||
|
|
||||||
|
mClonedData.mGlobal = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
mClonedData.mGlobal = aGlobal;
|
|
||||||
|
|
||||||
ProcessCallData(aCx, mConsoleData, mCallData);
|
|
||||||
|
|
||||||
mClonedData.mGlobal = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<ConsoleCallData> mCallData;
|
RefPtr<ConsoleCallData> mCallData;
|
||||||
@ -1294,6 +1306,9 @@ void Console::MethodInternal(JSContext* aCx, MethodName aMethodName,
|
|||||||
|
|
||||||
RefPtr<ConsoleCallData> callData =
|
RefPtr<ConsoleCallData> callData =
|
||||||
new ConsoleCallData(aMethodName, aMethodString, this);
|
new ConsoleCallData(aMethodName, aMethodString, this);
|
||||||
|
|
||||||
|
MutexAutoLock lock(callData->mMutex);
|
||||||
|
|
||||||
if (!StoreCallData(aCx, callData, aData)) {
|
if (!StoreCallData(aCx, callData, aData)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1427,7 +1442,6 @@ void Console::MethodInternal(JSContext* aCx, MethodName aMethodName,
|
|||||||
if (callData->mTopStackFrame.isSome()) {
|
if (callData->mTopStackFrame.isSome()) {
|
||||||
filename = callData->mTopStackFrame->mFilename;
|
filename = callData->mTopStackFrame->mFilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
callData->SetIDs(u"jsm"_ns, NS_ConvertUTF8toUTF16(filename));
|
callData->SetIDs(u"jsm"_ns, NS_ConvertUTF8toUTF16(filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1512,6 +1526,7 @@ void MainThreadConsoleData::ProcessCallData(
|
|||||||
const Sequence<JS::Value>& aArguments) {
|
const Sequence<JS::Value>& aArguments) {
|
||||||
AssertIsOnMainThread();
|
AssertIsOnMainThread();
|
||||||
MOZ_ASSERT(aData);
|
MOZ_ASSERT(aData);
|
||||||
|
aData->mMutex.AssertCurrentThreadOwns();
|
||||||
|
|
||||||
JS::Rooted<JS::Value> eventValue(aCx);
|
JS::Rooted<JS::Value> eventValue(aCx);
|
||||||
|
|
||||||
@ -1573,6 +1588,8 @@ bool Console::PopulateConsoleNotificationInTheTargetScope(
|
|||||||
MOZ_ASSERT(aTargetScope);
|
MOZ_ASSERT(aTargetScope);
|
||||||
MOZ_ASSERT(JS_IsGlobalObject(aTargetScope));
|
MOZ_ASSERT(JS_IsGlobalObject(aTargetScope));
|
||||||
|
|
||||||
|
aData->mMutex.AssertCurrentThreadOwns();
|
||||||
|
|
||||||
ConsoleStackEntry frame;
|
ConsoleStackEntry frame;
|
||||||
if (aData->mTopStackFrame) {
|
if (aData->mTopStackFrame) {
|
||||||
frame = *aData->mTopStackFrame;
|
frame = *aData->mTopStackFrame;
|
||||||
@ -2501,11 +2518,14 @@ void Console::RetrieveConsoleEvents(JSContext* aCx,
|
|||||||
// Here we have aCx and sequence in the same compartment.
|
// Here we have aCx and sequence in the same compartment.
|
||||||
// targetScope is the destination scope and value will be populated in its
|
// targetScope is the destination scope and value will be populated in its
|
||||||
// compartment.
|
// compartment.
|
||||||
if (NS_WARN_IF(!PopulateConsoleNotificationInTheTargetScope(
|
{
|
||||||
aCx, sequence, targetScope, &value, mCallDataStorage[i],
|
MutexAutoLock lock(mCallDataStorage[i]->mMutex);
|
||||||
&mGroupStack))) {
|
if (NS_WARN_IF(!PopulateConsoleNotificationInTheTargetScope(
|
||||||
aRv.Throw(NS_ERROR_FAILURE);
|
aCx, sequence, targetScope, &value, mCallDataStorage[i],
|
||||||
return;
|
&mGroupStack))) {
|
||||||
|
aRv.Throw(NS_ERROR_FAILURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aEvents.AppendElement(value);
|
aEvents.AppendElement(value);
|
||||||
|
@ -7,6 +7,7 @@ support-files = ""
|
|||||||
["test_console_shouldLog.js"]
|
["test_console_shouldLog.js"]
|
||||||
|
|
||||||
["test_failing_console_listener.js"]
|
["test_failing_console_listener.js"]
|
||||||
|
skip-if = ["tsan"]
|
||||||
|
|
||||||
["test_formatting.js"]
|
["test_formatting.js"]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user