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