Bug 1367619 - Use RWLock when accessing font prefs service off main thread; r?heycam
MozReview-Commit-ID: Dxdq6Etbwxa
--- a/intl/locale/moz.build
+++ b/intl/locale/moz.build
@@ -45,22 +45,30 @@ EXPORTS.mozilla.intl += [
'OSPreferences.h',
]
UNIFIED_SOURCES += [
'LocaleService.cpp',
'nsCollationFactory.cpp',
'nsLanguageAtomService.cpp',
'nsLocale.cpp',
- 'nsLocaleService.cpp',
'nsScriptableDateFormat.cpp',
'nsUConvPropertySearch.cpp',
'OSPreferences.cpp',
]
+# This file includes Carbon.h, which pulls
+# in CoreServices on OSX. This requires
+# a TextRange struct, which clashes with mozilla::TextRange,
+# because of a `using namespace mozilla;` somewhere
+# in the unified build
+SOURCES += [
+ 'nsLocaleService.cpp',
+]
+
if CONFIG['ENABLE_INTL_API']:
UNIFIED_SOURCES += [
'DateTimeFormat.cpp',
'nsCollation.cpp',
]
else:
UNIFIED_SOURCES += [
'DateTimeFormatAndroid.cpp',
--- a/intl/locale/nsLanguageAtomService.cpp
+++ b/intl/locale/nsLanguageAtomService.cpp
@@ -6,16 +6,17 @@
#include "nsLanguageAtomService.h"
#include "nsUConvPropertySearch.h"
#include "nsUnicharUtils.h"
#include "nsIAtom.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/intl/OSPreferences.h"
#include "mozilla/dom/EncodingUtils.h"
#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ServoBindings.h"
using namespace mozilla;
using mozilla::intl::OSPreferences;
static constexpr nsUConvProp kLangGroups[] = {
#include "langGroups.properties.h"
};
@@ -61,25 +62,29 @@ nsLanguageAtomService::GetLocaleLanguage
mLocaleLanguage = NS_Atomize(locale);
}
} while (0);
return mLocaleLanguage;
}
nsIAtom*
-nsLanguageAtomService::GetLanguageGroup(nsIAtom *aLanguage)
+nsLanguageAtomService::GetLanguageGroup(nsIAtom *aLanguage, bool* aNeedsToCache)
{
nsIAtom *retVal = mLangToGroup.GetWeak(aLanguage);
if (!retVal) {
- MOZ_ASSERT(NS_IsMainThread(), "Should not append to cache off main thread");
+ if (aNeedsToCache) {
+ *aNeedsToCache = true;
+ return nullptr;
+ }
nsCOMPtr<nsIAtom> uncached = GetUncachedLanguageGroup(aLanguage);
retVal = uncached.get();
+ AssertIsMainThreadOrServoLangFontPrefsCacheLocked();
// The hashtable will keep an owning reference to the atom
mLangToGroup.Put(aLanguage, uncached);
}
return retVal;
}
already_AddRefed<nsIAtom>
--- a/intl/locale/nsLanguageAtomService.h
+++ b/intl/locale/nsLanguageAtomService.h
@@ -18,17 +18,33 @@
class nsLanguageAtomService
{
public:
static nsLanguageAtomService* GetService();
nsIAtom* LookupLanguage(const nsACString &aLanguage);
already_AddRefed<nsIAtom> LookupCharSet(const nsACString& aCharSet);
nsIAtom* GetLocaleLanguage();
- nsIAtom* GetLanguageGroup(nsIAtom* aLanguage);
+
+ // Returns the language group that the specified language is a part of.
+ //
+ // aNeedsToCache is used for two things. If null, it indicates that
+ // the nsLanguageAtomService is safe to cache the result of the
+ // language group lookup, either because we're on the main thread,
+ // or because we're on a style worker thread but the font lock has
+ // been acquired. If non-null, it indicates that it's not safe to
+ // cache the result of the language group lookup (because we're on
+ // a style worker thread without the lock acquired). In this case,
+ // GetLanguageGroup will store true in *aNeedsToCache true if we
+ // would have cached the result of a new lookup, and false if we
+ // were able to use an existing cached result. Thus, callers that
+ // get a true *aNeedsToCache outparam value should make an effort
+ // to re-call GetLanguageGroup when it is safe to cache, to avoid
+ // recomputing the language group again later.
+ nsIAtom* GetLanguageGroup(nsIAtom* aLanguage, bool* aNeedsToCache = nullptr);
already_AddRefed<nsIAtom> GetUncachedLanguageGroup(nsIAtom* aLanguage) const;
private:
nsInterfaceHashtable<nsISupportsHashKey, nsIAtom> mLangToGroup;
nsCOMPtr<nsIAtom> mLocaleLanguage;
};
#endif
--- a/intl/locale/nsLocaleService.cpp
+++ b/intl/locale/nsLocaleService.cpp
@@ -20,16 +20,18 @@
#elif defined(XP_MACOSX)
# include <Carbon/Carbon.h>
#elif defined(XP_UNIX)
# include <locale.h>
# include <stdlib.h>
# include "nsPosixLocale.h"
#endif
+using namespace mozilla;
+
//
// implementation constants
const int LocaleListLength = 6;
const char* LocaleList[LocaleListLength] =
{
NSILOCALE_COLLATE,
NSILOCALE_CTYPE,
NSILOCALE_MONETARY,
--- a/intl/locale/nsScriptableDateFormat.cpp
+++ b/intl/locale/nsScriptableDateFormat.cpp
@@ -3,16 +3,17 @@
* 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 "DateTimeFormat.h"
#include "mozilla/Sprintf.h"
#include "nsIScriptableDateFormat.h"
#include "nsCOMPtr.h"
#include "nsServiceManagerUtils.h"
+#include "nsILocaleService.h"
static NS_DEFINE_CID(kLocaleServiceCID, NS_LOCALESERVICE_CID);
class nsScriptableDateFormat final : public nsIScriptableDateFormat {
public:
NS_DECL_ISUPPORTS
NS_IMETHOD FormatDateTime(const char16_t *locale,
--- a/layout/base/StaticPresData.cpp
+++ b/layout/base/StaticPresData.cpp
@@ -2,18 +2,18 @@
/* 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 "mozilla/StaticPresData.h"
#include "mozilla/Preferences.h"
+#include "mozilla/ServoBindings.h"
#include "nsPresContext.h"
-
namespace mozilla {
static StaticPresData* sSingleton = nullptr;
void
StaticPresData::Init()
{
MOZ_ASSERT(!sSingleton);
@@ -231,20 +231,21 @@ LangGroupFontPrefs::Initialize(nsIAtom*
generic_dot_langGroup.get(),
NS_ConvertUTF16toUTF8(font->name).get(), font->size,
font->sizeAdjust);
#endif
}
}
nsIAtom*
-StaticPresData::GetLangGroup(nsIAtom* aLanguage) const
+StaticPresData::GetLangGroup(nsIAtom* aLanguage,
+ bool* aNeedsToCache) const
{
nsIAtom* langGroupAtom = nullptr;
- langGroupAtom = mLangService->GetLanguageGroup(aLanguage);
+ langGroupAtom = mLangService->GetLanguageGroup(aLanguage, aNeedsToCache);
if (!langGroupAtom) {
langGroupAtom = nsGkAtoms::x_western; // Assume x-western is safe...
}
return langGroupAtom;
}
already_AddRefed<nsIAtom>
StaticPresData::GetUncachedLangGroup(nsIAtom* aLanguage) const
@@ -253,45 +254,58 @@ StaticPresData::GetUncachedLangGroup(nsI
if (!langGroupAtom) {
langGroupAtom = nsGkAtoms::x_western; // Assume x-western is safe...
}
return langGroupAtom.forget();
}
const LangGroupFontPrefs*
StaticPresData::GetFontPrefsForLangHelper(nsIAtom* aLanguage,
- const LangGroupFontPrefs* aPrefs) const
+ const LangGroupFontPrefs* aPrefs,
+ bool* aNeedsToCache) const
{
// Get language group for aLanguage:
MOZ_ASSERT(aLanguage);
MOZ_ASSERT(mLangService);
MOZ_ASSERT(aPrefs);
- nsIAtom* langGroupAtom = GetLangGroup(aLanguage);
+ nsIAtom* langGroupAtom = GetLangGroup(aLanguage, aNeedsToCache);
+
+ if (aNeedsToCache && *aNeedsToCache) {
+ return nullptr;
+ }
LangGroupFontPrefs* prefs = const_cast<LangGroupFontPrefs*>(aPrefs);
if (prefs->mLangGroup) { // if initialized
DebugOnly<uint32_t> count = 0;
for (;;) {
NS_ASSERTION(++count < 35, "Lang group count exceeded!!!");
if (prefs->mLangGroup == langGroupAtom) {
return prefs;
}
if (!prefs->mNext) {
break;
}
prefs = prefs->mNext;
}
- MOZ_ASSERT(NS_IsMainThread(), "Should not append to cache off main thread");
+ if (aNeedsToCache) {
+ *aNeedsToCache = true;
+ return nullptr;
+ }
+ AssertIsMainThreadOrServoLangFontPrefsCacheLocked();
// nothing cached, so go on and fetch the prefs for this lang group:
prefs = prefs->mNext = new LangGroupFontPrefs;
}
- MOZ_ASSERT(NS_IsMainThread(), "Should not append to cache off main thread");
+ if (aNeedsToCache) {
+ *aNeedsToCache = true;
+ return nullptr;
+ }
+ AssertIsMainThreadOrServoLangFontPrefsCacheLocked();
prefs->Initialize(langGroupAtom);
return prefs;
}
const nsFont*
StaticPresData::GetDefaultFontHelper(uint8_t aFontID, nsIAtom *aLanguage,
const LangGroupFontPrefs* aPrefs) const
--- a/layout/base/StaticPresData.h
+++ b/layout/base/StaticPresData.h
@@ -92,18 +92,31 @@ public:
* to actual nscoord values.
*/
const nscoord* GetBorderWidthTable() { return mBorderWidthTable; }
/**
* Given a language, get the language group name, which can
* be used as an argument to LangGroupFontPrefs::Initialize()
*
+ * aNeedsToCache is used for two things. If null, it indicates that
+ * the nsLanguageAtomService is safe to cache the result of the
+ * language group lookup, either because we're on the main thread,
+ * or because we're on a style worker thread but the font lock has
+ * been acquired. If non-null, it indicates that it's not safe to
+ * cache the result of the language group lookup (because we're on
+ * a style worker thread without the lock acquired). In this case,
+ * GetLanguageGroup will store true in *aNeedsToCache true if we
+ * would have cached the result of a new lookup, and false if we
+ * were able to use an existing cached result. Thus, callers that
+ * get a true *aNeedsToCache outparam value should make an effort
+ * to re-call GetLanguageGroup when it is safe to cache, to avoid
+ * recomputing the language group again later.
*/
- nsIAtom* GetLangGroup(nsIAtom* aLanguage) const;
+ nsIAtom* GetLangGroup(nsIAtom* aLanguage, bool* aNeedsToCache = nullptr) const;
/**
* Same as GetLangGroup, but will not cache the result
*
*/
already_AddRefed<nsIAtom> GetUncachedLangGroup(nsIAtom* aLanguage) const;
/**
@@ -114,19 +127,22 @@ public:
* whereby language-specific prefs are read per-document, and the
* results are stored in a linked list, which is assumed to be very short
* since most documents only ever use one language.
*
* Storing this per-session rather than per-document would almost certainly
* be fine. But just to be on the safe side, we leave the old mechanism as-is,
* with an additional per-session cache that new callers can use if they don't
* have a PresContext.
+ *
+ * See comment on GetLangGroup for the usage of aNeedsToCache.
*/
const LangGroupFontPrefs* GetFontPrefsForLangHelper(nsIAtom* aLanguage,
- const LangGroupFontPrefs* aPrefs) const;
+ const LangGroupFontPrefs* aPrefs,
+ bool* aNeedsToCache = nullptr) const;
/**
* Get the default font for the given language and generic font ID.
* aLanguage may not be nullptr.
*
* This object is read-only, you must copy the font to modify it.
*
* When aFontID is kPresContext_DefaultVariableFontID or
* kPresContext_DefaultFixedFontID (which equals
@@ -149,20 +165,20 @@ public:
* These versions operate on the font pref cache on StaticPresData.
*/
const nsFont* GetDefaultFont(uint8_t aFontID, nsIAtom* aLanguage) const
{
MOZ_ASSERT(aLanguage);
return GetDefaultFontHelper(aFontID, aLanguage, GetFontPrefsForLang(aLanguage));
}
- const LangGroupFontPrefs* GetFontPrefsForLang(nsIAtom* aLanguage) const
+ const LangGroupFontPrefs* GetFontPrefsForLang(nsIAtom* aLanguage, bool* aNeedsToCache = nullptr) const
{
MOZ_ASSERT(aLanguage);
- return GetFontPrefsForLangHelper(aLanguage, &mStaticLangGroupFontPrefs);
+ return GetFontPrefsForLangHelper(aLanguage, &mStaticLangGroupFontPrefs, aNeedsToCache);
}
void ResetCachedFontPrefs() { mStaticLangGroupFontPrefs.Reset(); }
private:
StaticPresData();
~StaticPresData() {}
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -347,21 +347,24 @@ public:
/**
* Get the default font for the given language and generic font ID.
* If aLanguage is nullptr, the document's language is used.
*
* See the comment in StaticPresData::GetDefaultFont.
*/
const nsFont* GetDefaultFont(uint8_t aFontID,
- nsIAtom *aLanguage) const
+ nsIAtom *aLanguage, bool* aNeedsToCache = nullptr) const
{
nsIAtom* lang = aLanguage ? aLanguage : mLanguage.get();
- return StaticPresData::Get()->GetDefaultFontHelper(aFontID, lang,
- GetFontPrefsForLang(lang));
+ const LangGroupFontPrefs* prefs = GetFontPrefsForLang(lang, aNeedsToCache);
+ if (aNeedsToCache && *aNeedsToCache) {
+ return nullptr;
+ }
+ return StaticPresData::Get()->GetDefaultFontHelper(aFontID, lang, prefs);
}
void ForceCacheLang(nsIAtom *aLanguage);
void CacheAllLangs();
/** Get a cached boolean pref, by its type */
// * - initially created for bugs 31816, 20760, 22963
bool GetCachedBoolPref(nsPresContext_CachedBoolPrefType aPrefType) const
@@ -1227,20 +1230,20 @@ protected:
static void PrefChangedUpdateTimerCallback(nsITimer *aTimer, void *aClosure);
void GetUserPreferences();
/**
* Fetch the user's font preferences for the given aLanguage's
* langugage group.
*/
- const LangGroupFontPrefs* GetFontPrefsForLang(nsIAtom *aLanguage) const
+ const LangGroupFontPrefs* GetFontPrefsForLang(nsIAtom *aLanguage, bool* aNeedsToCache = nullptr) const
{
nsIAtom* lang = aLanguage ? aLanguage : mLanguage.get();
- return StaticPresData::Get()->GetFontPrefsForLangHelper(lang, &mLangGroupFontPrefs);
+ return StaticPresData::Get()->GetFontPrefsForLangHelper(lang, &mLangGroupFontPrefs, aNeedsToCache);
}
void UpdateCharSet(const nsCString& aCharSet);
static bool NotifyDidPaintSubdocumentCallback(nsIDocument* aDocument, void* aData);
public:
void DoChangeCharSet(const nsCString& aCharSet);
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -52,16 +52,17 @@
#include "mozilla/Keyframe.h"
#include "mozilla/Mutex.h"
#include "mozilla/ServoElementSnapshot.h"
#include "mozilla/ServoRestyleManager.h"
#include "mozilla/StyleAnimationValue.h"
#include "mozilla/SystemGroup.h"
#include "mozilla/ServoMediaList.h"
#include "mozilla/ServoComputedValuesWithParent.h"
+#include "mozilla/RWLock.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ElementInlines.h"
#include "mozilla/dom/HTMLTableCellElement.h"
#include "mozilla/dom/HTMLBodyElement.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/URLExtraData.h"
#if defined(MOZ_MEMORY)
@@ -78,16 +79,47 @@ using namespace mozilla::dom;
result.swap(mPtr); \
return result.forget(); \
}
#include "mozilla/ServoArcTypeList.h"
#undef SERVO_ARC_TYPE
static Mutex* sServoFontMetricsLock = nullptr;
+static RWLock* sServoLangFontPrefsLock = nullptr;
+
+
+static
+const nsFont*
+ThreadSafeGetDefaultFontHelper(const nsPresContext* aPresContext, nsIAtom* aLanguage)
+{
+ bool needsCache = false;
+ const nsFont* retval;
+
+ {
+ AutoReadLock guard(*sServoLangFontPrefsLock);
+ retval = aPresContext->GetDefaultFont(kPresContext_DefaultVariableFont_ID,
+ aLanguage, &needsCache);
+ }
+ if (!needsCache) {
+ return retval;
+ }
+ {
+ AutoWriteLock guard(*sServoLangFontPrefsLock);
+ retval = aPresContext->GetDefaultFont(kPresContext_DefaultVariableFont_ID,
+ aLanguage, nullptr);
+ }
+ return retval;
+}
+
+void
+AssertIsMainThreadOrServoLangFontPrefsCacheLocked()
+{
+ MOZ_ASSERT(NS_IsMainThread() || sServoLangFontPrefsLock->LockedForWritingByCurrentThread());
+}
uint32_t
Gecko_ChildrenCount(RawGeckoNodeBorrowed aNode)
{
return aNode->GetChildCount();
}
bool
@@ -1078,19 +1110,17 @@ Gecko_CopyFontFamilyFrom(nsFont* dst, co
dst->fontlist = src->fontlist;
}
void
Gecko_nsFont_InitSystem(nsFont* aDest, int32_t aFontId,
const nsStyleFont* aFont, RawGeckoPresContextBorrowed aPresContext)
{
MutexAutoLock lock(*sServoFontMetricsLock);
- const nsFont* defaultVariableFont =
- aPresContext->GetDefaultFont(kPresContext_DefaultVariableFont_ID,
- aFont->mLanguage);
+ const nsFont* defaultVariableFont = ThreadSafeGetDefaultFontHelper(aPresContext, aFont->mLanguage);
// We have passed uninitialized memory to this function,
// initialize it. We can't simply return an nsFont because then
// we need to know its size beforehand. Servo cannot initialize nsFont
// itself, so this will do.
nsFont* system = new (aDest) nsFont(*defaultVariableFont);
MOZ_RELEASE_ASSERT(system);
@@ -1949,19 +1979,17 @@ Gecko_nsStyleFont_CopyLangFrom(nsStyleFo
{
aFont->mLanguage = aSource->mLanguage;
}
void
Gecko_nsStyleFont_FixupNoneGeneric(nsStyleFont* aFont,
RawGeckoPresContextBorrowed aPresContext)
{
- const nsFont* defaultVariableFont =
- aPresContext->GetDefaultFont(kPresContext_DefaultVariableFont_ID,
- aFont->mLanguage);
+ const nsFont* defaultVariableFont = ThreadSafeGetDefaultFontHelper(aPresContext, aFont->mLanguage);
nsRuleNode::FixupNoneGeneric(&aFont->mFont, aPresContext,
aFont->mGenericID, defaultVariableFont);
}
void
FontSizePrefs::CopyFrom(const LangGroupFontPrefs& prefs)
{
mDefaultVariableSize = prefs.mDefaultVariableFont.size;
@@ -1988,22 +2016,24 @@ Gecko_GetBaseSize(nsIAtom* aLanguage)
void
InitializeServo()
{
URLExtraData::InitDummy();
Servo_Initialize(URLExtraData::Dummy());
sServoFontMetricsLock = new Mutex("Gecko_GetFontMetrics");
+ sServoLangFontPrefsLock = new RWLock("nsPresContext::GetDefaultFont");
}
void
ShutdownServo()
{
delete sServoFontMetricsLock;
+ delete sServoLangFontPrefsLock;
Servo_Shutdown();
}
namespace mozilla {
void
AssertIsMainThreadOrServoFontMetricsLocked()
{
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -508,16 +508,17 @@ struct GeckoFontMetrics
GeckoFontMetrics Gecko_GetFontMetrics(RawGeckoPresContextBorrowed pres_context,
bool is_vertical,
const nsStyleFont* font,
nscoord font_size,
bool use_user_font_set);
int32_t Gecko_GetAppUnitsPerPhysicalInch(RawGeckoPresContextBorrowed pres_context);
void InitializeServo();
void ShutdownServo();
+void AssertIsMainThreadOrServoLangFontPrefsCacheLocked();
const nsMediaFeature* Gecko_GetMediaFeatures();
nsCSSKeyword Gecko_LookupCSSKeyword(const uint8_t* string, uint32_t len);
const char* Gecko_CSSKeywordString(nsCSSKeyword keyword, uint32_t* len);
// Font face rule
// Creates and returns a new (already-addrefed) nsCSSFontFaceRule object.
nsCSSFontFaceRule* Gecko_CSSFontFaceRule_Create(uint32_t line, uint32_t column);
--- a/xpcom/threads/RWLock.cpp
+++ b/xpcom/threads/RWLock.cpp
@@ -27,16 +27,24 @@ RWLock::RWLock(const char* aName)
#ifdef XP_WIN
InitializeSRWLock(NativeHandle(mRWLock));
#else
MOZ_RELEASE_ASSERT(pthread_rwlock_init(NativeHandle(mRWLock), nullptr) == 0,
"pthread_rwlock_init failed");
#endif
}
+#ifdef DEBUG
+bool
+RWLock::LockedForWritingByCurrentThread()
+{
+ return mOwningThread == PR_GetCurrentThread();
+}
+#endif
+
#ifndef XP_WIN
RWLock::~RWLock()
{
MOZ_RELEASE_ASSERT(pthread_rwlock_destroy(NativeHandle(mRWLock)) == 0,
"pthread_rwlock_destroy failed");
}
#endif
--- a/xpcom/threads/RWLock.h
+++ b/xpcom/threads/RWLock.h
@@ -50,16 +50,17 @@ public:
// POSIX ones do.
#ifdef XP_WIN
~RWLock() = default;
#else
~RWLock();
#endif
#ifdef DEBUG
+ bool LockedForWritingByCurrentThread();
void ReadLock();
void ReadUnlock();
void WriteLock();
void WriteUnlock();
#else
void ReadLock() { ReadLockInternal(); }
void ReadUnlock() { ReadUnlockInternal(); }
void WriteLock() { WriteLockInternal(); }