1 // 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.
5 #include "net/tools/quic/test_tools/packet_dropping_test_writer.h"
9 #include "base/rand_util.h"
10 #include "net/tools/quic/quic_epoll_connection_helper.h"
11 #include "net/tools/quic/quic_socket_utils.h"
17 // An alarm that is scheduled if a blocked socket is simulated to indicate
18 // it's writable again.
19 class WriteUnblockedAlarm : public QuicAlarm::Delegate {
21 explicit WriteUnblockedAlarm(PacketDroppingTestWriter* writer)
24 virtual QuicTime OnAlarm() OVERRIDE {
25 DVLOG(1) << "Unblocking socket.";
26 writer_->OnCanWrite();
27 return QuicTime::Zero();
31 PacketDroppingTestWriter* writer_;
34 // An alarm that is scheduled every time a new packet is to be written at a
36 class DelayAlarm : public QuicAlarm::Delegate {
38 explicit DelayAlarm(PacketDroppingTestWriter* writer)
41 virtual QuicTime OnAlarm() OVERRIDE {
42 return writer_->ReleaseOldPackets();
46 PacketDroppingTestWriter* writer_;
49 PacketDroppingTestWriter::PacketDroppingTestWriter()
53 fake_packet_loss_percentage_(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()),
59 uint32 seed = base::RandInt(0, std::numeric_limits<int32>::max());
60 VLOG(1) << "Seeding packet loss with " << seed;
61 simple_random_.set_seed(seed);
64 PacketDroppingTestWriter::~PacketDroppingTestWriter() {}
66 void PacketDroppingTestWriter::Initialize(
67 QuicEpollConnectionHelper* helper,
68 Delegate* on_can_write) {
69 clock_ = helper->GetClock();
70 write_unblocked_alarm_.reset(
71 helper->CreateAlarm(new WriteUnblockedAlarm(this)));
73 helper->CreateAlarm(new DelayAlarm(this)));
74 on_can_write_.reset(on_can_write);
77 WriteResult PacketDroppingTestWriter::WritePacket(
80 const net::IPAddressNumber& self_address,
81 const net::IPEndPoint& peer_address) {
84 base::AutoLock locked(config_mutex_);
85 if (fake_packet_loss_percentage_ > 0 &&
86 simple_random_.RandUint64() % 100 <
87 static_cast<uint64>(fake_packet_loss_percentage_)) {
88 DVLOG(1) << "Dropping packet.";
89 return WriteResult(WRITE_STATUS_OK, buf_len);
91 if (fake_blocked_socket_percentage_ > 0 &&
92 simple_random_.RandUint64() % 100 <
93 static_cast<uint64>(fake_blocked_socket_percentage_)) {
94 CHECK(on_can_write_.get() != NULL);
95 DVLOG(1) << "Blocking socket.";
96 if (!write_unblocked_alarm_->IsSet()) {
97 // Set the alarm to fire immediately.
98 write_unblocked_alarm_->Set(clock_->ApproximateNow());
100 return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN);
103 if (!fake_packet_delay_.IsZero() || !fake_bandwidth_.IsZero()) {
104 if (buffer_size_ > 0 && buf_len + cur_buffer_size_ > buffer_size_) {
105 // Drop packets which do not fit into the buffer.
106 DVLOG(1) << "Dropping packet because the buffer is full.";
107 return WriteResult(WRITE_STATUS_OK, buf_len);
110 // Queue it to be sent.
111 QuicTime send_time = clock_->ApproximateNow().Add(fake_packet_delay_);
112 if (!fake_bandwidth_.IsZero()) {
113 // Calculate a time the bandwidth limit would impose.
114 QuicTime::Delta bandwidth_delay = QuicTime::Delta::FromMicroseconds(
115 (buf_len * kNumMicrosPerSecond) /
116 fake_bandwidth_.ToBytesPerSecond());
117 send_time = delayed_packets_.empty() ?
118 send_time.Add(bandwidth_delay) :
119 delayed_packets_.back().send_time.Add(bandwidth_delay);
121 delayed_packets_.push_back(DelayedWrite(buffer, buf_len, self_address,
122 peer_address, send_time));
123 cur_buffer_size_ += buf_len;
125 // Set the alarm if it's not yet set.
126 if (!delay_alarm_->IsSet()) {
127 delay_alarm_->Set(send_time);
130 return WriteResult(WRITE_STATUS_OK, buf_len);
133 return QuicPacketWriterWrapper::WritePacket(
134 buffer, buf_len, self_address, peer_address);
137 bool PacketDroppingTestWriter::IsWriteBlocked() const {
138 if (write_unblocked_alarm_.get() != NULL && write_unblocked_alarm_->IsSet()) {
141 return QuicPacketWriterWrapper::IsWriteBlocked();
144 void PacketDroppingTestWriter::SetWritable() {
145 if (write_unblocked_alarm_.get() != NULL && write_unblocked_alarm_->IsSet()) {
146 write_unblocked_alarm_->Cancel();
148 QuicPacketWriterWrapper::SetWritable();
151 QuicTime PacketDroppingTestWriter::ReleaseNextPacket() {
152 if (delayed_packets_.empty()) {
153 return QuicTime::Zero();
155 base::AutoLock locked(config_mutex_);
156 DelayedPacketList::iterator iter = delayed_packets_.begin();
157 // Determine if we should re-order.
158 if (delayed_packets_.size() > 1 && fake_packet_reorder_percentage_ > 0 &&
159 simple_random_.RandUint64() % 100 <
160 static_cast<uint64>(fake_packet_reorder_percentage_)) {
161 DVLOG(1) << "Reordering packets.";
163 // Swap the send times when re-ordering packets.
164 delayed_packets_.begin()->send_time = iter->send_time;
167 DVLOG(1) << "Releasing packet. " << (delayed_packets_.size() - 1)
169 // Grab the next one off the queue and send it.
170 QuicPacketWriterWrapper::WritePacket(
171 iter->buffer.data(), iter->buffer.length(),
172 iter->self_address, iter->peer_address);
173 DCHECK_GE(cur_buffer_size_, iter->buffer.length());
174 cur_buffer_size_ -= iter->buffer.length();
175 delayed_packets_.erase(iter);
177 // If there are others, find the time for the next to be sent.
178 if (delayed_packets_.empty()) {
179 return QuicTime::Zero();
181 return delayed_packets_.begin()->send_time;
184 QuicTime PacketDroppingTestWriter::ReleaseOldPackets() {
185 while (!delayed_packets_.empty()) {
186 QuicTime next_send_time = delayed_packets_.front().send_time;
187 if (next_send_time > clock_->Now()) {
188 return next_send_time;
192 return QuicTime::Zero();
195 void PacketDroppingTestWriter::OnCanWrite() {
196 on_can_write_->OnCanWrite();
199 PacketDroppingTestWriter::DelayedWrite::DelayedWrite(
202 const net::IPAddressNumber& self_address,
203 const net::IPEndPoint& peer_address,
205 : buffer(buffer, buf_len),
206 self_address(self_address),
207 peer_address(peer_address),
208 send_time(send_time) {}
210 PacketDroppingTestWriter::DelayedWrite::~DelayedWrite() {}