Bug 1498651 - make initial timer target setting more efficient; r=erahm
authorNathan Froyd <froydnj@mozilla.com>
Wed, 17 Oct 2018 19:57:36 -0400
changeset 500335 3714d89762380c501cf13257d0c373260dc7de0e
parent 500334 06e5a5bfd05ef276dce035af837f2ba76e9a55a4
child 500336 706f1a7eae882e297e85322e4dccba27a222e4a0
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerserahm
bugs1498651
milestone64.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 1498651 - make initial timer target setting more efficient; r=erahm The NS_NewTimer* family of functions, when using a custom event target, currently go through a path that looks something like: auto timer = createTimer() timer->SetTarget(target); // call the requisite Init* function return timer; This setup is inefficient, because SetTarget requires the timer mutex to be acquired. The mutex acquisition here is completely unnecessary, because the timer hasn't yet been shared out to the wider world; we can set the timer target without acquiring the mutex at all because we know that no sharing is possible at this point. This patch reworks things somewhat to make that possible.
xpcom/build/XPCOMInit.cpp
xpcom/build/XPCOMModule.inc
xpcom/threads/nsTimerImpl.cpp
xpcom/threads/nsTimerImpl.h
--- a/xpcom/build/XPCOMInit.cpp
+++ b/xpcom/build/XPCOMInit.cpp
@@ -194,17 +194,16 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupport
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRInt16)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRInt32)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRInt64)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsFloat)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsDouble)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsInterfacePointer)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsConsoleService, Init)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsTimer)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsBinaryOutputStream)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsBinaryInputStream)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsStorageStream)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsVersionComparatorImpl)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsScriptableBase64Encoder)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsVariantCC)
 
--- a/xpcom/build/XPCOMModule.inc
+++ b/xpcom/build/XPCOMModule.inc
@@ -11,17 +11,17 @@
     COMPONENT(VERSIONCOMPARATOR, nsVersionComparatorImplConstructor)
     COMPONENT(SCRIPTABLEBASE64ENCODER, nsScriptableBase64EncoderConstructor)
     COMPONENT(PIPE, nsPipeConstructor)
 
     COMPONENT(ARRAY, nsArrayBase::XPCOMConstructor)
     COMPONENT(CONSOLESERVICE, nsConsoleServiceConstructor)
     COMPONENT_M(OBSERVERSERVICE, nsObserverService::Create, Module::ALLOW_IN_GPU_PROCESS)
 
-    COMPONENT_M(TIMER, nsTimerConstructor, Module::ALLOW_IN_GPU_PROCESS)
+    COMPONENT_M(TIMER, nsTimer::XPCOMConstructor, Module::ALLOW_IN_GPU_PROCESS)
 
 #define COMPONENT_SUPPORTS(TYPE, Type)                                         \
   COMPONENT(SUPPORTS_##TYPE, nsSupports##Type##Constructor)
 
     COMPONENT_SUPPORTS(ID, ID)
     COMPONENT_SUPPORTS(STRING, String)
     COMPONENT_SUPPORTS(CSTRING, CString)
     COMPONENT_SUPPORTS(PRBOOL, PRBool)
--- a/xpcom/threads/nsTimerImpl.cpp
+++ b/xpcom/threads/nsTimerImpl.cpp
@@ -55,27 +55,23 @@ NS_GetTimerDeadlineHintOnCurrentThread(T
   return gThread
            ? gThread->FindNextFireTimeForCurrentThread(aDefault, aSearchBound)
            : TimeStamp();
 }
 
 already_AddRefed<nsITimer>
 NS_NewTimer()
 {
-  return do_AddRef(new nsTimer());
+  return NS_NewTimer(nullptr);
 }
 
 already_AddRefed<nsITimer>
 NS_NewTimer(nsIEventTarget* aTarget)
 {
-  auto timer = MakeRefPtr<nsTimer>();
-  if (aTarget && MOZ_LIKELY(timer)) {
-    MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(aTarget));
-  }
-  return timer.forget();
+  return nsTimer::WithEventTarget(aTarget).forget();
 }
 
 mozilla::Result<nsCOMPtr<nsITimer>, nsresult>
 NS_NewTimerWithObserver(nsIObserver* aObserver,
                         uint32_t aDelay,
                         uint32_t aType,
                         nsIEventTarget* aTarget)
 {
@@ -89,20 +85,17 @@ NS_NewTimerWithObserver(nsIObserver* aOb
 }
 nsresult
 NS_NewTimerWithObserver(nsITimer** aTimer,
                         nsIObserver* aObserver,
                         uint32_t aDelay,
                         uint32_t aType,
                         nsIEventTarget* aTarget)
 {
-  auto timer = MakeRefPtr<nsTimer>();
-  if (aTarget) {
-    MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(aTarget));
-  }
+  auto timer = nsTimer::WithEventTarget(aTarget);
 
   MOZ_TRY(timer->Init(aObserver, aDelay, aType));
   timer.forget(aTimer);
   return NS_OK;
 }
 
 mozilla::Result<nsCOMPtr<nsITimer>, nsresult>
 NS_NewTimerWithCallback(nsITimerCallback* aCallback,
@@ -120,20 +113,17 @@ NS_NewTimerWithCallback(nsITimerCallback
 }
 nsresult
 NS_NewTimerWithCallback(nsITimer** aTimer,
                         nsITimerCallback* aCallback,
                         uint32_t aDelay,
                         uint32_t aType,
                         nsIEventTarget* aTarget)
 {
-  auto timer = MakeRefPtr<nsTimer>();
-  if (aTarget) {
-    MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(aTarget));
-  }
+  auto timer = nsTimer::WithEventTarget(aTarget);
 
   MOZ_TRY(timer->InitWithCallback(aCallback, aDelay, aType));
   timer.forget(aTimer);
   return NS_OK;
 }
 
 mozilla::Result<nsCOMPtr<nsITimer>, nsresult>
 NS_NewTimerWithCallback(nsITimerCallback* aCallback,
@@ -151,20 +141,17 @@ NS_NewTimerWithCallback(nsITimerCallback
 }
 nsresult
 NS_NewTimerWithCallback(nsITimer** aTimer,
                         nsITimerCallback* aCallback,
                         const TimeDuration& aDelay,
                         uint32_t aType,
                         nsIEventTarget* aTarget)
 {
-  auto timer = MakeRefPtr<nsTimer>();
-  if (aTarget) {
-    MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(aTarget));
-  }
+  auto timer = nsTimer::WithEventTarget(aTarget);
 
   MOZ_TRY(timer->InitHighResolutionWithCallback(aCallback, aDelay, aType));
   timer.forget(aTimer);
   return NS_OK;
 }
 
 mozilla::Result<nsCOMPtr<nsITimer>, nsresult>
 NS_NewTimerWithFuncCallback(nsTimerCallbackFunc aCallback,
@@ -188,20 +175,17 @@ nsresult
 NS_NewTimerWithFuncCallback(nsITimer** aTimer,
                             nsTimerCallbackFunc aCallback,
                             void* aClosure,
                             uint32_t aDelay,
                             uint32_t aType,
                             const char* aNameString,
                             nsIEventTarget* aTarget)
 {
-  auto timer = MakeRefPtr<nsTimer>();
-  if (aTarget) {
-    MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(aTarget));
-  }
+  auto timer = nsTimer::WithEventTarget(aTarget);
 
   MOZ_TRY(timer->InitWithNamedFuncCallback(aCallback, aClosure,
                                            aDelay, aType,
                                            aNameString));
   timer.forget(aTimer);
   return NS_OK;
 }
 
@@ -227,20 +211,17 @@ nsresult
 NS_NewTimerWithFuncCallback(nsITimer** aTimer,
                             nsTimerCallbackFunc aCallback,
                             void* aClosure,
                             uint32_t aDelay,
                             uint32_t aType,
                             nsTimerNameCallbackFunc aNameCallback,
                             nsIEventTarget* aTarget)
 {
-  auto timer = MakeRefPtr<nsTimer>();
-  if (aTarget) {
-    MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(aTarget));
-  }
+  auto timer = nsTimer::WithEventTarget(aTarget);
 
   MOZ_TRY(timer->InitWithNameableFuncCallback(aCallback, aClosure,
                                               aDelay, aType,
                                               aNameCallback));
   timer.forget(aTimer);
   return NS_OK;
 }
 
@@ -325,25 +306,27 @@ nsTimer::Release(void)
     mImpl->CancelImpl(true);
   } else if (count == 0) {
     delete this;
   }
 
   return count;
 }
 
-nsTimerImpl::nsTimerImpl(nsITimer* aTimer) :
+nsTimerImpl::nsTimerImpl(nsITimer* aTimer, nsIEventTarget* aTarget) :
+  mEventTarget(aTarget),
   mHolder(nullptr),
   mType(0),
   mGeneration(0),
   mITimer(aTimer),
   mMutex("nsTimerImpl::mMutex")
 {
-  // XXXbsmedberg: shouldn't this be in Init()?
-  mEventTarget = mozilla::GetCurrentThreadEventTarget();
+  // XXX some code creates timers during xpcom shutdown, when threads are no
+  // longer available, so we cannot turn this on yet.
+  //MOZ_ASSERT(mEventTarget);
 }
 
 //static
 nsresult
 nsTimerImpl::Startup()
 {
   nsresult rv;
 
@@ -905,16 +888,38 @@ nsTimer::~nsTimer()
 }
 
 size_t
 nsTimer::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this);
 }
 
+/* static */ RefPtr<nsTimer>
+nsTimer::WithEventTarget(nsIEventTarget* aTarget)
+{
+  if (!aTarget) {
+    aTarget = mozilla::GetCurrentThreadEventTarget();
+  }
+  return do_AddRef(new nsTimer(aTarget));
+}
+
+/* static */ nsresult
+nsTimer::XPCOMConstructor(nsISupports* aOuter, REFNSIID aIID, void** aResult)
+{
+  *aResult = nullptr;
+  if (aOuter != nullptr) {
+    return NS_ERROR_NO_AGGREGATION;
+  }
+
+  auto timer = WithEventTarget(nullptr);
+
+  return timer->QueryInterface(aIID, aResult);
+}
+
 /* static */
 const nsTimerImpl::Callback::NameNothing nsTimerImpl::Callback::Nothing = 0;
 
 #ifdef MOZ_TASK_TRACER
 void
 nsTimerImpl::GetTLSTraceInfo()
 {
   mTracedTask.GetTLSTraceInfo();
--- a/xpcom/threads/nsTimerImpl.h
+++ b/xpcom/threads/nsTimerImpl.h
@@ -43,17 +43,17 @@ class nsTimerImpl
   ~nsTimerImpl()
   {
     MOZ_ASSERT(!mHolder);
   }
 
 public:
   typedef mozilla::TimeStamp TimeStamp;
 
-  explicit nsTimerImpl(nsITimer* aTimer);
+  nsTimerImpl(nsITimer* aTimer, nsIEventTarget* aTarget);
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsTimerImpl)
   NS_DECL_NON_VIRTUAL_NSITIMER
 
   static nsresult Startup();
   static void Shutdown();
 
   void SetDelayInternal(uint32_t aDelay, TimeStamp aBase = TimeStamp::Now());
   void CancelImpl(bool aClearITimer);
@@ -214,28 +214,36 @@ public:
   RefPtr<nsITimer>      mITimer;
   mozilla::Mutex mMutex;
   Callback              mCallback;
   Callback              mCallbackDuringFire;
 };
 
 class nsTimer final : public nsITimer
 {
+  explicit nsTimer(nsIEventTarget* aTarget)
+    : mImpl(new nsTimerImpl(this, aTarget))
+  {}
+
   virtual ~nsTimer();
 public:
-  nsTimer() : mImpl(new nsTimerImpl(this)) {}
-
   friend class TimerThread;
   friend class nsTimerEvent;
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_FORWARD_SAFE_NSITIMER(mImpl);
 
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 
+  // Create a timer targeting the given target.  nullptr indicates that the
+  // current thread should be used as the timer's target.
+  static RefPtr<nsTimer> WithEventTarget(nsIEventTarget* aTarget);
+
+  static nsresult XPCOMConstructor(nsISupports* aOuter, REFNSIID aIID, void** aResult);
+
 private:
   // nsTimerImpl holds a strong ref to us. When our refcount goes to 1, we will
   // null this to break the cycle.
   RefPtr<nsTimerImpl> mImpl;
 };
 
 // A class that holds on to an nsTimerImpl.  This lets the nsTimerImpl object
 // directly instruct its holder to forget the timer, avoiding list lookups.