Commit c20009d3 authored by Aaryaman Sagar's avatar Aaryaman Sagar Committed by Facebook Github Bot

Move ManualTimekeeper to its own file in folly/futures

Summary:
This is a useful testing utility that people can use if they use timeout APIs
like `folly::futures::sleep()`

Reviewed By: LeeHowes

Differential Revision: D17065352

fbshipit-source-id: f9ea8366af7fc9429bfb1065328ed746c824f810
parent 9205b9d2
......@@ -21,6 +21,7 @@
#include <folly/executors/SerialExecutor.h>
#include <folly/executors/ThreadedExecutor.h>
#include <folly/executors/TimekeeperScheduledExecutor.h>
#include <folly/futures/ManualTimekeeper.h>
#include <folly/portability/GTest.h>
#include <folly/synchronization/Baton.h>
......@@ -28,6 +29,7 @@ using folly::Duration;
using folly::Executor;
using folly::Func;
using folly::Future;
using folly::ManualTimekeeper;
using folly::Promise;
using folly::ScheduledExecutor;
using folly::TimekeeperScheduledExecutor;
......@@ -36,54 +38,6 @@ using std::chrono::steady_clock;
using time_point = steady_clock::time_point;
namespace {
// Manually controlled Timekeeper for unit testing.
// We assume advance(), now(), and numScheduled() are called
// from only a single thread, while after() can safely be called
// from multiple threads.
class ManualTimekeeper : public folly::Timekeeper {
public:
explicit ManualTimekeeper(Executor::KeepAlive<Executor>&& executor)
: executor_(std::move(executor)), now_(steady_clock::now()) {}
Future<Unit> after(Duration dur) override {
auto contract = folly::makePromiseContract<Unit>(executor_.get());
if (dur.count() == 0) {
contract.first.setValue(folly::unit);
} else {
schedule_.withWLock([&contract, this, &dur](auto& schedule) {
schedule.insert(std::make_pair(now_ + dur, std::move(contract.first)));
});
}
return std::move(contract.second);
}
void advance(Duration dur) {
now_ += dur;
schedule_.withWLock([this](auto& schedule) {
auto start = schedule.begin();
auto end = schedule.upper_bound(now_);
for (auto iter = start; iter != end; iter++) {
iter->second.setValue(folly::unit);
}
schedule.erase(start, end);
});
}
time_point now() {
return now_;
}
size_t numScheduled() {
return schedule_.withRLock([](const auto& sched) { return sched.size(); });
}
private:
Executor::KeepAlive<Executor> executor_;
time_point now_;
folly::Synchronized<std::multimap<time_point, Promise<Unit>>> schedule_;
};
void simpleTest(std::unique_ptr<folly::Executor> const& parent) {
auto tk = std::make_shared<ManualTimekeeper>(
folly::getKeepAliveToken(parent.get()));
......
/*
* Copyright 2019-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/futures/ManualTimekeeper.h>
namespace folly {
ManualTimekeeper::ManualTimekeeper(Executor::KeepAlive<Executor>&& executor)
: executor_{std::move(executor)}, now_{std::chrono::steady_clock::now()} {}
Future<Unit> ManualTimekeeper::after(Duration dur) {
auto contract = folly::makePromiseContract<Unit>(executor_.get());
if (dur.count() == 0) {
contract.first.setValue(folly::unit);
} else {
schedule_.withWLock([&contract, this, &dur](auto& schedule) {
schedule.insert(std::make_pair(now_ + dur, std::move(contract.first)));
});
}
return std::move(contract.second);
}
void ManualTimekeeper::advance(Duration dur) {
now_ += dur;
schedule_.withWLock([this](auto& schedule) {
auto start = schedule.begin();
auto end = schedule.upper_bound(now_);
for (auto iter = start; iter != end; iter++) {
iter->second.setValue(folly::unit);
}
schedule.erase(start, end);
});
}
std::chrono::steady_clock::time_point ManualTimekeeper::now() const {
return now_;
}
std::size_t ManualTimekeeper::numScheduled() const {
return schedule_.withRLock([](const auto& sched) { return sched.size(); });
}
} // namespace folly
/*
* Copyright 2004-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <folly/Synchronized.h>
#include <folly/futures/Future.h>
#include <folly/futures/Promise.h>
#include <chrono>
#include <map>
namespace folly {
// Manually controlled Timekeeper for unit testing.
//
// We assume advance(), now(), and numScheduled() are called from only a single
// thread, while after() can safely be called from multiple threads.
class ManualTimekeeper : public folly::Timekeeper {
public:
explicit ManualTimekeeper(Executor::KeepAlive<Executor>&& executor);
/// The returned future is completed when someone calls advance and pushes the
/// executor's clock to a value greater than or equal to (now() + dur)
Future<Unit> after(folly::Duration dur) override;
/// Advance the timekeeper's clock to (now() + dur). All futures with target
/// time points less than or equal to (now() + dur) are fulfilled after the
/// call to advance() returns
void advance(folly::Duration dur);
/// Returns the current clock value in the timekeeper. This is advanced only
/// when someone calls advance()
std::chrono::steady_clock::time_point now() const;
/// Returns the number of futures that are pending and have not yet been
/// fulfilled
std::size_t numScheduled() const;
private:
Executor::KeepAlive<Executor> executor_;
std::chrono::steady_clock::time_point now_;
folly::Synchronized<
std::multimap<std::chrono::steady_clock::time_point, Promise<Unit>>>
schedule_;
};
} // namespace folly
/*
* Copyright 2004-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/futures/ManualTimekeeper.h>
#include <folly/portability/GTest.h>
#include <chrono>
using namespace std::literals;
namespace folly {
class ManualTimekeeperTest : public ::testing::Test {};
TEST_F(ManualTimekeeperTest, Basic) {
auto timekeeper = folly::ManualTimekeeper{&folly::InlineExecutor::instance()};
auto future = timekeeper.after(100s);
timekeeper.advance(100s);
EXPECT_TRUE(future.isReady());
}
TEST_F(ManualTimekeeperTest, AdvanceWithoutAnyFutures) {
auto timekeeper = folly::ManualTimekeeper{&folly::InlineExecutor::instance()};
timekeeper.advance(100s);
auto future = timekeeper.after(100s);
EXPECT_FALSE(future.isReady());
timekeeper.advance(100s);
EXPECT_TRUE(future.isReady());
}
TEST_F(ManualTimekeeperTest, AdvanceWithManyFutures) {
auto timekeeper = folly::ManualTimekeeper{&folly::InlineExecutor::instance()};
auto one = timekeeper.after(100s);
auto two = timekeeper.after(200s);
auto three = timekeeper.after(300s);
EXPECT_FALSE(one.isReady());
EXPECT_FALSE(two.isReady());
EXPECT_FALSE(three.isReady());
timekeeper.advance(100s);
EXPECT_TRUE(one.isReady());
EXPECT_FALSE(two.isReady());
EXPECT_FALSE(three.isReady());
timekeeper.advance(100s);
EXPECT_TRUE(one.isReady());
EXPECT_TRUE(two.isReady());
EXPECT_FALSE(three.isReady());
timekeeper.advance(100s);
EXPECT_TRUE(one.isReady());
EXPECT_TRUE(two.isReady());
EXPECT_TRUE(three.isReady());
timekeeper.advance(100s);
EXPECT_TRUE(one.isReady());
EXPECT_TRUE(two.isReady());
EXPECT_TRUE(three.isReady());
auto four = timekeeper.after(100s);
EXPECT_FALSE(four.isReady());
timekeeper.advance(100s);
EXPECT_TRUE(one.isReady());
EXPECT_TRUE(two.isReady());
EXPECT_TRUE(three.isReady());
EXPECT_TRUE(four.isReady());
}
} // namespace folly
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment