blob: ea909e261fea19c06cdefb18a85381b183fce915 [file] [log] [blame]
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "test/scenario/network/network_emulation.h"
#include <memory>
#include "absl/memory/memory.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace test {
EmulatedIpPacket::EmulatedIpPacket(const rtc::SocketAddress& from,
const rtc::SocketAddress& to,
uint64_t dest_endpoint_id,
rtc::CopyOnWriteBuffer data,
Timestamp arrival_time)
: from(from),
to(to),
dest_endpoint_id(dest_endpoint_id),
data(data),
arrival_time(arrival_time) {}
EmulatedIpPacket::~EmulatedIpPacket() = default;
EmulatedIpPacket::EmulatedIpPacket(EmulatedIpPacket&&) = default;
void EmulatedNetworkNode::CreateRoute(
uint64_t receiver_id,
std::vector<EmulatedNetworkNode*> nodes,
EmulatedNetworkReceiverInterface* receiver) {
RTC_CHECK(!nodes.empty());
for (size_t i = 0; i + 1 < nodes.size(); ++i)
nodes[i]->SetReceiver(receiver_id, nodes[i + 1]);
nodes.back()->SetReceiver(receiver_id, receiver);
}
void EmulatedNetworkNode::ClearRoute(uint64_t receiver_id,
std::vector<EmulatedNetworkNode*> nodes) {
for (EmulatedNetworkNode* node : nodes)
node->RemoveReceiver(receiver_id);
}
EmulatedNetworkNode::EmulatedNetworkNode(
std::unique_ptr<NetworkBehaviorInterface> network_behavior,
size_t packet_overhead)
: network_behavior_(std::move(network_behavior)),
packet_overhead_(packet_overhead) {}
EmulatedNetworkNode::~EmulatedNetworkNode() = default;
void EmulatedNetworkNode::OnPacketReceived(EmulatedIpPacket packet) {
rtc::CritScope crit(&lock_);
if (routing_.find(packet.dest_endpoint_id) == routing_.end())
return;
uint64_t packet_id = next_packet_id_++;
bool sent = network_behavior_->EnqueuePacket(PacketInFlightInfo(
packet.size() + packet_overhead_, packet.arrival_time.us(), packet_id));
if (sent) {
packets_.emplace_back(StoredPacket{packet_id, std::move(packet), false});
}
}
void EmulatedNetworkNode::Process(Timestamp at_time) {
std::vector<PacketDeliveryInfo> delivery_infos;
{
rtc::CritScope crit(&lock_);
absl::optional<int64_t> delivery_us =
network_behavior_->NextDeliveryTimeUs();
if (delivery_us && *delivery_us > at_time.us())
return;
delivery_infos = network_behavior_->DequeueDeliverablePackets(at_time.us());
}
for (PacketDeliveryInfo& delivery_info : delivery_infos) {
StoredPacket* packet = nullptr;
EmulatedNetworkReceiverInterface* receiver = nullptr;
{
rtc::CritScope crit(&lock_);
for (auto& stored_packet : packets_) {
if (stored_packet.id == delivery_info.packet_id) {
packet = &stored_packet;
break;
}
}
RTC_CHECK(packet);
RTC_DCHECK(!packet->removed);
receiver = routing_[packet->packet.dest_endpoint_id];
packet->removed = true;
}
RTC_CHECK(receiver);
// We don't want to keep the lock here. Otherwise we would get a deadlock if
// the receiver tries to push a new packet.
packet->packet.arrival_time = Timestamp::us(delivery_info.receive_time_us);
receiver->OnPacketReceived(std::move(packet->packet));
{
rtc::CritScope crit(&lock_);
while (!packets_.empty() && packets_.front().removed) {
packets_.pop_front();
}
}
}
}
void EmulatedNetworkNode::SetReceiver(
uint64_t dest_endpoint_id,
EmulatedNetworkReceiverInterface* receiver) {
rtc::CritScope crit(&lock_);
RTC_CHECK(routing_
.insert(std::pair<uint64_t, EmulatedNetworkReceiverInterface*>(
dest_endpoint_id, receiver))
.second)
<< "Such routing already exists";
}
void EmulatedNetworkNode::RemoveReceiver(uint64_t dest_endpoint_id) {
rtc::CritScope crit(&lock_);
routing_.erase(dest_endpoint_id);
}
} // namespace test
} // namespace webrtc