}
void Baton::wait(TimeoutHandler& timeoutHandler) {
- timeoutHandler.setBaton(this);
- timeoutHandler.setFiberManager(FiberManager::getFiberManagerUnsafe());
+ auto timeoutFunc = [this, &timeoutHandler] {
+ if (!try_wait()) {
+ postHelper(TIMEOUT);
+ }
+ timeoutHandler.timeoutPtr_ = 0;
+ };
+ timeoutHandler.timeoutFunc_ = std::ref(timeoutFunc);
+ timeoutHandler.fiberManager_ = FiberManager::getFiberManagerUnsafe();
wait();
timeoutHandler.cancelTimeout();
}
waitingFiber_.store(NO_WAITER, std::memory_order_relaxed);;
}
-void Baton::TimeoutHandler::scheduleTimeout(uint32_t timeoutMs) {
+void Baton::TimeoutHandler::scheduleTimeout(
+ TimeoutController::Duration timeout) {
assert(fiberManager_ != nullptr);
- assert(baton_ != nullptr);
- if (timeoutMs > 0) {
+ assert(timeoutFunc_ != nullptr);
+ assert(timeoutPtr_ == 0);
+
+ if (timeout.count() > 0) {
timeoutPtr_ = fiberManager_->timeoutManager_->registerTimeout(
- [baton = baton_]() {
- if (!baton->try_wait()) {
- baton->postHelper(TIMEOUT);
- }
- },
- std::chrono::milliseconds(timeoutMs));
+ timeoutFunc_, timeout);
}
}
/**
* Provides a way to schedule a wakeup for a wait()ing fiber.
* A TimeoutHandler must be passed to Baton::wait(TimeoutHandler&)
- * before timeouts are scheduled/cancelled. It is only safe to use the
+ * before a timeout is scheduled. It is only safe to use the
* TimeoutHandler on the same thread as the wait()ing fiber.
* scheduleTimeout() may only be called once prior to the end of the
* associated Baton's life.
*/
class TimeoutHandler {
public:
- void scheduleTimeout(uint32_t timeoutMs);
- void cancelTimeout();
+ void scheduleTimeout(TimeoutController::Duration timeoutMs);
private:
friend class Baton;
- void setFiberManager(FiberManager* fiberManager) {
- fiberManager_ = fiberManager;
- }
- void setBaton(Baton* baton) {
- baton_ = baton;
- }
+ void cancelTimeout();
+ std::function<void()> timeoutFunc_{nullptr};
FiberManager* fiberManager_{nullptr};
- Baton* baton_{nullptr};
intptr_t timeoutPtr_{0};
};
EXPECT_FALSE(baton.try_wait());
EXPECT_EQ(0, fibersRun);
- timeoutHandler.scheduleTimeout(250);
+ timeoutHandler.scheduleTimeout(std::chrono::milliseconds(250));
std::this_thread::sleep_for(std::chrono::milliseconds(500));
EXPECT_FALSE(baton.try_wait());
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();
+}
+
static size_t sNumAwaits;
void runBenchmark(size_t numAwaits, size_t toSend) {