Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2435)

Unified Diff: remoting/test/fake_socket_factory.cc

Issue 427613005: Implement network performance simulation for remoting perf tests. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « remoting/test/fake_socket_factory.h ('k') | remoting/test/leaky_bucket.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: remoting/test/fake_socket_factory.cc
diff --git a/remoting/test/fake_socket_factory.cc b/remoting/test/fake_socket_factory.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c9b927d3afe79cd9bdb054c10edb970536a77b4a
--- /dev/null
+++ b/remoting/test/fake_socket_factory.cc
@@ -0,0 +1,328 @@
+// Copyright 2014 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.
+
+// MSVC++ requires this to be set before any other includes to get M_PI.
+#define _USE_MATH_DEFINES
+
+#include "remoting/test/fake_socket_factory.h"
+
+#include <math.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/rand_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "net/base/io_buffer.h"
+#include "remoting/test/leaky_bucket.h"
+#include "third_party/webrtc/base/asyncpacketsocket.h"
+
+namespace remoting {
+
+namespace {
+
+const int kPortRangeStart = 1024;
+const int kPortRangeEnd = 65535;
+
+double GetNormalRandom(double average, double stddev) {
+ // Based on Box-Muller transform, see
+ // http://en.wikipedia.org/wiki/Box_Muller_transform .
+ return average +
+ stddev * sqrt(-2.0 * log(1.0 - base::RandDouble())) *
+ cos(base::RandDouble() * 2.0 * M_PI);
+}
+
+class FakeUdpSocket : public rtc::AsyncPacketSocket {
+ public:
+ FakeUdpSocket(FakePacketSocketFactory* factory,
+ scoped_refptr<FakeNetworkDispatcher> dispatcher,
+ const rtc::SocketAddress& local_address);
+ virtual ~FakeUdpSocket();
+
+ void ReceivePacket(const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ const scoped_refptr<net::IOBuffer>& data,
+ int data_size);
+
+ // rtc::AsyncPacketSocket interface.
+ virtual rtc::SocketAddress GetLocalAddress() const OVERRIDE;
+ virtual rtc::SocketAddress GetRemoteAddress() const OVERRIDE;
+ virtual int Send(const void* data, size_t data_size,
+ const rtc::PacketOptions& options) OVERRIDE;
+ virtual int SendTo(const void* data, size_t data_size,
+ const rtc::SocketAddress& address,
+ const rtc::PacketOptions& options) OVERRIDE;
+ virtual int Close() OVERRIDE;
+ virtual State GetState() const OVERRIDE;
+ virtual int GetOption(rtc::Socket::Option option, int* value) OVERRIDE;
+ virtual int SetOption(rtc::Socket::Option option, int value) OVERRIDE;
+ virtual int GetError() const OVERRIDE;
+ virtual void SetError(int error) OVERRIDE;
+
+ private:
+ FakePacketSocketFactory* factory_;
+ scoped_refptr<FakeNetworkDispatcher> dispatcher_;
+ rtc::SocketAddress local_address_;
+ State state_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeUdpSocket);
+};
+
+FakeUdpSocket::FakeUdpSocket(FakePacketSocketFactory* factory,
+ scoped_refptr<FakeNetworkDispatcher> dispatcher,
+ const rtc::SocketAddress& local_address)
+ : factory_(factory),
+ dispatcher_(dispatcher),
+ local_address_(local_address),
+ state_(STATE_BOUND) {
+}
+
+FakeUdpSocket::~FakeUdpSocket() {
+ factory_->OnSocketDestroyed(local_address_.port());
+}
+
+void FakeUdpSocket::ReceivePacket(const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ const scoped_refptr<net::IOBuffer>& data,
+ int data_size) {
+ SignalReadPacket(
+ this, data->data(), data_size, from, rtc::CreatePacketTime(0));
+}
+
+rtc::SocketAddress FakeUdpSocket::GetLocalAddress() const {
+ return local_address_;
+}
+
+rtc::SocketAddress FakeUdpSocket::GetRemoteAddress() const {
+ NOTREACHED();
+ return rtc::SocketAddress();
+}
+
+int FakeUdpSocket::Send(const void* data, size_t data_size,
+ const rtc::PacketOptions& options) {
+ NOTREACHED();
+ return EINVAL;
+}
+
+int FakeUdpSocket::SendTo(const void* data, size_t data_size,
+ const rtc::SocketAddress& address,
+ const rtc::PacketOptions& options) {
+ scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(data_size);
+ memcpy(buffer->data(), data, data_size);
+ dispatcher_->DeliverPacket(local_address_, address, buffer, data_size);
+ return data_size;
+}
+
+int FakeUdpSocket::Close() {
+ state_ = STATE_CLOSED;
+ return 0;
+}
+
+rtc::AsyncPacketSocket::State FakeUdpSocket::GetState() const {
+ return state_;
+}
+
+int FakeUdpSocket::GetOption(rtc::Socket::Option option, int* value) {
+ NOTIMPLEMENTED();
+ return -1;
+}
+
+int FakeUdpSocket::SetOption(rtc::Socket::Option option, int value) {
+ NOTIMPLEMENTED();
+ return -1;
+}
+
+int FakeUdpSocket::GetError() const {
+ return 0;
+}
+
+void FakeUdpSocket::SetError(int error) {
+ NOTREACHED();
+}
+
+} // namespace
+
+FakePacketSocketFactory::PendingPacket::PendingPacket()
+ : data_size(0) {
+}
+
+FakePacketSocketFactory::PendingPacket::PendingPacket(
+ const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ const scoped_refptr<net::IOBuffer>& data,
+ int data_size)
+ : from(from), to(to), data(data), data_size(data_size) {
+}
+
+FakePacketSocketFactory::PendingPacket::~PendingPacket() {
+}
+
+FakePacketSocketFactory::FakePacketSocketFactory(
+ FakeNetworkDispatcher* dispatcher)
+ : task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ dispatcher_(dispatcher),
+ address_(dispatcher_->AllocateAddress()),
+ out_of_order_rate_(0.0),
+ next_port_(kPortRangeStart),
+ weak_factory_(this) {
+ dispatcher_->AddNode(this);
+}
+
+FakePacketSocketFactory::~FakePacketSocketFactory() {
+ CHECK(udp_sockets_.empty());
+ dispatcher_->RemoveNode(this);
+}
+
+void FakePacketSocketFactory::OnSocketDestroyed(int port) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ udp_sockets_.erase(port);
+}
+
+void FakePacketSocketFactory::SetBandwidth(int bandwidth, int max_buffer) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ if (bandwidth <= 0) {
+ leaky_bucket_.reset();
+ } else {
+ leaky_bucket_.reset(new LeakyBucket(max_buffer, bandwidth));
+ }
+}
+
+void FakePacketSocketFactory::SetLatency(base::TimeDelta average,
+ base::TimeDelta stddev) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ latency_average_ = average;
+ latency_stddev_ = stddev;
+}
+
+rtc::AsyncPacketSocket* FakePacketSocketFactory::CreateUdpSocket(
+ const rtc::SocketAddress& local_address,
+ int min_port, int max_port) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ int port = -1;
+ if (min_port > 0 && max_port > 0) {
+ for (int i = min_port; i <= max_port; ++i) {
+ if (udp_sockets_.find(i) == udp_sockets_.end()) {
+ port = i;
+ break;
+ }
+ }
+ if (port < 0)
+ return NULL;
+ } else {
+ do {
+ port = next_port_;
+ next_port_ =
+ (next_port_ >= kPortRangeEnd) ? kPortRangeStart : (next_port_ + 1);
+ } while (udp_sockets_.find(port) != udp_sockets_.end());
+ }
+
+ CHECK(local_address.ipaddr() == address_);
+
+ FakeUdpSocket* result =
+ new FakeUdpSocket(this, dispatcher_,
+ rtc::SocketAddress(local_address.ipaddr(), port));
+
+ udp_sockets_[port] =
+ base::Bind(&FakeUdpSocket::ReceivePacket, base::Unretained(result));
+
+ return result;
+}
+
+rtc::AsyncPacketSocket* FakePacketSocketFactory::CreateServerTcpSocket(
+ const rtc::SocketAddress& local_address,
+ int min_port, int max_port,
+ int opts) {
+ return NULL;
+}
+
+rtc::AsyncPacketSocket* FakePacketSocketFactory::CreateClientTcpSocket(
+ const rtc::SocketAddress& local_address,
+ const rtc::SocketAddress& remote_address,
+ const rtc::ProxyInfo& proxy_info,
+ const std::string& user_agent,
+ int opts) {
+ return NULL;
+}
+
+rtc::AsyncResolverInterface*
+FakePacketSocketFactory::CreateAsyncResolver() {
+ return NULL;
+}
+
+const scoped_refptr<base::SingleThreadTaskRunner>&
+FakePacketSocketFactory::GetThread() const {
+ return task_runner_;
+}
+
+const rtc::IPAddress& FakePacketSocketFactory::GetAddress() const {
+ return address_;
+}
+
+void FakePacketSocketFactory::ReceivePacket(
+ const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ const scoped_refptr<net::IOBuffer>& data,
+ int data_size) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ DCHECK(to.ipaddr() == address_);
+
+ base::TimeDelta delay;
+
+ if (leaky_bucket_) {
+ delay = leaky_bucket_->AddPacket(data_size);
+ if (delay.is_max()) {
+ // Drop the packet.
+ return;
+ }
+ }
+
+ if (latency_average_ > base::TimeDelta()) {
+ delay += base::TimeDelta::FromMillisecondsD(
+ GetNormalRandom(latency_average_.InMillisecondsF(),
+ latency_stddev_.InMillisecondsF()));
+ }
+ if (delay < base::TimeDelta())
+ delay = base::TimeDelta();
+
+ // Put the packet to the |pending_packets_| and post a task for
+ // DoReceivePackets(). Note that the DoReceivePackets() task posted here may
+ // deliver a different packet, not the one added to the queue here. This
+ // would happen if another task gets posted with a shorted delay or when
+ // |out_of_order_rate_| is greater than 0. It's implemented this way to
+ // decouple latency variability from out-of-order delivery.
+ PendingPacket packet(from, to, data, data_size);
+ pending_packets_.push_back(packet);
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&FakePacketSocketFactory::DoReceivePacket,
+ weak_factory_.GetWeakPtr()),
+ delay);
+}
+
+void FakePacketSocketFactory::DoReceivePacket() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ PendingPacket packet;
+ if (pending_packets_.size() > 1 && base::RandDouble() < out_of_order_rate_) {
+ std::list<PendingPacket>::iterator it = pending_packets_.begin();
+ ++it;
+ packet = *it;
+ pending_packets_.erase(it);
+ } else {
+ packet = pending_packets_.front();
+ pending_packets_.pop_front();
+ }
+
+ UdpSocketsMap::iterator iter = udp_sockets_.find(packet.to.port());
+ if (iter == udp_sockets_.end()) {
+ // Invalid port number.
+ return;
+ }
+
+ iter->second.Run(packet.from, packet.to, packet.data, packet.data_size);
+}
+
+} // namespace remoting
« no previous file with comments | « remoting/test/fake_socket_factory.h ('k') | remoting/test/leaky_bucket.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698