/*
- * 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>
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(
[&]() {
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;
- });
+ 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();
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) {
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;