Bug 1559000 - Make AutoProfilerLabel thread-safe - r=mstange
authorGerald Squelart <gsquelart@mozilla.com>
Thu, 04 Jul 2019 04:43:41 +0000
changeset 481214 c9da4cd2c6f56a3b6034c41fb208e583ba0cbcd3
parent 481213 8fed7bc35767fdfab4bac0908ae9b21c08f49e1f
child 481215 cb011f546027671fffd2305ad6cadc76fa140828
push id36236
push userrmaries@mozilla.com
push dateThu, 04 Jul 2019 21:41:54 +0000
treeherdermozilla-central@9340f815a2d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1559000
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1559000 - Make AutoProfilerLabel thread-safe - r=mstange Profilers will soon be able to set/reset entry&exit functions at different times, but simultaneously other code may want to use AutoProfilerLabel, so we need to make this all thread-safe. All shared static information is now encapsulated in an RAII class that enforces proper locking before giving access to this information. Also added a "generation" count, so that if an AutoProfilerLabel is in-flight when entry&exit functions are changed, the context given by the old entry function will not be passed to a mismatched new exit function. Differential Revision: https://phabricator.services.mozilla.com/D34807
mozglue/misc/AutoProfilerLabel.cpp
mozglue/misc/AutoProfilerLabel.h
--- a/mozglue/misc/AutoProfilerLabel.cpp
+++ b/mozglue/misc/AutoProfilerLabel.cpp
@@ -1,34 +1,94 @@
 /* -*- Mode: C++; tab-width: 2; 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 "mozilla/AutoProfilerLabel.h"
 
+#include "mozilla/PlatformMutex.h"
+
 namespace mozilla {
 
-static ProfilerLabelEnter sEnter = nullptr;
-static ProfilerLabelExit sExit = nullptr;
+// RAII class that encapsulates all shared static data, and enforces locking
+// when accessing this data.
+class MOZ_RAII AutoProfilerLabelData {
+ public:
+  AutoProfilerLabelData() { sAPLMutex.Lock(); }
+
+  ~AutoProfilerLabelData() { sAPLMutex.Unlock(); }
+
+  AutoProfilerLabelData(const AutoProfilerLabelData&) = delete;
+  void operator=(const AutoProfilerLabelData&) = delete;
+
+  const ProfilerLabelEnter& EnterCRef() const { return sEnter; }
+  ProfilerLabelEnter& EnterRef() { return sEnter; }
+
+  const ProfilerLabelExit& ExitCRef() const { return sExit; }
+  ProfilerLabelExit& ExitRef() { return sExit; }
+
+  const uint32_t& GenerationCRef() const { return sGeneration; }
+  uint32_t& GenerationRef() { return sGeneration; }
+
+ private:
+  // Thin shell around mozglue PlatformMutex, for local internal use.
+  // Does not preserve behavior in JS record/replay.
+  class Mutex : private mozilla::detail::MutexImpl {
+   public:
+    Mutex()
+        : mozilla::detail::MutexImpl(
+              mozilla::recordreplay::Behavior::DontPreserve) {}
+    void Lock() { mozilla::detail::MutexImpl::lock(); }
+    void Unlock() { mozilla::detail::MutexImpl::unlock(); }
+  };
+
+  // Mutex protecting access to the following static members.
+  static Mutex sAPLMutex;
+
+  static ProfilerLabelEnter sEnter;
+  static ProfilerLabelExit sExit;
+
+  // Current "generation" of RegisterProfilerLabelEnterExit calls.
+  static uint32_t sGeneration;
+};
+
+/* static */ AutoProfilerLabelData::Mutex AutoProfilerLabelData::sAPLMutex;
+/* static */ ProfilerLabelEnter AutoProfilerLabelData::sEnter = nullptr;
+/* static */ ProfilerLabelExit AutoProfilerLabelData::sExit = nullptr;
+/* static */ uint32_t AutoProfilerLabelData::sGeneration = 0;
 
 void RegisterProfilerLabelEnterExit(ProfilerLabelEnter aEnter,
                                     ProfilerLabelExit aExit) {
-  sEnter = aEnter;
-  sExit = aExit;
+  MOZ_ASSERT(!aEnter == !aExit, "Must provide both null or both non-null");
+
+  AutoProfilerLabelData data;
+  MOZ_ASSERT(!aEnter != !data.EnterRef(),
+             "Must go from null to non-null, or from non-null to null");
+  data.EnterRef() = aEnter;
+  data.ExitRef() = aExit;
+  ++data.GenerationRef();
 }
 
 AutoProfilerLabel::AutoProfilerLabel(
     const char* aLabel,
     const char* aDynamicString MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) {
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
-  mEntryContext = sEnter ? sEnter(aLabel, aDynamicString, this) : nullptr;
+  const AutoProfilerLabelData data;
+  mEntryContext = (data.EnterCRef())
+                      ? data.EnterCRef()(aLabel, aDynamicString, this)
+                      : nullptr;
+  mGeneration = data.GenerationCRef();
 }
 
 AutoProfilerLabel::~AutoProfilerLabel() {
-  if (sExit && mEntryContext) {
-    sExit(mEntryContext);
+  if (!mEntryContext) {
+    return;
+  }
+  const AutoProfilerLabelData data;
+  if (data.ExitCRef() && (mGeneration == data.GenerationCRef())) {
+    data.ExitCRef()(mEntryContext);
   }
 }
 
 }  // namespace mozilla
--- a/mozglue/misc/AutoProfilerLabel.h
+++ b/mozglue/misc/AutoProfilerLabel.h
@@ -44,15 +44,18 @@ class MOZ_RAII AutoProfilerLabel {
  public:
   AutoProfilerLabel(const char* aLabel,
                     const char* aDynamicString MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
   ~AutoProfilerLabel();
 
  private:
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   void* mEntryContext;
+  // Number of RegisterProfilerLabelEnterExit calls, to avoid giving an entry
+  // context from one generation to the next.
+  uint32_t mGeneration;
 };
 
 #endif
 
 }  // namespace mozilla
 
 #endif  // mozilla_AutoProfilerLabel_h