fix memory leak in case of large number of retries
[folly.git] / folly / futures / test / RetryingTest.cpp
index d1c429dfd323b40e504ec30df6b587b5af8c72b6..2de5c8a441a4a6884181eb4699ca60717381b24a 100644 (file)
@@ -20,6 +20,8 @@
 
 #include <folly/futures/Future.h>
 #include <folly/portability/GTest.h>
+#include <folly/portability/SysResource.h>
+#include "TestExecutor.h"
 
 using namespace std;
 using namespace std::chrono;
@@ -137,6 +139,45 @@ TEST(RetryingTest, policy_sleep_defaults) {
   });
 }
 
+TEST(RetryingTest, large_retries) {
+  rlimit oldMemLimit;
+  PCHECK(getrlimit(RLIMIT_AS, &oldMemLimit) == 0);
+
+  rlimit newMemLimit;
+  newMemLimit.rlim_cur = std::min(1UL << 30, oldMemLimit.rlim_max);
+  newMemLimit.rlim_max = oldMemLimit.rlim_max;
+  PCHECK(setrlimit(RLIMIT_AS, &newMemLimit) == 0);
+  SCOPE_EXIT {
+    PCHECK(setrlimit(RLIMIT_AS, &oldMemLimit) == 0);
+  };
+
+  TestExecutor executor;
+  // size of implicit promise is at least the size of the return.
+  using LargeReturn = array<uint64_t, 16000>;
+  auto func = [&executor](size_t retryNum) -> Future<LargeReturn> {
+    return via(&executor).then([retryNum] {
+      return retryNum < 10000
+          ? makeFuture<LargeReturn>(
+                make_exception_wrapper<std::runtime_error>("keep trying"))
+          : makeFuture<LargeReturn>(LargeReturn());
+    });
+  };
+
+  vector<Future<LargeReturn>> futures;
+  for (auto idx = 0; idx < 40; ++idx) {
+    futures.emplace_back(futures::retrying(
+        [&executor](size_t, const exception_wrapper&) {
+          return via(&executor).then([] { return true; });
+        },
+        func));
+  }
+
+  for (auto& f : futures) {
+    f.wait();
+    EXPECT_TRUE(f.hasValue());
+  }
+}
+
 /*
 TEST(RetryingTest, policy_sleep_cancel) {
   multiAttemptExpectDurationWithin(5, milliseconds(0), milliseconds(10), []{