rtc::Bind: Capture scoped_refptr reference arguments by value

R=tommi@webrtc.org

Review URL: https://codereview.webrtc.org/1308563004 .

Cr-Original-Commit-Position: refs/heads/master@{#9780}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: b274547ebdefd0db470e21cfe113457cabd99dff
diff --git a/base/bind.h b/base/bind.h
index 2d81140..923fda2 100644
--- a/base/bind.h
+++ b/base/bind.h
@@ -128,6 +128,18 @@
                                        T*>::type type;
 };
 
+// RemoveScopedPtrRef will capture scoped_refptr by-value instead of
+// by-reference.
+template <class T> struct RemoveScopedPtrRef { typedef T type; };
+template <class T>
+struct RemoveScopedPtrRef<const scoped_refptr<T>&> {
+  typedef scoped_refptr<T> type;
+};
+template <class T>
+struct RemoveScopedPtrRef<scoped_refptr<T>&> {
+  typedef scoped_refptr<T> type;
+};
+
 }  // namespace detail
 
 template <class ObjectT, class MethodT, class R>
@@ -208,7 +220,7 @@
  private:
   MethodT method_;
   typename detail::PointerType<ObjectT>::type object_;
-  P1 p1_;
+  typename detail::RemoveScopedPtrRef<P1>::type p1_;
 };
 
 template <class FunctorT, class R,
@@ -222,7 +234,7 @@
     return functor_(p1_); }
  private:
   FunctorT functor_;
-  P1 p1_;
+  typename detail::RemoveScopedPtrRef<P1>::type p1_;
 };
 
 
@@ -291,8 +303,8 @@
  private:
   MethodT method_;
   typename detail::PointerType<ObjectT>::type object_;
-  P1 p1_;
-  P2 p2_;
+  typename detail::RemoveScopedPtrRef<P1>::type p1_;
+  typename detail::RemoveScopedPtrRef<P2>::type p2_;
 };
 
 template <class FunctorT, class R,
@@ -308,8 +320,8 @@
     return functor_(p1_, p2_); }
  private:
   FunctorT functor_;
-  P1 p1_;
-  P2 p2_;
+  typename detail::RemoveScopedPtrRef<P1>::type p1_;
+  typename detail::RemoveScopedPtrRef<P2>::type p2_;
 };
 
 
@@ -389,9 +401,9 @@
  private:
   MethodT method_;
   typename detail::PointerType<ObjectT>::type object_;
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
+  typename detail::RemoveScopedPtrRef<P1>::type p1_;
+  typename detail::RemoveScopedPtrRef<P2>::type p2_;
+  typename detail::RemoveScopedPtrRef<P3>::type p3_;
 };
 
 template <class FunctorT, class R,
@@ -409,9 +421,9 @@
     return functor_(p1_, p2_, p3_); }
  private:
   FunctorT functor_;
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
+  typename detail::RemoveScopedPtrRef<P1>::type p1_;
+  typename detail::RemoveScopedPtrRef<P2>::type p2_;
+  typename detail::RemoveScopedPtrRef<P3>::type p3_;
 };
 
 
@@ -502,10 +514,10 @@
  private:
   MethodT method_;
   typename detail::PointerType<ObjectT>::type object_;
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-  P4 p4_;
+  typename detail::RemoveScopedPtrRef<P1>::type p1_;
+  typename detail::RemoveScopedPtrRef<P2>::type p2_;
+  typename detail::RemoveScopedPtrRef<P3>::type p3_;
+  typename detail::RemoveScopedPtrRef<P4>::type p4_;
 };
 
 template <class FunctorT, class R,
@@ -525,10 +537,10 @@
     return functor_(p1_, p2_, p3_, p4_); }
  private:
   FunctorT functor_;
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-  P4 p4_;
+  typename detail::RemoveScopedPtrRef<P1>::type p1_;
+  typename detail::RemoveScopedPtrRef<P2>::type p2_;
+  typename detail::RemoveScopedPtrRef<P3>::type p3_;
+  typename detail::RemoveScopedPtrRef<P4>::type p4_;
 };
 
 
@@ -630,11 +642,11 @@
  private:
   MethodT method_;
   typename detail::PointerType<ObjectT>::type object_;
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-  P4 p4_;
-  P5 p5_;
+  typename detail::RemoveScopedPtrRef<P1>::type p1_;
+  typename detail::RemoveScopedPtrRef<P2>::type p2_;
+  typename detail::RemoveScopedPtrRef<P3>::type p3_;
+  typename detail::RemoveScopedPtrRef<P4>::type p4_;
+  typename detail::RemoveScopedPtrRef<P5>::type p5_;
 };
 
 template <class FunctorT, class R,
@@ -656,11 +668,11 @@
     return functor_(p1_, p2_, p3_, p4_, p5_); }
  private:
   FunctorT functor_;
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-  P4 p4_;
-  P5 p5_;
+  typename detail::RemoveScopedPtrRef<P1>::type p1_;
+  typename detail::RemoveScopedPtrRef<P2>::type p2_;
+  typename detail::RemoveScopedPtrRef<P3>::type p3_;
+  typename detail::RemoveScopedPtrRef<P4>::type p4_;
+  typename detail::RemoveScopedPtrRef<P5>::type p5_;
 };
 
 
@@ -773,12 +785,12 @@
  private:
   MethodT method_;
   typename detail::PointerType<ObjectT>::type object_;
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-  P4 p4_;
-  P5 p5_;
-  P6 p6_;
+  typename detail::RemoveScopedPtrRef<P1>::type p1_;
+  typename detail::RemoveScopedPtrRef<P2>::type p2_;
+  typename detail::RemoveScopedPtrRef<P3>::type p3_;
+  typename detail::RemoveScopedPtrRef<P4>::type p4_;
+  typename detail::RemoveScopedPtrRef<P5>::type p5_;
+  typename detail::RemoveScopedPtrRef<P6>::type p6_;
 };
 
 template <class FunctorT, class R,
@@ -802,12 +814,12 @@
     return functor_(p1_, p2_, p3_, p4_, p5_, p6_); }
  private:
   FunctorT functor_;
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-  P4 p4_;
-  P5 p5_;
-  P6 p6_;
+  typename detail::RemoveScopedPtrRef<P1>::type p1_;
+  typename detail::RemoveScopedPtrRef<P2>::type p2_;
+  typename detail::RemoveScopedPtrRef<P3>::type p3_;
+  typename detail::RemoveScopedPtrRef<P4>::type p4_;
+  typename detail::RemoveScopedPtrRef<P5>::type p5_;
+  typename detail::RemoveScopedPtrRef<P6>::type p6_;
 };
 
 
diff --git a/base/bind.h.pump b/base/bind.h.pump
index 9a4bc66..e1cea61 100644
--- a/base/bind.h.pump
+++ b/base/bind.h.pump
@@ -124,6 +124,18 @@
                                        T*>::type type;
 };
 
+// RemoveScopedPtrRef will capture scoped_refptr by-value instead of
+// by-reference.
+template <class T> struct RemoveScopedPtrRef { typedef T type; };
+template <class T>
+struct RemoveScopedPtrRef<const scoped_refptr<T>&> {
+  typedef scoped_refptr<T> type;
+};
+template <class T>
+struct RemoveScopedPtrRef<scoped_refptr<T>&> {
+  typedef scoped_refptr<T> type;
+};
+
 }  // namespace detail
 
 $var n = 6
@@ -145,7 +157,7 @@
   MethodT method_;
   typename detail::PointerType<ObjectT>::type object_;$for j [[
 
-  P$j p$(j)_;]]
+  typename detail::RemoveScopedPtrRef<P$j>::type p$(j)_;]]
 
 };
 
@@ -162,7 +174,7 @@
  private:
   FunctorT functor_;$for j [[
 
-  P$j p$(j)_;]]
+  typename detail::RemoveScopedPtrRef<P$j>::type p$(j)_;]]
 
 };
 
diff --git a/base/bind_unittest.cc b/base/bind_unittest.cc
index 7a621dc..d38729d 100644
--- a/base/bind_unittest.cc
+++ b/base/bind_unittest.cc
@@ -17,6 +17,8 @@
 
 namespace {
 
+struct LifeTimeCheck;
+
 struct MethodBindTester {
   void NullaryVoid() { ++call_count; }
   int NullaryInt() { ++call_count; return 1; }
@@ -25,6 +27,10 @@
   template <class T> T Identity(T value) { ++call_count; return value; }
   int UnaryByRef(int& value) const { ++call_count; return ++value; }  // NOLINT
   int Multiply(int a, int b) const { ++call_count; return a * b; }
+  void RefArgument(const scoped_refptr<LifeTimeCheck>& object) {
+    EXPECT_TRUE(object.get() != nullptr);
+  }
+
   mutable int call_count;
 };
 
@@ -42,19 +48,12 @@
   void Release();
 };
 
-class LifeTimeCheck : public RefCountInterface {
- public:
-  LifeTimeCheck(bool* has_died) : has_died_(has_died), is_ok_to_die_(false) {}
-  ~LifeTimeCheck() {
-    EXPECT_TRUE(is_ok_to_die_);
-    *has_died_ = true;
-  }
-  void PrepareToDie() { is_ok_to_die_ = true; }
+struct LifeTimeCheck {
+  LifeTimeCheck() : ref_count_(0) {}
+  void AddRef() { ++ref_count_; }
+  void Release() { --ref_count_; }
   void NullaryVoid() {}
-
- private:
-  bool* const has_died_;
-  bool is_ok_to_die_;
+  int ref_count_;
 };
 
 int Return42() { return 42; }
@@ -65,6 +64,27 @@
 
 // Try to catch any problem with scoped_refptr type deduction in rtc::Bind at
 // compile time.
+static_assert(is_same<detail::RemoveScopedPtrRef<
+                          const scoped_refptr<RefCountInterface>&>::type,
+                      scoped_refptr<RefCountInterface>>::value,
+              "const scoped_refptr& should be captured by value");
+
+static_assert(is_same<detail::RemoveScopedPtrRef<const scoped_refptr<F>&>::type,
+                      scoped_refptr<F>>::value,
+              "const scoped_refptr& should be captured by value");
+
+static_assert(
+    is_same<detail::RemoveScopedPtrRef<const int&>::type, const int&>::value,
+    "const int& should be captured as const int&");
+
+static_assert(
+    is_same<detail::RemoveScopedPtrRef<const F&>::type, const F&>::value,
+    "const F& should be captured as const F&");
+
+static_assert(
+    is_same<detail::RemoveScopedPtrRef<F&>::type, F&>::value,
+    "F& should be captured as F&");
+
 #define EXPECT_IS_CAPTURED_AS_PTR(T)                              \
   static_assert(is_same<detail::PointerType<T>::type, T*>::value, \
                 "PointerType")
@@ -124,46 +144,78 @@
 // Test Bind where method object implements RefCountInterface and is passed as a
 // pointer.
 TEST(BindTest, CapturePointerAsScopedRefPtr) {
-  bool object_has_died = false;
-  scoped_refptr<LifeTimeCheck> object =
-      new RefCountedObject<LifeTimeCheck>(&object_has_died);
+  LifeTimeCheck object;
+  EXPECT_EQ(object.ref_count_, 0);
+  scoped_refptr<LifeTimeCheck> scoped_object(&object);
+  EXPECT_EQ(object.ref_count_, 1);
   {
-    auto functor = Bind(&LifeTimeCheck::PrepareToDie, object.get());
-    object = nullptr;
-    EXPECT_FALSE(object_has_died);
-    // Run prepare to die via functor.
-    functor();
+    auto functor = Bind(&LifeTimeCheck::NullaryVoid, &object);
+    EXPECT_EQ(object.ref_count_, 2);
+    scoped_object = nullptr;
+    EXPECT_EQ(object.ref_count_, 1);
   }
-  EXPECT_TRUE(object_has_died);
+  EXPECT_EQ(object.ref_count_, 0);
 }
 
 // Test Bind where method object implements RefCountInterface and is passed as a
 // scoped_refptr<>.
 TEST(BindTest, CaptureScopedRefPtrAsScopedRefPtr) {
-  bool object_has_died = false;
-  scoped_refptr<LifeTimeCheck> object =
-      new RefCountedObject<LifeTimeCheck>(&object_has_died);
+  LifeTimeCheck object;
+  EXPECT_EQ(object.ref_count_, 0);
+  scoped_refptr<LifeTimeCheck> scoped_object(&object);
+  EXPECT_EQ(object.ref_count_, 1);
   {
-    auto functor = Bind(&LifeTimeCheck::PrepareToDie, object);
-    object = nullptr;
-    EXPECT_FALSE(object_has_died);
-    // Run prepare to die via functor.
-    functor();
+    auto functor = Bind(&LifeTimeCheck::NullaryVoid, scoped_object);
+    EXPECT_EQ(object.ref_count_, 2);
+    scoped_object = nullptr;
+    EXPECT_EQ(object.ref_count_, 1);
   }
-  EXPECT_TRUE(object_has_died);
+  EXPECT_EQ(object.ref_count_, 0);
 }
 
 // Test Bind where method object is captured as scoped_refptr<> and the functor
 // dies while there are references left.
 TEST(BindTest, FunctorReleasesObjectOnDestruction) {
-  bool object_has_died = false;
-  scoped_refptr<LifeTimeCheck> object =
-      new RefCountedObject<LifeTimeCheck>(&object_has_died);
-  Bind(&LifeTimeCheck::NullaryVoid, object.get())();
-  EXPECT_FALSE(object_has_died);
-  object->PrepareToDie();
-  object = nullptr;
-  EXPECT_TRUE(object_has_died);
+  LifeTimeCheck object;
+  EXPECT_EQ(object.ref_count_, 0);
+  scoped_refptr<LifeTimeCheck> scoped_object(&object);
+  EXPECT_EQ(object.ref_count_, 1);
+  Bind(&LifeTimeCheck::NullaryVoid, &object)();
+  EXPECT_EQ(object.ref_count_, 1);
+  scoped_object = nullptr;
+  EXPECT_EQ(object.ref_count_, 0);
+}
+
+// Test Bind with scoped_refptr<> argument.
+TEST(BindTest, ScopedRefPointerArgument) {
+  LifeTimeCheck object;
+  EXPECT_EQ(object.ref_count_, 0);
+  scoped_refptr<LifeTimeCheck> scoped_object(&object);
+  EXPECT_EQ(object.ref_count_, 1);
+  {
+    MethodBindTester bind_tester;
+    auto functor =
+        Bind(&MethodBindTester::RefArgument, &bind_tester, scoped_object);
+    EXPECT_EQ(object.ref_count_, 2);
+  }
+  EXPECT_EQ(object.ref_count_, 1);
+  scoped_object = nullptr;
+  EXPECT_EQ(object.ref_count_, 0);
+}
+
+namespace {
+
+const int* Ref(const int& a) { return &a; }
+
+}  // anonymous namespace
+
+// Test Bind with non-scoped_refptr<> reference argument.
+TEST(BindTest, RefArgument) {
+  const int x = 42;
+  EXPECT_TRUE(Ref(x) == &x);
+  // Bind() should not make a copy of |x|, i.e. the pointers should be the same.
+  auto functor = Bind(&Ref, x);
+  EXPECT_TRUE(functor() == &x);
 }
 
 }  // namespace rtc