/*
- * 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>
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;
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(
[&]() {
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) {
[&pendingRequests](Promise<int> promise) {
pendingRequests.push(std::move(promise));
});
- assert(result == 0);
+ DCHECK_EQ(result, 0);
}
});
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());
+ }
+}