blob: 87935d5c013530fd07b35cac7df73f708b5d3dec [file] [log] [blame]
QUICHE teama6ef0a62019-03-07 20:34:33 -05001// Copyright 2013 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 "net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h"
6
7#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
8#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
9#include "net/quic/platform/impl/quic_socket_utils.h"
10
11namespace quic {
12namespace test {
13
14const int32_t kMaxConsecutivePacketLoss = 3;
15
16// An alarm that is scheduled if a blocked socket is simulated to indicate
17// it's writable again.
18class WriteUnblockedAlarm : public QuicAlarm::Delegate {
19 public:
20 explicit WriteUnblockedAlarm(PacketDroppingTestWriter* writer)
21 : writer_(writer) {}
22
23 void OnAlarm() override {
24 QUIC_DLOG(INFO) << "Unblocking socket.";
25 writer_->OnCanWrite();
26 }
27
28 private:
29 PacketDroppingTestWriter* writer_;
30};
31
32// An alarm that is scheduled every time a new packet is to be written at a
33// later point.
34class DelayAlarm : public QuicAlarm::Delegate {
35 public:
36 explicit DelayAlarm(PacketDroppingTestWriter* writer) : writer_(writer) {}
37
38 void OnAlarm() override {
39 QuicTime new_deadline = writer_->ReleaseOldPackets();
40 if (new_deadline.IsInitialized()) {
41 writer_->SetDelayAlarm(new_deadline);
42 }
43 }
44
45 private:
46 PacketDroppingTestWriter* writer_;
47};
48
49PacketDroppingTestWriter::PacketDroppingTestWriter()
50 : clock_(nullptr),
51 cur_buffer_size_(0),
52 num_calls_to_write_(0),
53 fake_packet_loss_percentage_(0),
54 fake_drop_first_n_packets_(0),
55 fake_blocked_socket_percentage_(0),
56 fake_packet_reorder_percentage_(0),
57 fake_packet_delay_(QuicTime::Delta::Zero()),
58 fake_bandwidth_(QuicBandwidth::Zero()),
59 buffer_size_(0),
60 num_consecutive_packet_lost_(0) {
61 uint64_t seed = QuicRandom::GetInstance()->RandUint64();
62 QUIC_LOG(INFO) << "Seeding packet loss with " << seed;
63 simple_random_.set_seed(seed);
64}
65
66PacketDroppingTestWriter::~PacketDroppingTestWriter() = default;
67
68void PacketDroppingTestWriter::Initialize(
69 QuicConnectionHelperInterface* helper,
70 QuicAlarmFactory* alarm_factory,
71 std::unique_ptr<Delegate> on_can_write) {
72 clock_ = helper->GetClock();
73 write_unblocked_alarm_.reset(
74 alarm_factory->CreateAlarm(new WriteUnblockedAlarm(this)));
75 delay_alarm_.reset(alarm_factory->CreateAlarm(new DelayAlarm(this)));
76 on_can_write_ = std::move(on_can_write);
77}
78
79WriteResult PacketDroppingTestWriter::WritePacket(
80 const char* buffer,
81 size_t buf_len,
82 const QuicIpAddress& self_address,
83 const QuicSocketAddress& peer_address,
84 PerPacketOptions* options) {
85 ++num_calls_to_write_;
86 ReleaseOldPackets();
87
88 QuicWriterMutexLock lock(&config_mutex_);
89 if (fake_drop_first_n_packets_ > 0 &&
90 num_calls_to_write_ <=
91 static_cast<uint64_t>(fake_drop_first_n_packets_)) {
92 QUIC_DVLOG(1) << "Dropping first " << fake_drop_first_n_packets_
93 << " packets (packet number " << num_calls_to_write_ << ")";
94 return WriteResult(WRITE_STATUS_OK, buf_len);
95 }
96 const int32_t kMaxPacketLossPercentage =
97 kMaxConsecutivePacketLoss * 100.0 / (kMaxConsecutivePacketLoss + 1);
98 if (fake_packet_loss_percentage_ > 0 &&
99 // Do not allow too many consecutive packet drops to avoid test flakiness.
100 (num_consecutive_packet_lost_ <= kMaxConsecutivePacketLoss ||
101 // Allow as many consecutive packet drops as possbile if
102 // |fake_packet_lost_percentage_| is large enough. Without this exception
103 // it is hard to simulate high loss rate, like 100%.
104 fake_packet_loss_percentage_ > kMaxPacketLossPercentage) &&
105 (simple_random_.RandUint64() % 100 <
106 static_cast<uint64_t>(fake_packet_loss_percentage_))) {
107 QUIC_DVLOG(1) << "Dropping packet.";
108 ++num_consecutive_packet_lost_;
109 return WriteResult(WRITE_STATUS_OK, buf_len);
110 } else {
111 num_consecutive_packet_lost_ = 0;
112 }
113 if (fake_blocked_socket_percentage_ > 0 &&
114 simple_random_.RandUint64() % 100 <
115 static_cast<uint64_t>(fake_blocked_socket_percentage_)) {
116 CHECK(on_can_write_ != nullptr);
117 QUIC_DVLOG(1) << "Blocking socket.";
118 if (!write_unblocked_alarm_->IsSet()) {
119 // Set the alarm to fire immediately.
120 write_unblocked_alarm_->Set(clock_->ApproximateNow());
121 }
122 return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN);
123 }
124
125 if (!fake_packet_delay_.IsZero() || !fake_bandwidth_.IsZero()) {
126 if (buffer_size_ > 0 && buf_len + cur_buffer_size_ > buffer_size_) {
127 // Drop packets which do not fit into the buffer.
128 QUIC_DVLOG(1) << "Dropping packet because the buffer is full.";
129 return WriteResult(WRITE_STATUS_OK, buf_len);
130 }
131
132 // Queue it to be sent.
133 QuicTime send_time = clock_->ApproximateNow() + fake_packet_delay_;
134 if (!fake_bandwidth_.IsZero()) {
135 // Calculate a time the bandwidth limit would impose.
136 QuicTime::Delta bandwidth_delay = QuicTime::Delta::FromMicroseconds(
137 (buf_len * kNumMicrosPerSecond) / fake_bandwidth_.ToBytesPerSecond());
138 send_time = delayed_packets_.empty()
139 ? send_time + bandwidth_delay
140 : delayed_packets_.back().send_time + bandwidth_delay;
141 }
142 std::unique_ptr<PerPacketOptions> delayed_options;
143 if (options != nullptr) {
144 delayed_options = options->Clone();
145 }
146 delayed_packets_.push_back(
147 DelayedWrite(buffer, buf_len, self_address, peer_address,
148 std::move(delayed_options), send_time));
149 cur_buffer_size_ += buf_len;
150
151 // Set the alarm if it's not yet set.
152 if (!delay_alarm_->IsSet()) {
153 delay_alarm_->Set(send_time);
154 }
155
156 return WriteResult(WRITE_STATUS_OK, buf_len);
157 }
158
159 return QuicPacketWriterWrapper::WritePacket(buffer, buf_len, self_address,
160 peer_address, options);
161}
162
163bool PacketDroppingTestWriter::IsWriteBlocked() const {
164 if (write_unblocked_alarm_ != nullptr && write_unblocked_alarm_->IsSet()) {
165 return true;
166 }
167 return QuicPacketWriterWrapper::IsWriteBlocked();
168}
169
170void PacketDroppingTestWriter::SetWritable() {
171 if (write_unblocked_alarm_ != nullptr && write_unblocked_alarm_->IsSet()) {
172 write_unblocked_alarm_->Cancel();
173 }
174 QuicPacketWriterWrapper::SetWritable();
175}
176
177QuicTime PacketDroppingTestWriter::ReleaseNextPacket() {
178 if (delayed_packets_.empty()) {
179 return QuicTime::Zero();
180 }
181 QuicReaderMutexLock lock(&config_mutex_);
182 auto iter = delayed_packets_.begin();
183 // Determine if we should re-order.
184 if (delayed_packets_.size() > 1 && fake_packet_reorder_percentage_ > 0 &&
185 simple_random_.RandUint64() % 100 <
186 static_cast<uint64_t>(fake_packet_reorder_percentage_)) {
187 QUIC_DLOG(INFO) << "Reordering packets.";
188 ++iter;
189 // Swap the send times when re-ordering packets.
190 delayed_packets_.begin()->send_time = iter->send_time;
191 }
192
193 QUIC_DVLOG(1) << "Releasing packet. " << (delayed_packets_.size() - 1)
194 << " remaining.";
195 // Grab the next one off the queue and send it.
196 QuicPacketWriterWrapper::WritePacket(
197 iter->buffer.data(), iter->buffer.length(), iter->self_address,
198 iter->peer_address, iter->options.get());
199 DCHECK_GE(cur_buffer_size_, iter->buffer.length());
200 cur_buffer_size_ -= iter->buffer.length();
201 delayed_packets_.erase(iter);
202
203 // If there are others, find the time for the next to be sent.
204 if (delayed_packets_.empty()) {
205 return QuicTime::Zero();
206 }
207 return delayed_packets_.begin()->send_time;
208}
209
210QuicTime PacketDroppingTestWriter::ReleaseOldPackets() {
211 while (!delayed_packets_.empty()) {
212 QuicTime next_send_time = delayed_packets_.front().send_time;
213 if (next_send_time > clock_->Now()) {
214 return next_send_time;
215 }
216 ReleaseNextPacket();
217 }
218 return QuicTime::Zero();
219}
220
221void PacketDroppingTestWriter::SetDelayAlarm(QuicTime new_deadline) {
222 delay_alarm_->Set(new_deadline);
223}
224
225void PacketDroppingTestWriter::OnCanWrite() {
226 on_can_write_->OnCanWrite();
227}
228
229void PacketDroppingTestWriter::set_fake_packet_loss_percentage(
230 int32_t fake_packet_loss_percentage) {
231 QuicWriterMutexLock lock(&config_mutex_);
232 fake_packet_loss_percentage_ = fake_packet_loss_percentage;
233 num_consecutive_packet_lost_ = 0;
234}
235
236PacketDroppingTestWriter::DelayedWrite::DelayedWrite(
237 const char* buffer,
238 size_t buf_len,
239 const QuicIpAddress& self_address,
240 const QuicSocketAddress& peer_address,
241 std::unique_ptr<PerPacketOptions> options,
242 QuicTime send_time)
243 : buffer(buffer, buf_len),
244 self_address(self_address),
245 peer_address(peer_address),
246 options(std::move(options)),
247 send_time(send_time) {}
248
249PacketDroppingTestWriter::DelayedWrite::~DelayedWrite() = default;
250
251} // namespace test
252} // namespace quic