Bug 1186745 part 1 - Add LeakRefPtr for pointer leaking by default. r=froydnj
authorXidorn Quan <quanxunzhen@gmail.com>
Tue, 06 Oct 2015 13:00:59 +1100
changeset 286903 0de9cc9cb4059a867c921a7263f4f63e579351df
parent 286902 53e486b9137bb483be0adf6d757b32c40b16920f
child 286904 8ba8d5b80bd08f0c339f97a239fdc25b3f6b9054
push idunknown
push userunknown
push dateunknown
reviewersfroydnj
bugs1186745
milestone44.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 1186745 part 1 - Add LeakRefPtr for pointer leaking by default. r=froydnj This class can be used instead of raw pointer for a sound leaking-by-default behavior. Also it could take advantage of move semantic check in the future.
xpcom/glue/moz.build
xpcom/glue/nsThreadUtils.cpp
xpcom/glue/standalone/moz.build
xpcom/glue/standalone/staticruntime/moz.build
xpcom/glue/staticruntime/moz.build
xpcom/threads/LeakRefPtr.h
xpcom/threads/nsThread.cpp
--- a/xpcom/glue/moz.build
+++ b/xpcom/glue/moz.build
@@ -101,16 +101,17 @@ FORCE_STATIC_LIB = True
 if CONFIG['_MSC_VER']:
     DEFINES['_USE_ANSI_CPP'] = True
     # Don't include directives about which CRT to use
     CFLAGS += ['-Zl']
     CXXFLAGS += ['-Zl']
 
 LOCAL_INCLUDES += [
     '../build',
+    '../threads',
 ]
 
 if CONFIG['ENABLE_TESTS']:
     DIRS += ['tests/gtest']
 
 # Include fallible for third party code using the xpcom glue
 USE_LIBS += [
     'fallible',
--- a/xpcom/glue/nsThreadUtils.cpp
+++ b/xpcom/glue/nsThreadUtils.cpp
@@ -2,16 +2,17 @@
 /* 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 "nsThreadUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Likely.h"
+#include "LeakRefPtr.h"
 
 #ifdef MOZILLA_INTERNAL_API
 # include "nsThreadManager.h"
 #else
 # include "nsXPCOMCIDInternal.h"
 # include "nsIThreadManager.h"
 # include "nsServiceManagerUtils.h"
 #endif
@@ -22,16 +23,18 @@
 using mozilla::IsVistaOrLater;
 #elif defined(XP_MACOSX)
 #include <sys/resource.h>
 #endif
 
 #include <pratom.h>
 #include <prthread.h>
 
+using namespace mozilla;
+
 #ifndef XPCOM_GLUE_AVOID_NSPR
 
 NS_IMPL_ISUPPORTS(nsRunnable, nsIRunnable)
 
 NS_IMETHODIMP
 nsRunnable::Run()
 {
   // Do nothing
@@ -159,27 +162,26 @@ NS_DispatchToCurrentThread(nsIRunnable* 
   }
 #endif
   return thread->Dispatch(aEvent, NS_DISPATCH_NORMAL);
 }
 
 NS_METHOD
 NS_DispatchToMainThread(already_AddRefed<nsIRunnable>&& aEvent, uint32_t aDispatchFlags)
 {
-  nsCOMPtr<nsIRunnable> event(aEvent);
+  LeakRefPtr<nsIRunnable> event(Move(aEvent));
   nsCOMPtr<nsIThread> thread;
   nsresult rv = NS_GetMainThread(getter_AddRefs(thread));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     NS_ASSERTION(false, "Failed NS_DispatchToMainThread() in shutdown; leaking");
     // NOTE: if you stop leaking here, adjust Promise::MaybeReportRejected(),
     // which assumes a leak here, or split into leaks and no-leaks versions
-    nsIRunnable* temp = event.forget().take(); // leak without using "unused <<" due to Windows (boo)
-    return temp ? rv : rv; // to make compiler not bletch on us
+    return rv;
   }
-  return thread->Dispatch(event.forget(), aDispatchFlags);
+  return thread->Dispatch(event.take(), aDispatchFlags);
 }
 
 // In the case of failure with a newly allocated runnable with a
 // refcount of zero, we intentionally leak the runnable, because it is
 // likely that the runnable is being dispatched to the main thread
 // because it owns main thread only objects, so it is not safe to
 // release them here.
 NS_METHOD
--- a/xpcom/glue/standalone/moz.build
+++ b/xpcom/glue/standalone/moz.build
@@ -33,16 +33,17 @@ if CONFIG['_MSC_VER']:
     # Don't include directives about which CRT to use
     CFLAGS += ['-Zl']
     CXXFLAGS += ['-Zl']
 
 DEFINES['XPCOM_GLUE'] = True
 
 LOCAL_INCLUDES += [
     '../../build',
+    '../../threads',
 ]
 
 # Don't use STL wrappers here (i.e. wrapped <new>); they require mozalloc
 DISABLE_STL_WRAPPING = True
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wshadow']
 
--- a/xpcom/glue/standalone/staticruntime/moz.build
+++ b/xpcom/glue/standalone/staticruntime/moz.build
@@ -25,16 +25,17 @@ if CONFIG['_MSC_VER']:
     # Don't include directives about which CRT to use
     CFLAGS += ['-Zl']
     CXXFLAGS += ['-Zl']
 
 DEFINES['XPCOM_GLUE'] = True
 
 LOCAL_INCLUDES += [
     '../../../build',
+    '../../../threads',
 ]
 
 # Statically link to the CRT on Windows
 USE_STATIC_LIBS = True
 
 # Don't use STL wrappers here (i.e. wrapped <new>); they require mozalloc
 DISABLE_STL_WRAPPING = True
 
--- a/xpcom/glue/staticruntime/moz.build
+++ b/xpcom/glue/staticruntime/moz.build
@@ -23,16 +23,17 @@ FORCE_STATIC_LIB = True
 if CONFIG['_MSC_VER']:
     DEFINES['_USE_ANSI_CPP'] = True
     # Don't include directives about which CRT to use
     CFLAGS += ['-Zl']
     CXXFLAGS += ['-Zl']
 
 LOCAL_INCLUDES += [
     '../../build',
+    '../../threads',
 ]
 
 # Statically link to the CRT on Windows
 USE_STATIC_LIBS = True
 
 # Don't use STL wrappers here (i.e. wrapped <new>); they require mozalloc
 DISABLE_STL_WRAPPING = True
 
new file mode 100644
--- /dev/null
+++ b/xpcom/threads/LeakRefPtr.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; 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/. */
+
+/* Smart pointer which leaks its owning refcounted object by default. */
+
+#ifndef LeakRefPtr_h
+#define LeakRefPtr_h
+
+#include "mozilla/AlreadyAddRefed.h"
+
+namespace mozilla {
+
+/**
+ * Instance of this class behaves like a raw pointer which leaks the
+ * resource it's owning if not explicitly released.
+ */
+template<class T>
+class LeakRefPtr
+{
+public:
+  explicit LeakRefPtr(already_AddRefed<T>&& aPtr)
+    : mRawPtr(aPtr.take()) { }
+
+  explicit operator bool() const { return !!mRawPtr; }
+
+  LeakRefPtr<T>& operator=(already_AddRefed<T>&& aPtr)
+  {
+    mRawPtr = aPtr.take();
+    return *this;
+  }
+
+  T* get() const { return mRawPtr; }
+
+  already_AddRefed<T> take()
+  {
+    T* rawPtr = mRawPtr;
+    mRawPtr = nullptr;
+    return already_AddRefed<T>(rawPtr);
+  }
+
+  void release() { NS_RELEASE(mRawPtr); }
+
+private:
+  T* MOZ_OWNING_REF mRawPtr;
+};
+
+} // namespace mozilla
+
+#endif // LeakRefPtr_h
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -31,16 +31,17 @@
 #include "mozilla/IOInterposer.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #endif // defined(MOZILLA_XPCOMRT_API)
 #include "mozilla/Services.h"
 #include "nsXPCOMPrivate.h"
 #include "mozilla/ChaosMode.h"
 #include "mozilla/TimeStamp.h"
+#include "LeakRefPtr.h"
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsServiceManagerUtils.h"
 #include "nsICrashReporter.h"
 #endif
 
 #ifdef MOZ_NUWA_PROCESS
 #include "private/pprthred.h"
@@ -536,34 +537,35 @@ nsThread::PutEvent(nsIRunnable* aEvent, 
 {
   nsCOMPtr<nsIRunnable> event(aEvent);
   return PutEvent(event.forget(), aTarget);
 }
 
 nsresult
 nsThread::PutEvent(already_AddRefed<nsIRunnable>&& aEvent, nsNestedEventTarget* aTarget)
 {
+  // We want to leak the reference when we fail to dispatch it, so that
+  // we won't release the event in a wrong thread.
+  LeakRefPtr<nsIRunnable> event(Move(aEvent));
   nsCOMPtr<nsIThreadObserver> obs;
 
 #ifdef MOZ_NUWA_PROCESS
   // On debug build or when tests are enabled, assert that we are not about to
   // create a deadlock in the Nuwa process.
   NuwaAssertNotFrozen(PR_GetThreadID(mThread), PR_GetThreadName(mThread));
 #endif
 
   {
     MutexAutoLock lock(mLock);
     nsChainedEventQueue* queue = aTarget ? aTarget->mQueue : &mEventsRoot;
     if (!queue || (queue == &mEventsRoot && mEventsAreDoomed)) {
       NS_WARNING("An event was posted to a thread that will never run it (rejected)");
-      nsCOMPtr<nsIRunnable> temp(aEvent);
-      nsIRunnable* temp2 = temp.forget().take(); // can't use unused << aEvent here due to Windows (boo)
-      return temp2 ? NS_ERROR_UNEXPECTED : NS_ERROR_UNEXPECTED; // to make compiler not bletch on us
+      return NS_ERROR_UNEXPECTED;
     }
-    queue->PutEvent(Move(aEvent), lock);
+    queue->PutEvent(event.take(), lock);
 
     // Make sure to grab the observer before dropping the lock, otherwise the
     // event that we just placed into the queue could run and eventually delete
     // this nsThread before the calling thread is scheduled again. We would then
     // crash while trying to access a dead nsThread.
     obs = mObserver;
   }