Bug 1367619 - Use RWLock when accessing font prefs service off main thread; r?heycam draft
authorManish Goregaokar <manishearth@gmail.com>
Thu, 25 May 2017 15:04:13 -0700
changeset 585335 ce5d56a1ad5edf343ac871f4f1ef37f1abd88d42
parent 584877 5d6fe59a9a5dfd2d381d2cdfdc968d3305b417cf
child 585452 81067a709cae75521534963ee211760032a2519a
push id61099
push userbmo:manishearth@gmail.com
push dateFri, 26 May 2017 20:47:52 +0000
reviewersheycam
bugs1367619
milestone55.0a1
Bug 1367619 - Use RWLock when accessing font prefs service off main thread; r?heycam MozReview-Commit-ID: Dxdq6Etbwxa
intl/locale/moz.build
intl/locale/nsLanguageAtomService.cpp
intl/locale/nsLanguageAtomService.h
intl/locale/nsLocaleService.cpp
intl/locale/nsScriptableDateFormat.cpp
layout/base/StaticPresData.cpp
layout/base/StaticPresData.h
layout/base/nsPresContext.h
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
xpcom/threads/RWLock.cpp
xpcom/threads/RWLock.h
--- 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(); }