rtc::Bind: Capture scoped_refptr reference arguments by value
R=tommi@webrtc.org
Review URL: https://codereview.webrtc.org/1308563004 .
Cr-Commit-Position: refs/heads/master@{#9780}
diff --git a/webrtc/base/bind_unittest.cc b/webrtc/base/bind_unittest.cc
index 7a621dc..d38729d 100644
--- a/webrtc/base/bind_unittest.cc
+++ b/webrtc/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