mirror of
https://github.com/facebook/sapling.git
synced 2024-10-08 07:49:11 +03:00
d71a100be2
Summary: This is part of "the great r-valuification of folly::Future": * This is something we should do for safety in general. * Context: `Future::get(...)` means both `Future::get()` and `Future::get(Duration)` * Using lvalue-qualified `Future::get(...)` has caused some failures around D7840699 since lvalue-qualification hides that operation's move-out semantics - leads to some use of future operations that are really not correct, but are not obviously incorrect. * Problems with `Future::get(...) &`: it moves-out the result but doesn't invalidate the Future - the Future remains (technically) valid even though it actually is partially moved-out. Callers can subsequently access that moved-out result via things like `future.get(...)`, `future.result()`, `future.value()`, etc. - these access an already-moved-out result which is/can be surprising. * Reasons `Future::get(...) &&` is better: its semantics are more obvious and user-testable. It moves-out the Future, leaving it with `future.valid() == false`. Reviewed By: yfeldblum Differential Revision: D8711368 fbshipit-source-id: fbfcb731097cdf9d8d98583956bc7fe614157a6b
120 lines
3.7 KiB
C++
120 lines
3.7 KiB
C++
/*
|
|
* Copyright (c) 2016-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
*/
|
|
#include "eden/fs/utils/IoFuture.h"
|
|
|
|
#include <folly/Exception.h>
|
|
#include <folly/File.h>
|
|
#include <folly/io/async/EventBase.h>
|
|
#include <folly/test/TestUtils.h>
|
|
#include <gtest/gtest.h>
|
|
#include <sys/socket.h>
|
|
#include <chrono>
|
|
|
|
using facebook::eden::IoFuture;
|
|
using facebook::eden::waitForIO;
|
|
using folly::checkUnixError;
|
|
using folly::EventBase;
|
|
using folly::EventHandler;
|
|
using namespace std::chrono_literals;
|
|
|
|
namespace {
|
|
std::pair<folly::File, folly::File> createSocketPair() {
|
|
std::array<int, 2> sockets;
|
|
int rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets.data());
|
|
checkUnixError(rc, "socketpair failed");
|
|
return std::make_pair(
|
|
folly::File{sockets[0], true}, folly::File{sockets[1], true});
|
|
}
|
|
} // namespace
|
|
|
|
TEST(IoFuture, read) {
|
|
auto sockets = createSocketPair();
|
|
EventBase evb;
|
|
|
|
// Wait for READ readiness
|
|
auto f = waitForIO(&evb, sockets.first.fd(), EventHandler::READ, 1s);
|
|
evb.loopOnce();
|
|
EXPECT_FALSE(f.isReady());
|
|
|
|
auto bytesSent = send(sockets.second.fd(), "foo", 3, 0);
|
|
checkUnixError(bytesSent, "send failed");
|
|
EXPECT_FALSE(f.isReady());
|
|
|
|
evb.loopOnce();
|
|
EXPECT_TRUE(f.isReady());
|
|
}
|
|
|
|
TEST(IoFuture, readTimeout) {
|
|
auto sockets = createSocketPair();
|
|
EventBase evb;
|
|
|
|
// Wait for READ readiness
|
|
auto f = waitForIO(&evb, sockets.first.fd(), EventHandler::READ, 10ms)
|
|
.ensure([&evb] { evb.terminateLoopSoon(); });
|
|
evb.loopForever();
|
|
ASSERT_TRUE(f.isReady());
|
|
EXPECT_THROW_ERRNO(std::move(f).get(), ETIMEDOUT);
|
|
}
|
|
|
|
TEST(IoFuture, multiRead) {
|
|
auto sockets = createSocketPair();
|
|
EventBase evb;
|
|
|
|
// Re-use the same IoFuture object for multiple reads.
|
|
IoFuture iof{&evb, sockets.first.fd()};
|
|
|
|
// Wait for writability. This should be immediately ready.
|
|
auto writeF = iof.wait(EventHandler::WRITE, 1s);
|
|
evb.loopOnce();
|
|
EXPECT_TRUE(writeF.isReady());
|
|
EXPECT_EQ(EventHandler::WRITE, std::move(writeF).get());
|
|
|
|
// Wait for readability.
|
|
auto readF1 = iof.wait(EventHandler::READ, 1s);
|
|
EXPECT_FALSE(readF1.isReady());
|
|
auto bytesSent = send(sockets.second.fd(), "foo", 3, 0);
|
|
checkUnixError(bytesSent, "send failed");
|
|
evb.loopOnce();
|
|
EXPECT_TRUE(readF1.isReady());
|
|
EXPECT_EQ(EventHandler::READ, readF1.wait().value());
|
|
EXPECT_FALSE(readF1.hasException());
|
|
|
|
// Read the data so the socket no longer has read data pending.
|
|
std::array<char, 8> buf;
|
|
auto bytesRead =
|
|
recv(sockets.first.fd(), buf.data(), buf.size(), MSG_DONTWAIT);
|
|
EXPECT_EQ(bytesRead, 3);
|
|
|
|
// Wait for readability again, but expect it to time out this time.
|
|
auto readF2 = iof.wait(EventHandler::READ, 20ms);
|
|
EXPECT_FALSE(readF2.isReady());
|
|
evb.loopOnce();
|
|
ASSERT_TRUE(readF2.isReady());
|
|
EXPECT_THROW_ERRNO(std::move(readF2).get(), ETIMEDOUT);
|
|
|
|
// Try calling iof.wait() twice in a row, even though the
|
|
// first one did not finish. This should fail the earlier future with an
|
|
// ECANCELED error.
|
|
auto readF3 = iof.wait(EventHandler::READ, 1s);
|
|
EXPECT_FALSE(readF3.isReady());
|
|
auto readF4 = iof.wait(EventHandler::READ, 1s);
|
|
ASSERT_TRUE(readF3.isReady());
|
|
EXPECT_THROW_ERRNO(std::move(readF3).get(), ECANCELED);
|
|
EXPECT_FALSE(readF4.isReady());
|
|
|
|
bytesSent = send(sockets.second.fd(), "bar", 3, 0);
|
|
checkUnixError(bytesSent, "send failed");
|
|
evb.loopOnce();
|
|
ASSERT_TRUE(readF4.isReady());
|
|
EXPECT_EQ(EventHandler::READ, std::move(readF4).get());
|
|
bytesRead = recv(sockets.first.fd(), buf.data(), buf.size(), MSG_DONTWAIT);
|
|
EXPECT_EQ(bytesRead, 3);
|
|
}
|