1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-08-08 09:42:06 +03:00

Quic socket write for the inplace buffer writing

Summary: as title

Reviewed By: mjoras

Differential Revision: D21211414

fbshipit-source-id: 5c4b8a747457e3f21da77541a35da1af1b1ac6e4
This commit is contained in:
Yang Chi
2020-05-05 09:50:51 -07:00
committed by Facebook GitHub Bot
parent f3fe4b3f63
commit 014d50aa41
4 changed files with 432 additions and 13 deletions

View File

@@ -218,6 +218,109 @@ ssize_t GSOPacketBatchWriter::write(
: sock.write(address, buf_); : sock.write(address, buf_);
} }
GSOInplacePacketBatchWriter::GSOInplacePacketBatchWriter(
QuicConnectionStateBase& conn,
size_t maxPackets)
: conn_(conn), maxPackets_(maxPackets) {}
void GSOInplacePacketBatchWriter::reset() {
lastPacketEnd_ = nullptr;
prevSize_ = 0;
numPackets_ = 0;
}
bool GSOInplacePacketBatchWriter::needsFlush(size_t size) {
return prevSize_ && size > prevSize_;
}
bool GSOInplacePacketBatchWriter::append(
std::unique_ptr<folly::IOBuf>&& /*buf*/,
size_t size,
const folly::SocketAddress& /* addr */,
folly::AsyncUDPSocket* /* sock */) {
CHECK(!needsFlush(size));
ScopedBufAccessor scopedBufAccessor(conn_.bufAccessor);
auto& buf = scopedBufAccessor.buf();
if (!lastPacketEnd_) {
CHECK(prevSize_ == 0 && numPackets_ == 0);
prevSize_ = size;
lastPacketEnd_ = buf->tail();
numPackets_ = 1;
return false;
}
CHECK(prevSize_ && prevSize_ >= size);
++numPackets_;
lastPacketEnd_ = buf->tail();
if (prevSize_ > size || numPackets_ == maxPackets_) {
return true;
}
return false;
}
/**
* Write the buffer owned by conn_.bufAccessor to the sock, until
* lastPacketEnd_. After write, everything in the buffer after lastPacketEnd_
* will be moved to the beginning of the buffer, and buffer will be returned to
* conn_.bufAccessor.
*/
ssize_t GSOInplacePacketBatchWriter::write(
folly::AsyncUDPSocket& sock,
const folly::SocketAddress& address) {
ScopedBufAccessor scopedBufAccessor(conn_.bufAccessor);
CHECK(lastPacketEnd_);
auto& buf = scopedBufAccessor.buf();
CHECK(!buf->isChained());
CHECK(lastPacketEnd_ >= buf->data() && lastPacketEnd_ <= buf->tail())
<< "lastPacketEnd_=" << (long)lastPacketEnd_
<< " data=" << (long)buf->data() << " tail=" << (long)buf->tail();
auto diffToEnd = buf->tail() - lastPacketEnd_;
CHECK(
diffToEnd >= 0 &&
static_cast<uint64_t>(diffToEnd) <= conn_.udpSendPacketLen);
auto diffToStart = lastPacketEnd_ - buf->data();
buf->trimEnd(diffToEnd);
auto bytesWritten = (numPackets_ > 1)
? sock.writeGSO(address, buf, static_cast<int>(prevSize_))
: sock.write(address, buf);
/**
* If there is one more bytes after lastPacketEnd_, that means there is a
* packet we choose not to write in this batch (e.g., it has a size larger
* than all existing packets in this batch). So after the socket write, we
* need to move that packet from the middle of the buffer to the beginning of
* the buffer so make sure we maximize the buffer space. An alternative here
* is to writem to write everything out in the previous sock write call. But
* that needs a much bigger change in the IoBufQuicBatch API.
*/
if (diffToEnd) {
buf->trimStart(diffToStart);
buf->append(diffToEnd);
buf->retreat(diffToStart);
CHECK(buf->length() <= conn_.udpSendPacketLen);
CHECK(0 == buf->headroom());
} else {
buf->clear();
}
reset();
return bytesWritten;
}
bool GSOInplacePacketBatchWriter::empty() const {
return numPackets_ == 0;
}
size_t GSOInplacePacketBatchWriter::size() const {
if (empty()) {
return 0;
}
ScopedBufAccessor scopedBufAccessor(conn_.bufAccessor);
CHECK(lastPacketEnd_);
auto& buf = scopedBufAccessor.buf();
CHECK(lastPacketEnd_ >= buf->data() && lastPacketEnd_ <= buf->tail());
size_t ret = lastPacketEnd_ - buf->data();
return ret;
}
// SendmmsgPacketBatchWriter // SendmmsgPacketBatchWriter
SendmmsgPacketBatchWriter::SendmmsgPacketBatchWriter(size_t maxBufs) SendmmsgPacketBatchWriter::SendmmsgPacketBatchWriter(size_t maxBufs)
: maxBufs_(maxBufs) { : maxBufs_(maxBufs) {
@@ -390,7 +493,9 @@ BatchWriterPtr BatchWriterFactory::makeBatchWriter(
const quic::QuicBatchingMode& batchingMode, const quic::QuicBatchingMode& batchingMode,
uint32_t batchSize, uint32_t batchSize,
bool useThreadLocal, bool useThreadLocal,
const std::chrono::microseconds& threadLocalDelay) { const std::chrono::microseconds& threadLocalDelay,
DataPathType dataPathType,
QuicConnectionStateBase& conn) {
#if USE_THREAD_LOCAL_BATCH_WRITER #if USE_THREAD_LOCAL_BATCH_WRITER
if (useThreadLocal && if (useThreadLocal &&
(batchingMode == quic::QuicBatchingMode::BATCHING_MODE_SENDMMSG_GSO) && (batchingMode == quic::QuicBatchingMode::BATCHING_MODE_SENDMMSG_GSO) &&
@@ -414,7 +519,10 @@ BatchWriterPtr BatchWriterFactory::makeBatchWriter(
return BatchWriterPtr(new SinglePacketBatchWriter()); return BatchWriterPtr(new SinglePacketBatchWriter());
case quic::QuicBatchingMode::BATCHING_MODE_GSO: { case quic::QuicBatchingMode::BATCHING_MODE_GSO: {
if (sock.getGSO() >= 0) { if (sock.getGSO() >= 0) {
return BatchWriterPtr(new GSOPacketBatchWriter(batchSize)); if (dataPathType == DataPathType::ChainedMemory) {
return BatchWriterPtr(new GSOPacketBatchWriter(batchSize));
}
return BatchWriterPtr(new GSOInplacePacketBatchWriter(conn, batchSize));
} }
return BatchWriterPtr(new SinglePacketBatchWriter()); return BatchWriterPtr(new SinglePacketBatchWriter());

View File

@@ -12,6 +12,7 @@
#include <folly/io/IOBuf.h> #include <folly/io/IOBuf.h>
#include <folly/io/async/AsyncUDPSocket.h> #include <folly/io/async/AsyncUDPSocket.h>
#include <quic/QuicConstants.h> #include <quic/QuicConstants.h>
#include <quic/state/StateData.h>
namespace quic { namespace quic {
class BatchWriter { class BatchWriter {
@@ -128,6 +129,54 @@ class GSOPacketBatchWriter : public IOBufBatchWriter {
size_t prevSize_{0}; size_t prevSize_{0};
}; };
class GSOInplacePacketBatchWriter : public BatchWriter {
public:
explicit GSOInplacePacketBatchWriter(
QuicConnectionStateBase& conn,
size_t maxPackets);
~GSOInplacePacketBatchWriter() override = default;
struct ScopedBufAccessor {
public:
explicit ScopedBufAccessor(BufAccessor* accessor) : bufAccessor_(accessor) {
CHECK(bufAccessor_->ownsBuffer());
buf_ = bufAccessor_->obtain();
}
~ScopedBufAccessor() {
bufAccessor_->release(std::move(buf_));
}
std::unique_ptr<folly::IOBuf>& buf() {
return buf_;
}
private:
BufAccessor* bufAccessor_;
std::unique_ptr<folly::IOBuf> buf_;
};
void reset() override;
bool needsFlush(size_t size) override;
bool append(
std::unique_ptr<folly::IOBuf>&& buf,
size_t size,
const folly::SocketAddress& addr,
folly::AsyncUDPSocket* sock) override;
ssize_t write(
folly::AsyncUDPSocket& sock,
const folly::SocketAddress& address) override;
bool empty() const override;
size_t size() const override;
private:
QuicConnectionStateBase& conn_;
size_t maxPackets_;
const uint8_t* lastPacketEnd_{nullptr};
size_t prevSize_{0};
size_t numPackets_{0};
};
class SendmmsgPacketBatchWriter : public BatchWriter { class SendmmsgPacketBatchWriter : public BatchWriter {
public: public:
explicit SendmmsgPacketBatchWriter(size_t maxBufs); explicit SendmmsgPacketBatchWriter(size_t maxBufs);
@@ -203,7 +252,9 @@ class BatchWriterFactory {
const quic::QuicBatchingMode& batchingMode, const quic::QuicBatchingMode& batchingMode,
uint32_t batchSize, uint32_t batchSize,
bool useThreadLocal, bool useThreadLocal,
const std::chrono::microseconds& threadLocalDelay); const std::chrono::microseconds& threadLocalDelay,
DataPathType dataPathType,
QuicConnectionStateBase& conn);
}; };
} // namespace quic } // namespace quic

View File

@@ -979,7 +979,9 @@ uint64_t writeConnectionDataToSocket(
connection.transportSettings.batchingMode, connection.transportSettings.batchingMode,
connection.transportSettings.maxBatchSize, connection.transportSettings.maxBatchSize,
connection.transportSettings.useThreadLocalBatching, connection.transportSettings.useThreadLocalBatching,
connection.transportSettings.threadLocalDelay); connection.transportSettings.threadLocalDelay,
connection.transportSettings.dataPathType,
connection);
IOBufQuicBatch ioBufBatch( IOBufQuicBatch ioBufBatch(
std::move(batchWriter), std::move(batchWriter),

View File

@@ -8,7 +8,11 @@
#include <quic/api/QuicBatchWriter.h> #include <quic/api/QuicBatchWriter.h>
#include <folly/io/async/test/MockAsyncUDPSocket.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <quic/server/state/ServerStateMachine.h>
using namespace testing;
namespace quic { namespace quic {
namespace testing { namespace testing {
@@ -20,7 +24,10 @@ constexpr const auto kBatchNum = 3;
constexpr const auto kNumLoops = 10; constexpr const auto kNumLoops = 10;
struct QuicBatchWriterTest : public ::testing::Test, struct QuicBatchWriterTest : public ::testing::Test,
public ::testing::WithParamInterface<bool> {}; public ::testing::WithParamInterface<bool> {
protected:
QuicServerConnectionState conn_;
};
TEST_P(QuicBatchWriterTest, TestBatchingNone) { TEST_P(QuicBatchWriterTest, TestBatchingNone) {
bool useThreadLocal = GetParam(); bool useThreadLocal = GetParam();
@@ -34,7 +41,9 @@ TEST_P(QuicBatchWriterTest, TestBatchingNone) {
quic::QuicBatchingMode::BATCHING_MODE_NONE, quic::QuicBatchingMode::BATCHING_MODE_NONE,
kBatchNum, kBatchNum,
useThreadLocal, useThreadLocal,
quic::kDefaultThreadLocalDelay); quic::kDefaultThreadLocalDelay,
DataPathType::ChainedMemory,
conn_);
CHECK(batchWriter); CHECK(batchWriter);
std::string strTest('A', kStrLen); std::string strTest('A', kStrLen);
@@ -63,7 +72,9 @@ TEST_P(QuicBatchWriterTest, TestBatchingGSOBase) {
quic::QuicBatchingMode::BATCHING_MODE_GSO, quic::QuicBatchingMode::BATCHING_MODE_GSO,
1, 1,
useThreadLocal, useThreadLocal,
quic::kDefaultThreadLocalDelay); quic::kDefaultThreadLocalDelay,
DataPathType::ChainedMemory,
conn_);
CHECK(batchWriter); CHECK(batchWriter);
std::string strTest(kStrLen, 'A'); std::string strTest(kStrLen, 'A');
// if GSO is not available, just test we've got a regular // if GSO is not available, just test we've got a regular
@@ -90,7 +101,9 @@ TEST_P(QuicBatchWriterTest, TestBatchingGSOLastSmallPacket) {
quic::QuicBatchingMode::BATCHING_MODE_GSO, quic::QuicBatchingMode::BATCHING_MODE_GSO,
1, 1,
useThreadLocal, useThreadLocal,
quic::kDefaultThreadLocalDelay); quic::kDefaultThreadLocalDelay,
DataPathType::ChainedMemory,
conn_);
CHECK(batchWriter); CHECK(batchWriter);
std::string strTest; std::string strTest;
// only if GSO is available // only if GSO is available
@@ -129,7 +142,9 @@ TEST_P(QuicBatchWriterTest, TestBatchingGSOLastBigPacket) {
quic::QuicBatchingMode::BATCHING_MODE_GSO, quic::QuicBatchingMode::BATCHING_MODE_GSO,
1, 1,
useThreadLocal, useThreadLocal,
quic::kDefaultThreadLocalDelay); quic::kDefaultThreadLocalDelay,
DataPathType::ChainedMemory,
conn_);
CHECK(batchWriter); CHECK(batchWriter);
std::string strTest; std::string strTest;
// only if GSO is available // only if GSO is available
@@ -163,7 +178,9 @@ TEST_P(QuicBatchWriterTest, TestBatchingGSOBatchNum) {
quic::QuicBatchingMode::BATCHING_MODE_GSO, quic::QuicBatchingMode::BATCHING_MODE_GSO,
kBatchNum, kBatchNum,
useThreadLocal, useThreadLocal,
quic::kDefaultThreadLocalDelay); quic::kDefaultThreadLocalDelay,
DataPathType::ChainedMemory,
conn_);
CHECK(batchWriter); CHECK(batchWriter);
std::string strTest(kStrLen, 'A'); std::string strTest(kStrLen, 'A');
// if GSO is not available, just test we've got a regular // if GSO is not available, just test we've got a regular
@@ -206,7 +223,9 @@ TEST_P(QuicBatchWriterTest, TestBatchingSendmmsg) {
quic::QuicBatchingMode::BATCHING_MODE_SENDMMSG, quic::QuicBatchingMode::BATCHING_MODE_SENDMMSG,
kBatchNum, kBatchNum,
useThreadLocal, useThreadLocal,
quic::kDefaultThreadLocalDelay); quic::kDefaultThreadLocalDelay,
DataPathType::ChainedMemory,
conn_);
CHECK(batchWriter); CHECK(batchWriter);
std::string strTest(kStrLen, 'A'); std::string strTest(kStrLen, 'A');
@@ -246,7 +265,9 @@ TEST_P(QuicBatchWriterTest, TestBatchingSendmmsgGSOBatchNum) {
quic::QuicBatchingMode::BATCHING_MODE_SENDMMSG_GSO, quic::QuicBatchingMode::BATCHING_MODE_SENDMMSG_GSO,
kBatchNum, kBatchNum,
useThreadLocal, useThreadLocal,
quic::kDefaultThreadLocalDelay); quic::kDefaultThreadLocalDelay,
DataPathType::ChainedMemory,
conn_);
CHECK(batchWriter); CHECK(batchWriter);
std::string strTest(kStrLen, 'A'); std::string strTest(kStrLen, 'A');
// if GSO is not available, just test we've got a regular // if GSO is not available, just test we've got a regular
@@ -289,7 +310,9 @@ TEST_P(QuicBatchWriterTest, TestBatchingSendmmsgGSOBatcBigSmallPacket) {
quic::QuicBatchingMode::BATCHING_MODE_SENDMMSG_GSO, quic::QuicBatchingMode::BATCHING_MODE_SENDMMSG_GSO,
3 * kBatchNum, 3 * kBatchNum,
useThreadLocal, useThreadLocal,
quic::kDefaultThreadLocalDelay); quic::kDefaultThreadLocalDelay,
DataPathType::ChainedMemory,
conn_);
CHECK(batchWriter); CHECK(batchWriter);
std::string strTest(kStrLen, 'A'); std::string strTest(kStrLen, 'A');
// if GSO is not available, just test we've got a regular // if GSO is not available, just test we've got a regular
@@ -325,6 +348,241 @@ TEST_P(QuicBatchWriterTest, TestBatchingSendmmsgGSOBatcBigSmallPacket) {
} }
} }
TEST_P(QuicBatchWriterTest, InplaceWriterNeedsFlush) {
bool useThreadLocal = GetParam();
folly::EventBase evb;
folly::AsyncUDPSocket sock(&evb);
sock.setReuseAddr(false);
sock.bind(folly::SocketAddress("127.0.0.1", 0));
uint32_t batchSize = 20;
auto bufAccessor =
std::make_unique<SimpleBufAccessor>(conn_.udpSendPacketLen * batchSize);
conn_.bufAccessor = bufAccessor.get();
auto batchWriter = quic::BatchWriterFactory::makeBatchWriter(
sock,
quic::QuicBatchingMode::BATCHING_MODE_GSO,
batchSize,
useThreadLocal,
quic::kDefaultThreadLocalDelay,
DataPathType::ContinuousMemory,
conn_);
CHECK(batchWriter);
EXPECT_FALSE(batchWriter->needsFlush(1000));
for (size_t i = 0; i < 10; i++) {
EXPECT_FALSE(batchWriter->needsFlush(1000));
batchWriter->append(nullptr, 1000, folly::SocketAddress(), nullptr);
}
EXPECT_TRUE(batchWriter->needsFlush(conn_.udpSendPacketLen));
}
TEST_P(QuicBatchWriterTest, InplaceWriterAppendLimit) {
bool useThreadLocal = GetParam();
folly::EventBase evb;
folly::AsyncUDPSocket sock(&evb);
sock.setReuseAddr(false);
sock.bind(folly::SocketAddress("127.0.0.1", 0));
uint32_t batchSize = 20;
auto bufAccessor =
std::make_unique<SimpleBufAccessor>(conn_.udpSendPacketLen * batchSize);
conn_.bufAccessor = bufAccessor.get();
auto batchWriter = quic::BatchWriterFactory::makeBatchWriter(
sock,
quic::QuicBatchingMode::BATCHING_MODE_GSO,
batchSize,
useThreadLocal,
quic::kDefaultThreadLocalDelay,
DataPathType::ContinuousMemory,
conn_);
CHECK(batchWriter);
EXPECT_FALSE(batchWriter->needsFlush(1000));
for (size_t i = 0; i < batchSize - 1; i++) {
auto buf = bufAccessor->obtain();
buf->append(1000);
bufAccessor->release(std::move(buf));
EXPECT_FALSE(
batchWriter->append(nullptr, 1000, folly::SocketAddress(), nullptr));
}
auto buf = bufAccessor->obtain();
buf->append(1000);
bufAccessor->release(std::move(buf));
EXPECT_TRUE(
batchWriter->append(nullptr, 1000, folly::SocketAddress(), nullptr));
}
TEST_P(QuicBatchWriterTest, InplaceWriterAppendSmaller) {
bool useThreadLocal = GetParam();
folly::EventBase evb;
folly::AsyncUDPSocket sock(&evb);
sock.setReuseAddr(false);
sock.bind(folly::SocketAddress("127.0.0.1", 0));
uint32_t batchSize = 20;
auto bufAccessor =
std::make_unique<SimpleBufAccessor>(conn_.udpSendPacketLen * batchSize);
conn_.bufAccessor = bufAccessor.get();
auto batchWriter = quic::BatchWriterFactory::makeBatchWriter(
sock,
quic::QuicBatchingMode::BATCHING_MODE_GSO,
batchSize,
useThreadLocal,
quic::kDefaultThreadLocalDelay,
DataPathType::ContinuousMemory,
conn_);
CHECK(batchWriter);
EXPECT_FALSE(batchWriter->needsFlush(1000));
for (size_t i = 0; i < batchSize / 2; i++) {
auto buf = bufAccessor->obtain();
buf->append(1000);
bufAccessor->release(std::move(buf));
EXPECT_FALSE(
batchWriter->append(nullptr, 1000, folly::SocketAddress(), nullptr));
}
auto buf = bufAccessor->obtain();
buf->append(700);
bufAccessor->release(std::move(buf));
EXPECT_TRUE(
batchWriter->append(nullptr, 700, folly::SocketAddress(), nullptr));
}
TEST_P(QuicBatchWriterTest, InplaceWriterWriteAll) {
bool useThreadLocal = GetParam();
folly::EventBase evb;
folly::test::MockAsyncUDPSocket sock(&evb);
uint32_t batchSize = 20;
auto bufAccessor =
std::make_unique<SimpleBufAccessor>(conn_.udpSendPacketLen * batchSize);
conn_.bufAccessor = bufAccessor.get();
EXPECT_CALL(sock, getGSO()).WillRepeatedly(Return(1));
auto batchWriter = quic::BatchWriterFactory::makeBatchWriter(
sock,
quic::QuicBatchingMode::BATCHING_MODE_GSO,
batchSize,
useThreadLocal,
quic::kDefaultThreadLocalDelay,
DataPathType::ContinuousMemory,
conn_);
CHECK(batchWriter);
ASSERT_FALSE(batchWriter->needsFlush(1000));
for (size_t i = 0; i < 5; i++) {
auto buf = bufAccessor->obtain();
buf->append(1000);
bufAccessor->release(std::move(buf));
ASSERT_FALSE(
batchWriter->append(nullptr, 1000, folly::SocketAddress(), nullptr));
}
auto buf = bufAccessor->obtain();
buf->append(700);
bufAccessor->release(std::move(buf));
ASSERT_TRUE(
batchWriter->append(nullptr, 700, folly::SocketAddress(), nullptr));
EXPECT_CALL(sock, writeGSO(_, _, _))
.Times(1)
.WillOnce(Invoke([&](const auto& /* addr */,
const std::unique_ptr<folly::IOBuf>& buf,
int gso) {
EXPECT_EQ(1000 * 5 + 700, buf->length());
EXPECT_EQ(1000, gso);
return 1000 * 5 + 700;
}));
EXPECT_EQ(1000 * 5 + 700, batchWriter->write(sock, folly::SocketAddress()));
EXPECT_TRUE(bufAccessor->ownsBuffer());
buf = bufAccessor->obtain();
EXPECT_EQ(0, buf->length());
}
TEST_P(QuicBatchWriterTest, InplaceWriterWriteOne) {
bool useThreadLocal = GetParam();
folly::EventBase evb;
folly::test::MockAsyncUDPSocket sock(&evb);
uint32_t batchSize = 20;
auto bufAccessor =
std::make_unique<SimpleBufAccessor>(conn_.udpSendPacketLen * batchSize);
conn_.bufAccessor = bufAccessor.get();
EXPECT_CALL(sock, getGSO()).WillRepeatedly(Return(1));
auto batchWriter = quic::BatchWriterFactory::makeBatchWriter(
sock,
quic::QuicBatchingMode::BATCHING_MODE_GSO,
batchSize,
useThreadLocal,
quic::kDefaultThreadLocalDelay,
DataPathType::ContinuousMemory,
conn_);
CHECK(batchWriter);
ASSERT_FALSE(batchWriter->needsFlush(1000));
auto buf = bufAccessor->obtain();
buf->append(1000);
bufAccessor->release(std::move(buf));
ASSERT_FALSE(
batchWriter->append(nullptr, 1000, folly::SocketAddress(), nullptr));
EXPECT_CALL(sock, write(_, _))
.Times(1)
.WillOnce(Invoke([&](const auto& /* addr */,
const std::unique_ptr<folly::IOBuf>& buf) {
EXPECT_EQ(1000, buf->length());
return 1000;
}));
EXPECT_EQ(1000, batchWriter->write(sock, folly::SocketAddress()));
EXPECT_TRUE(bufAccessor->ownsBuffer());
buf = bufAccessor->obtain();
EXPECT_EQ(0, buf->length());
}
TEST_P(QuicBatchWriterTest, InplaceWriterLastOneTooBig) {
bool useThreadLocal = GetParam();
folly::EventBase evb;
folly::test::MockAsyncUDPSocket sock(&evb);
uint32_t batchSize = 20;
auto bufAccessor =
std::make_unique<SimpleBufAccessor>(conn_.udpSendPacketLen * batchSize);
conn_.bufAccessor = bufAccessor.get();
EXPECT_CALL(sock, getGSO()).WillRepeatedly(Return(1));
auto batchWriter = quic::BatchWriterFactory::makeBatchWriter(
sock,
quic::QuicBatchingMode::BATCHING_MODE_GSO,
batchSize,
useThreadLocal,
quic::kDefaultThreadLocalDelay,
DataPathType::ContinuousMemory,
conn_);
for (size_t i = 0; i < 5; i++) {
auto buf = bufAccessor->obtain();
buf->append(700);
bufAccessor->release(std::move(buf));
ASSERT_FALSE(
batchWriter->append(nullptr, 700, folly::SocketAddress(), nullptr));
}
auto buf = bufAccessor->obtain();
buf->append(1000);
bufAccessor->release(std::move(buf));
EXPECT_TRUE(batchWriter->needsFlush(1000));
EXPECT_CALL(sock, writeGSO(_, _, _))
.Times(1)
.WillOnce(Invoke([&](const auto& /* addr */,
const std::unique_ptr<folly::IOBuf>& buf,
int gso) {
EXPECT_EQ(5 * 700, buf->length());
EXPECT_EQ(700, gso);
return 700 * 5;
}));
EXPECT_EQ(5 * 700, batchWriter->write(sock, folly::SocketAddress()));
EXPECT_TRUE(bufAccessor->ownsBuffer());
buf = bufAccessor->obtain();
EXPECT_EQ(1000, buf->length());
EXPECT_EQ(0, buf->headroom());
}
INSTANTIATE_TEST_CASE_P( INSTANTIATE_TEST_CASE_P(
QuicBatchWriterTest, QuicBatchWriterTest,
QuicBatchWriterTest, QuicBatchWriterTest,