1 // Copyright (c) 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "quiche/quic/core/batch_writer/quic_sendmmsg_batch_writer.h"
6 
7 namespace quic {
8 
QuicSendmmsgBatchWriter(std::unique_ptr<QuicBatchWriterBuffer> batch_buffer,int fd)9 QuicSendmmsgBatchWriter::QuicSendmmsgBatchWriter(
10     std::unique_ptr<QuicBatchWriterBuffer> batch_buffer, int fd)
11     : QuicUdpBatchWriter(std::move(batch_buffer), fd) {}
12 
CanBatch(const char *,size_t,const QuicIpAddress &,const QuicSocketAddress &,const PerPacketOptions *,const QuicPacketWriterParams &,uint64_t) const13 QuicSendmmsgBatchWriter::CanBatchResult QuicSendmmsgBatchWriter::CanBatch(
14     const char* /*buffer*/, size_t /*buf_len*/,
15     const QuicIpAddress& /*self_address*/,
16     const QuicSocketAddress& /*peer_address*/,
17     const PerPacketOptions* /*options*/,
18     const QuicPacketWriterParams& /*params*/, uint64_t /*release_time*/) const {
19   return CanBatchResult(/*can_batch=*/true, /*must_flush=*/false);
20 }
21 
FlushImpl()22 QuicSendmmsgBatchWriter::FlushImplResult QuicSendmmsgBatchWriter::FlushImpl() {
23   return InternalFlushImpl(
24       kCmsgSpaceForIp,
25       [](QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write) {
26         mhdr->SetIpInNextCmsg(i, buffered_write.self_address);
27       });
28 }
29 
30 QuicSendmmsgBatchWriter::FlushImplResult
InternalFlushImpl(size_t cmsg_space,const CmsgBuilder & cmsg_builder)31 QuicSendmmsgBatchWriter::InternalFlushImpl(size_t cmsg_space,
32                                            const CmsgBuilder& cmsg_builder) {
33   QUICHE_DCHECK(!IsWriteBlocked());
34   QUICHE_DCHECK(!buffered_writes().empty());
35 
36   FlushImplResult result = {WriteResult(WRITE_STATUS_OK, 0),
37                             /*num_packets_sent=*/0, /*bytes_written=*/0};
38   WriteResult& write_result = result.write_result;
39 
40   auto first = buffered_writes().cbegin();
41   const auto last = buffered_writes().cend();
42   while (first != last) {
43     QuicMMsgHdr mhdr(first, last, cmsg_space, cmsg_builder);
44 
45     int num_packets_sent;
46     write_result = QuicLinuxSocketUtils::WriteMultiplePackets(
47         fd(), &mhdr, &num_packets_sent);
48     QUIC_DVLOG(1) << "WriteMultiplePackets sent " << num_packets_sent
49                   << " out of " << mhdr.num_msgs()
50                   << " packets. WriteResult=" << write_result;
51 
52     if (write_result.status != WRITE_STATUS_OK) {
53       QUICHE_DCHECK_EQ(0, num_packets_sent);
54       break;
55     } else if (num_packets_sent == 0) {
56       QUIC_BUG(quic_bug_10825_1)
57           << "WriteMultiplePackets returned OK, but no packets were sent.";
58       write_result = WriteResult(WRITE_STATUS_ERROR, EIO);
59       break;
60     }
61 
62     first += num_packets_sent;
63 
64     result.num_packets_sent += num_packets_sent;
65     result.bytes_written += write_result.bytes_written;
66   }
67 
68   // Call PopBufferedWrite() even if write_result.status is not WRITE_STATUS_OK,
69   // to deal with partial writes.
70   batch_buffer().PopBufferedWrite(result.num_packets_sent);
71 
72   if (write_result.status != WRITE_STATUS_OK) {
73     return result;
74   }
75 
76   QUIC_BUG_IF(quic_bug_12537_1, !buffered_writes().empty())
77       << "All packets should have been written on a successful return";
78   write_result.bytes_written = result.bytes_written;
79   return result;
80 }
81 
82 }  // namespace quic
83