test that the value remains alive even if the .then callback takes no arguments
authorChad Austin <chadaustin@fb.com>
Wed, 20 Dec 2017 21:37:15 +0000 (13:37 -0800)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Wed, 20 Dec 2017 21:45:21 +0000 (13:45 -0800)
Summary:
It was not clear to me, if a callback takes no arguments, the
underlying value is guaranteed to be alive during the execution of the
callback, so I wrote these tests.

Reviewed By: yfeldblum

Differential Revision: D6594921

fbshipit-source-id: 6a658afc1bf4d29eaa9c62269ddc21c7f897ad01

folly/futures/test/ThenTest.cpp

index 0bdd9ad5f7ffd76b265997086d7f4ba936fbbb50..2ec4372533add4af982f695fac8abcf559e83734 100644 (file)
@@ -36,6 +36,40 @@ struct Widget {
     throw std::logic_error("unexpected move assignment");
   }
 };
+
+struct CountedWidget : Widget {
+  static std::vector<Widget*> instances_;
+  bool alive = true;
+  /* implicit */ CountedWidget(int v) : Widget(v) {
+    instances_.push_back(this);
+  }
+  CountedWidget(const CountedWidget& other) : Widget(other) {
+    instances_.push_back(this);
+  }
+  CountedWidget(CountedWidget&& other) noexcept(false)
+      : Widget(std::move(other)) {
+    other.alive = false;
+    other.remove();
+    instances_.push_back(this);
+  }
+  ~CountedWidget() {
+    if (alive) {
+      remove();
+    }
+  }
+
+ private:
+  CountedWidget& operator=(const CountedWidget&) = delete;
+  CountedWidget& operator=(CountedWidget&&) = delete;
+
+  void remove() {
+    auto iter = std::find(instances_.begin(), instances_.end(), this);
+    EXPECT_TRUE(iter != instances_.end());
+    instances_.erase(iter);
+  }
+};
+
+std::vector<Widget*> CountedWidget::instances_;
 } // namespace
 
 TEST(Then, tryConstructor) {
@@ -172,6 +206,29 @@ TEST(Then, constValue) {
   EXPECT_EQ(future.value(), 23);
 }
 
+TEST(Then, objectAliveDuringImmediateNoParamContinuation) {
+  auto f = makeFuture<CountedWidget>(23);
+  auto called = false;
+  f.then([&] {
+    EXPECT_EQ(CountedWidget::instances_.size(), 1u);
+    EXPECT_EQ(CountedWidget::instances_[0]->v_, 23);
+    called = true;
+  });
+  EXPECT_EQ(true, called);
+}
+
+TEST(Then, objectAliveDuringDeferredNoParamContinuation) {
+  auto p = Promise<CountedWidget>{};
+  bool called = false;
+  p.getFuture().then([&] {
+    EXPECT_EQ(CountedWidget::instances_.size(), 1u);
+    EXPECT_EQ(CountedWidget::instances_[0]->v_, 23);
+    called = true;
+  });
+  p.setValue(CountedWidget{23});
+  EXPECT_EQ(true, called);
+}
+
 TEST(Then, voidThenShouldPropagateExceptions) {
   EXPECT_FALSE(makeFuture(42).then().hasException());
   EXPECT_TRUE(makeFuture<int>(std::runtime_error("err"))