Fix GuardPageAllocator to do mprotect lazily
[folly.git] / folly / experimental / fibers / test / FibersTest.cpp
index 878fc64cdf5090ebf63214d7ad74c92dab59a251..4fe936455ef7046d80e3ba7a139653e7546172e8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 Facebook, Inc.
+ * Copyright 2016 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 #include <folly/Benchmark.h>
 #include <folly/Memory.h>
+#include <folly/futures/Future.h>
 
 #include <folly/experimental/fibers/AddTasks.h>
 #include <folly/experimental/fibers/EventBaseLoopController.h>
 #include <folly/experimental/fibers/FiberManager.h>
+#include <folly/experimental/fibers/FiberManagerMap.h>
 #include <folly/experimental/fibers/GenericBaton.h>
 #include <folly/experimental/fibers/SimpleLoopController.h>
 #include <folly/experimental/fibers/WhenN.h>
@@ -205,8 +207,6 @@ TEST(FiberManager, batonTimedWaitPostEvb) {
 TEST(FiberManager, batonTryWait) {
 
   FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-    dynamic_cast<SimpleLoopController&>(manager.loopController());
 
   // Check if try_wait and post work as expected
   Baton b;
@@ -938,16 +938,22 @@ TEST(FiberManager, runInMainContext) {
 
   checkRan = false;
 
-  manager.addTask(
-    [&]() {
-      int stackLocation;
-      runInMainContext(
-        [&]() {
-          expectMainContext(checkRan, &mainLocation, &stackLocation);
-        });
-      EXPECT_TRUE(checkRan);
-    }
-  );
+  manager.addTask([&]() {
+    struct A {
+      explicit A(int value_) : value(value_) {}
+      A(const A&) = delete;
+      A(A&&) = default;
+
+      int value;
+    };
+    int stackLocation;
+    auto ret = runInMainContext([&]() {
+      expectMainContext(checkRan, &mainLocation, &stackLocation);
+      return A(42);
+    });
+    EXPECT_TRUE(checkRan);
+    EXPECT_EQ(42, ret.value);
+  });
 
   loopController.loop(
     [&]() {
@@ -1332,6 +1338,249 @@ TEST(FiberManager, yieldTest) {
   EXPECT_TRUE(checkRan);
 }
 
+TEST(FiberManager, RequestContext) {
+  FiberManager fm(folly::make_unique<SimpleLoopController>());
+
+  bool checkRun1 = false;
+  bool checkRun2 = false;
+  bool checkRun3 = false;
+
+  folly::fibers::Baton baton1;
+  folly::fibers::Baton baton2;
+  folly::fibers::Baton baton3;
+
+  folly::RequestContext::create();
+  auto rcontext1 = folly::RequestContext::get();
+  fm.addTask([&]() {
+      EXPECT_EQ(rcontext1, folly::RequestContext::get());
+      baton1.wait([&]() {
+          EXPECT_EQ(rcontext1, folly::RequestContext::get());
+        });
+      EXPECT_EQ(rcontext1, folly::RequestContext::get());
+      runInMainContext([&]() {
+          EXPECT_EQ(rcontext1, folly::RequestContext::get());
+        });
+      checkRun1 = true;
+    });
+
+  folly::RequestContext::create();
+  auto rcontext2 = folly::RequestContext::get();
+  fm.addTaskRemote([&]() {
+      EXPECT_EQ(rcontext2, folly::RequestContext::get());
+      baton2.wait();
+      EXPECT_EQ(rcontext2, folly::RequestContext::get());
+      checkRun2 = true;
+    });
+
+  folly::RequestContext::create();
+  auto rcontext3 = folly::RequestContext::get();
+  fm.addTaskFinally(
+      [&]() {
+        EXPECT_EQ(rcontext3, folly::RequestContext::get());
+        baton3.wait();
+        EXPECT_EQ(rcontext3, folly::RequestContext::get());
+
+        return folly::Unit();
+      },
+      [&](Try<folly::Unit>&& /* t */) {
+        EXPECT_EQ(rcontext3, folly::RequestContext::get());
+        checkRun3 = true;
+      });
+
+  folly::RequestContext::create();
+  auto rcontext = folly::RequestContext::get();
+
+  fm.loopUntilNoReady();
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+
+  baton1.post();
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+  fm.loopUntilNoReady();
+  EXPECT_TRUE(checkRun1);
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+
+  baton2.post();
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+  fm.loopUntilNoReady();
+  EXPECT_TRUE(checkRun2);
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+
+  baton3.post();
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+  fm.loopUntilNoReady();
+  EXPECT_TRUE(checkRun3);
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+}
+
+TEST(FiberManager, resizePeriodically) {
+  FiberManager::Options opts;
+  opts.fibersPoolResizePeriodMs = 300;
+  opts.maxFibersPoolSize = 5;
+
+  FiberManager manager(folly::make_unique<EventBaseLoopController>(), opts);
+
+  folly::EventBase evb;
+  dynamic_cast<EventBaseLoopController&>(manager.loopController())
+    .attachEventBase(evb);
+
+  std::vector<Baton> batons(10);
+
+  size_t tasksRun = 0;
+  for (size_t i = 0; i < 30; ++i) {
+    manager.addTask([i, &batons, &tasksRun]() {
+      ++tasksRun;
+      // Keep some fibers active indefinitely
+      if (i < batons.size()) {
+        batons[i].wait();
+      }
+    });
+  }
+
+  EXPECT_EQ(0, tasksRun);
+  EXPECT_EQ(30, manager.fibersAllocated());
+  EXPECT_EQ(0, manager.fibersPoolSize());
+
+  evb.loopOnce();
+  EXPECT_EQ(30, tasksRun);
+  EXPECT_EQ(30, manager.fibersAllocated());
+  // Can go over maxFibersPoolSize, 10 of 30 fibers still active
+  EXPECT_EQ(20, manager.fibersPoolSize());
+
+  std::this_thread::sleep_for(std::chrono::milliseconds(400));
+  evb.loopOnce(); // no fibers active in this period
+  EXPECT_EQ(30, manager.fibersAllocated());
+  EXPECT_EQ(20, manager.fibersPoolSize());
+
+  std::this_thread::sleep_for(std::chrono::milliseconds(400));
+  evb.loopOnce(); // should shrink fibers pool to maxFibersPoolSize
+  EXPECT_EQ(15, manager.fibersAllocated());
+  EXPECT_EQ(5, manager.fibersPoolSize());
+
+  for (size_t i = 0; i < batons.size(); ++i) {
+    batons[i].post();
+  }
+  evb.loopOnce();
+  EXPECT_EQ(15, manager.fibersAllocated());
+  EXPECT_EQ(15, manager.fibersPoolSize());
+
+  std::this_thread::sleep_for(std::chrono::milliseconds(400));
+  evb.loopOnce(); // 10 fibers active in last period
+  EXPECT_EQ(10, manager.fibersAllocated());
+  EXPECT_EQ(10, manager.fibersPoolSize());
+
+  std::this_thread::sleep_for(std::chrono::milliseconds(400));
+  evb.loopOnce();
+  EXPECT_EQ(5, manager.fibersAllocated());
+  EXPECT_EQ(5, manager.fibersPoolSize());
+}
+
+TEST(FiberManager, batonWaitTimeoutHandler) {
+  FiberManager manager(folly::make_unique<EventBaseLoopController>());
+
+  folly::EventBase evb;
+  dynamic_cast<EventBaseLoopController&>(manager.loopController())
+    .attachEventBase(evb);
+
+  size_t fibersRun = 0;
+  Baton baton;
+  Baton::TimeoutHandler timeoutHandler;
+
+  manager.addTask([&]() {
+    baton.wait(timeoutHandler);
+    ++fibersRun;
+  });
+  manager.loopUntilNoReady();
+
+  EXPECT_FALSE(baton.try_wait());
+  EXPECT_EQ(0, fibersRun);
+
+  timeoutHandler.scheduleTimeout(std::chrono::milliseconds(250));
+  std::this_thread::sleep_for(std::chrono::milliseconds(500));
+
+  EXPECT_FALSE(baton.try_wait());
+  EXPECT_EQ(0, fibersRun);
+
+  evb.loopOnce();
+  manager.loopUntilNoReady();
+
+  EXPECT_EQ(1, fibersRun);
+}
+
+TEST(FiberManager, batonWaitTimeoutMany) {
+  FiberManager manager(folly::make_unique<EventBaseLoopController>());
+
+  folly::EventBase evb;
+  dynamic_cast<EventBaseLoopController&>(manager.loopController())
+    .attachEventBase(evb);
+
+  constexpr size_t kNumTimeoutTasks = 10000;
+  size_t tasksCount = kNumTimeoutTasks;
+
+  // We add many tasks to hit timeout queue deallocation logic.
+  for (size_t i = 0; i < kNumTimeoutTasks; ++i) {
+    manager.addTask([&]() {
+      Baton baton;
+      Baton::TimeoutHandler timeoutHandler;
+
+      folly::fibers::addTask([&] {
+        timeoutHandler.scheduleTimeout(std::chrono::milliseconds(1000));
+      });
+
+      baton.wait(timeoutHandler);
+      if (--tasksCount == 0) {
+        evb.terminateLoopSoon();
+      }
+    });
+  }
+
+  evb.loopForever();
+}
+
+TEST(FiberManager, remoteFutureTest) {
+  FiberManager fiberManager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(fiberManager.loopController());
+
+  int testValue1 = 5;
+  int testValue2 = 7;
+  auto f1 = fiberManager.addTaskFuture([&]() { return testValue1; });
+  auto f2 = fiberManager.addTaskRemoteFuture([&]() { return testValue2; });
+  loopController.loop([&]() { loopController.stop(); });
+  auto v1 = f1.get();
+  auto v2 = f2.get();
+
+  EXPECT_EQ(v1, testValue1);
+  EXPECT_EQ(v2, testValue2);
+}
+
+TEST(FiberManager, nestedFiberManagers) {
+  folly::EventBase outerEvb;
+  folly::EventBase innerEvb;
+
+  getFiberManager(outerEvb).addTask([&]() {
+    EXPECT_EQ(&getFiberManager(outerEvb),
+              FiberManager::getFiberManagerUnsafe());
+
+    runInMainContext([&]() {
+      getFiberManager(innerEvb).addTask([&]() {
+        EXPECT_EQ(&getFiberManager(innerEvb),
+                  FiberManager::getFiberManagerUnsafe());
+
+        innerEvb.terminateLoopSoon();
+      });
+
+      innerEvb.loopForever();
+    });
+
+    EXPECT_EQ(&getFiberManager(outerEvb),
+              FiberManager::getFiberManagerUnsafe());
+
+    outerEvb.terminateLoopSoon();
+  });
+
+  outerEvb.loopForever();
+}
+
 static size_t sNumAwaits;
 
 void runBenchmark(size_t numAwaits, size_t toSend) {
@@ -1358,7 +1607,7 @@ void runBenchmark(size_t numAwaits, size_t toSend) {
               [&pendingRequests](Promise<int> promise) {
                 pendingRequests.push(std::move(promise));
               });
-            assert(result == 0);
+            DCHECK_EQ(result, 0);
           }
         });
 
@@ -1378,3 +1627,67 @@ BENCHMARK(FiberManagerBasicOneAwait, iters) {
 BENCHMARK(FiberManagerBasicFiveAwaits, iters) {
   runBenchmark(5, iters);
 }
+
+BENCHMARK(FiberManagerCreateDestroy, iters) {
+  for (size_t i = 0; i < iters; ++i) {
+    folly::EventBase evb;
+    auto& fm = folly::fibers::getFiberManager(evb);
+    fm.addTask([]() {});
+    evb.loop();
+  }
+}
+
+BENCHMARK(FiberManagerAllocateDeallocatePattern, iters) {
+  static const size_t kNumAllocations = 10000;
+
+  FiberManager::Options opts;
+  opts.maxFibersPoolSize = 0;
+
+  FiberManager fiberManager(folly::make_unique<SimpleLoopController>(), opts);
+
+  for (size_t iter = 0; iter < iters; ++iter) {
+    EXPECT_EQ(0, fiberManager.fibersPoolSize());
+
+    size_t fibersRun = 0;
+
+    for (size_t i = 0; i < kNumAllocations; ++i) {
+      fiberManager.addTask(
+        [&fibersRun] {
+          ++fibersRun;
+        }
+      );
+      fiberManager.loopUntilNoReady();
+    }
+
+    EXPECT_EQ(10000, fibersRun);
+    EXPECT_EQ(0, fiberManager.fibersPoolSize());
+  }
+}
+
+BENCHMARK(FiberManagerAllocateLargeChunk, iters) {
+  static const size_t kNumAllocations = 10000;
+
+  FiberManager::Options opts;
+  opts.maxFibersPoolSize = 0;
+
+  FiberManager fiberManager(folly::make_unique<SimpleLoopController>(), opts);
+
+  for (size_t iter = 0; iter < iters; ++iter) {
+    EXPECT_EQ(0, fiberManager.fibersPoolSize());
+
+    size_t fibersRun = 0;
+
+    for (size_t i = 0; i < kNumAllocations; ++i) {
+      fiberManager.addTask(
+        [&fibersRun] {
+          ++fibersRun;
+        }
+      );
+    }
+
+    fiberManager.loopUntilNoReady();
+
+    EXPECT_EQ(10000, fibersRun);
+    EXPECT_EQ(0, fiberManager.fibersPoolSize());
+  }
+}