| // Copyright (c) 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "quic/core/batch_writer/quic_sendmmsg_batch_writer.h" |
| |
| namespace quic { |
| |
| QuicSendmmsgBatchWriter::QuicSendmmsgBatchWriter( |
| std::unique_ptr<QuicBatchWriterBuffer> batch_buffer, |
| int fd) |
| : QuicUdpBatchWriter(std::move(batch_buffer), fd) {} |
| |
| QuicSendmmsgBatchWriter::CanBatchResult QuicSendmmsgBatchWriter::CanBatch( |
| const char* /*buffer*/, |
| size_t /*buf_len*/, |
| const QuicIpAddress& /*self_address*/, |
| const QuicSocketAddress& /*peer_address*/, |
| const PerPacketOptions* /*options*/, |
| uint64_t /*release_time*/) const { |
| return CanBatchResult(/*can_batch=*/true, /*must_flush=*/false); |
| } |
| |
| QuicSendmmsgBatchWriter::FlushImplResult QuicSendmmsgBatchWriter::FlushImpl() { |
| return InternalFlushImpl( |
| kCmsgSpaceForIp, |
| [](QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write) { |
| mhdr->SetIpInNextCmsg(i, buffered_write.self_address); |
| }); |
| } |
| |
| QuicSendmmsgBatchWriter::FlushImplResult |
| QuicSendmmsgBatchWriter::InternalFlushImpl(size_t cmsg_space, |
| const CmsgBuilder& cmsg_builder) { |
| QUICHE_DCHECK(!IsWriteBlocked()); |
| QUICHE_DCHECK(!buffered_writes().empty()); |
| |
| FlushImplResult result = {WriteResult(WRITE_STATUS_OK, 0), |
| /*num_packets_sent=*/0, /*bytes_written=*/0}; |
| WriteResult& write_result = result.write_result; |
| |
| auto first = buffered_writes().cbegin(); |
| const auto last = buffered_writes().cend(); |
| while (first != last) { |
| QuicMMsgHdr mhdr(first, last, cmsg_space, cmsg_builder); |
| |
| int num_packets_sent; |
| write_result = QuicLinuxSocketUtils::WriteMultiplePackets( |
| fd(), &mhdr, &num_packets_sent); |
| QUIC_DVLOG(1) << "WriteMultiplePackets sent " << num_packets_sent |
| << " out of " << mhdr.num_msgs() |
| << " packets. WriteResult=" << write_result; |
| |
| if (write_result.status != WRITE_STATUS_OK) { |
| QUICHE_DCHECK_EQ(0, num_packets_sent); |
| break; |
| } else if (num_packets_sent == 0) { |
| QUIC_BUG(quic_bug_10825_1) |
| << "WriteMultiplePackets returned OK, but no packets were sent."; |
| write_result = WriteResult(WRITE_STATUS_ERROR, EIO); |
| break; |
| } |
| |
| first += num_packets_sent; |
| |
| result.num_packets_sent += num_packets_sent; |
| result.bytes_written += write_result.bytes_written; |
| } |
| |
| // Call PopBufferedWrite() even if write_result.status is not WRITE_STATUS_OK, |
| // to deal with partial writes. |
| batch_buffer().PopBufferedWrite(result.num_packets_sent); |
| |
| if (write_result.status != WRITE_STATUS_OK) { |
| return result; |
| } |
| |
| QUIC_BUG_IF(quic_bug_12537_1, !buffered_writes().empty()) |
| << "All packets should have been written on a successful return"; |
| write_result.bytes_written = result.bytes_written; |
| return result; |
| } |
| |
| } // namespace quic |