mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 19:59:28 +02:00
Merge pull request #34 from birarda/atp
fixes for ordered sending via UDT, asset system
This commit is contained in:
commit
16d082e7c8
16 changed files with 155 additions and 70 deletions
|
@ -42,8 +42,8 @@ const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000;
|
||||||
int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
|
int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
|
||||||
|
|
||||||
AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool,
|
AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool,
|
||||||
QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort,
|
quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
|
||||||
quint16 assignmentMonitorPort) :
|
quint16 assignmentServerPort, quint16 assignmentMonitorPort) :
|
||||||
_assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME)
|
_assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME)
|
||||||
{
|
{
|
||||||
LogUtils::init();
|
LogUtils::init();
|
||||||
|
@ -53,7 +53,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
||||||
auto addressManager = DependencyManager::set<AddressManager>();
|
auto addressManager = DependencyManager::set<AddressManager>();
|
||||||
|
|
||||||
// create a NodeList as an unassigned client, must be after addressManager
|
// create a NodeList as an unassigned client, must be after addressManager
|
||||||
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned);
|
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned, listenPort);
|
||||||
|
|
||||||
auto animationCache = DependencyManager::set<AnimationCache>();
|
auto animationCache = DependencyManager::set<AnimationCache>();
|
||||||
auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>();
|
auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>();
|
||||||
|
|
|
@ -23,6 +23,7 @@ class AssignmentClient : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool,
|
AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool,
|
||||||
|
quint16 listenPort,
|
||||||
QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort,
|
QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort,
|
||||||
quint16 assignmentMonitorPort);
|
quint16 assignmentMonitorPort);
|
||||||
~AssignmentClient();
|
~AssignmentClient();
|
||||||
|
|
|
@ -59,6 +59,10 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
||||||
|
|
||||||
const QCommandLineOption poolOption(ASSIGNMENT_POOL_OPTION, "set assignment pool", "pool-name");
|
const QCommandLineOption poolOption(ASSIGNMENT_POOL_OPTION, "set assignment pool", "pool-name");
|
||||||
parser.addOption(poolOption);
|
parser.addOption(poolOption);
|
||||||
|
|
||||||
|
const QCommandLineOption portOption(ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION,
|
||||||
|
"UDP port for this assignment client (or monitor)", "port");
|
||||||
|
parser.addOption(portOption);
|
||||||
|
|
||||||
const QCommandLineOption walletDestinationOption(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION,
|
const QCommandLineOption walletDestinationOption(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION,
|
||||||
"set wallet destination", "wallet-uuid");
|
"set wallet destination", "wallet-uuid");
|
||||||
|
@ -158,12 +162,18 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
||||||
// check for an overriden assignment server port
|
// check for an overriden assignment server port
|
||||||
quint16 assignmentServerPort = DEFAULT_DOMAIN_SERVER_PORT;
|
quint16 assignmentServerPort = DEFAULT_DOMAIN_SERVER_PORT;
|
||||||
if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) {
|
if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) {
|
||||||
assignmentServerPort = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toString().toUInt();
|
assignmentServerPort = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toUInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parser.isSet(assignmentServerPortOption)) {
|
if (parser.isSet(assignmentServerPortOption)) {
|
||||||
assignmentServerPort = parser.value(assignmentServerPortOption).toInt();
|
assignmentServerPort = parser.value(assignmentServerPortOption).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for an overidden listen port
|
||||||
|
quint16 listenPort = 0;
|
||||||
|
if (argumentVariantMap.contains(ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION)) {
|
||||||
|
listenPort = argumentVariantMap.value(ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION).toUInt();
|
||||||
|
}
|
||||||
|
|
||||||
if (parser.isSet(numChildsOption)) {
|
if (parser.isSet(numChildsOption)) {
|
||||||
if (minForks && minForks > numForks) {
|
if (minForks && minForks > numForks) {
|
||||||
|
@ -185,12 +195,12 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
||||||
if (numForks || minForks || maxForks) {
|
if (numForks || minForks || maxForks) {
|
||||||
AssignmentClientMonitor* monitor = new AssignmentClientMonitor(numForks, minForks, maxForks,
|
AssignmentClientMonitor* monitor = new AssignmentClientMonitor(numForks, minForks, maxForks,
|
||||||
requestAssignmentType, assignmentPool,
|
requestAssignmentType, assignmentPool,
|
||||||
walletUUID, assignmentServerHostname,
|
listenPort, walletUUID, assignmentServerHostname,
|
||||||
assignmentServerPort);
|
assignmentServerPort);
|
||||||
monitor->setParent(this);
|
monitor->setParent(this);
|
||||||
connect(this, &QCoreApplication::aboutToQuit, monitor, &AssignmentClientMonitor::aboutToQuit);
|
connect(this, &QCoreApplication::aboutToQuit, monitor, &AssignmentClientMonitor::aboutToQuit);
|
||||||
} else {
|
} else {
|
||||||
AssignmentClient* client = new AssignmentClient(requestAssignmentType, assignmentPool,
|
AssignmentClient* client = new AssignmentClient(requestAssignmentType, assignmentPool, listenPort,
|
||||||
walletUUID, assignmentServerHostname,
|
walletUUID, assignmentServerHostname,
|
||||||
assignmentServerPort, monitorPort);
|
assignmentServerPort, monitorPort);
|
||||||
client->setParent(this);
|
client->setParent(this);
|
||||||
|
|
|
@ -17,15 +17,15 @@
|
||||||
|
|
||||||
const QString ASSIGNMENT_TYPE_OVERRIDE_OPTION = "t";
|
const QString ASSIGNMENT_TYPE_OVERRIDE_OPTION = "t";
|
||||||
const QString ASSIGNMENT_POOL_OPTION = "pool";
|
const QString ASSIGNMENT_POOL_OPTION = "pool";
|
||||||
|
const QString ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION = "p";
|
||||||
const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "wallet";
|
const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "wallet";
|
||||||
const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "a";
|
const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "i";
|
||||||
const QString CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION = "p";
|
const QString CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION = "server-port";
|
||||||
const QString ASSIGNMENT_NUM_FORKS_OPTION = "n";
|
const QString ASSIGNMENT_NUM_FORKS_OPTION = "n";
|
||||||
const QString ASSIGNMENT_MIN_FORKS_OPTION = "min";
|
const QString ASSIGNMENT_MIN_FORKS_OPTION = "min";
|
||||||
const QString ASSIGNMENT_MAX_FORKS_OPTION = "max";
|
const QString ASSIGNMENT_MAX_FORKS_OPTION = "max";
|
||||||
const QString ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION = "monitor-port";
|
const QString ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION = "monitor-port";
|
||||||
|
|
||||||
|
|
||||||
class AssignmentClientApp : public QCoreApplication {
|
class AssignmentClientApp : public QCoreApplication {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -28,7 +28,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
|
||||||
const unsigned int minAssignmentClientForks,
|
const unsigned int minAssignmentClientForks,
|
||||||
const unsigned int maxAssignmentClientForks,
|
const unsigned int maxAssignmentClientForks,
|
||||||
Assignment::Type requestAssignmentType, QString assignmentPool,
|
Assignment::Type requestAssignmentType, QString assignmentPool,
|
||||||
QUuid walletUUID, QString assignmentServerHostname,
|
quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
|
||||||
quint16 assignmentServerPort) :
|
quint16 assignmentServerPort) :
|
||||||
_numAssignmentClientForks(numAssignmentClientForks),
|
_numAssignmentClientForks(numAssignmentClientForks),
|
||||||
_minAssignmentClientForks(minAssignmentClientForks),
|
_minAssignmentClientForks(minAssignmentClientForks),
|
||||||
|
@ -50,7 +50,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
|
||||||
// create a NodeList so we can receive stats from children
|
// create a NodeList so we can receive stats from children
|
||||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||||
auto addressManager = DependencyManager::set<AddressManager>();
|
auto addressManager = DependencyManager::set<AddressManager>();
|
||||||
auto nodeList = DependencyManager::set<LimitedNodeList>();
|
auto nodeList = DependencyManager::set<LimitedNodeList>(listenPort);
|
||||||
|
|
||||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||||
packetReceiver.registerListener(PacketType::AssignmentClientStatus, this, "handleChildStatusPacket");
|
packetReceiver.registerListener(PacketType::AssignmentClientStatus, this, "handleChildStatusPacket");
|
||||||
|
|
|
@ -28,7 +28,7 @@ class AssignmentClientMonitor : public QObject {
|
||||||
public:
|
public:
|
||||||
AssignmentClientMonitor(const unsigned int numAssignmentClientForks, const unsigned int minAssignmentClientForks,
|
AssignmentClientMonitor(const unsigned int numAssignmentClientForks, const unsigned int minAssignmentClientForks,
|
||||||
const unsigned int maxAssignmentClientForks, Assignment::Type requestAssignmentType,
|
const unsigned int maxAssignmentClientForks, Assignment::Type requestAssignmentType,
|
||||||
QString assignmentPool, QUuid walletUUID, QString assignmentServerHostname,
|
QString assignmentPool, quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
|
||||||
quint16 assignmentServerPort);
|
quint16 assignmentServerPort);
|
||||||
~AssignmentClientMonitor();
|
~AssignmentClientMonitor();
|
||||||
|
|
||||||
|
|
|
@ -15,14 +15,20 @@
|
||||||
|
|
||||||
void FileResourceRequest::doSend() {
|
void FileResourceRequest::doSend() {
|
||||||
QString filename = _url.toLocalFile();
|
QString filename = _url.toLocalFile();
|
||||||
|
|
||||||
QFile file(filename);
|
QFile file(filename);
|
||||||
|
|
||||||
_state = Finished;
|
_state = Finished;
|
||||||
if (file.open(QFile::ReadOnly)) {
|
if (file.exists()) {
|
||||||
_data = file.readAll();
|
if (file.open(QFile::ReadOnly)) {
|
||||||
_result = ResourceRequest::Success;
|
_data = file.readAll();
|
||||||
emit finished();
|
_result = ResourceRequest::Success;
|
||||||
|
} else {
|
||||||
|
_result = ResourceRequest::AccessDenied;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
_result = ResourceRequest::AccessDenied;
|
_result = ResourceRequest::NotFound;
|
||||||
emit finished();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTTPResourceRequest::onTimeout() {
|
void HTTPResourceRequest::onTimeout() {
|
||||||
Q_ASSERT(_state != Unsent);
|
Q_ASSERT(_state != NotStarted);
|
||||||
|
|
||||||
if (_state == InProgress) {
|
if (_state == InProgress) {
|
||||||
qCDebug(networking) << "Timed out loading " << _url;
|
qCDebug(networking) << "Timed out loading " << _url;
|
||||||
|
|
|
@ -17,7 +17,7 @@ ResourceRequest::ResourceRequest(QObject* parent, const QUrl& url) :
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceRequest::send() {
|
void ResourceRequest::send() {
|
||||||
Q_ASSERT(_state == Unsent);
|
Q_ASSERT(_state == NotStarted);
|
||||||
|
|
||||||
_state = InProgress;
|
_state = InProgress;
|
||||||
doSend();
|
doSend();
|
||||||
|
|
|
@ -21,7 +21,7 @@ public:
|
||||||
ResourceRequest(QObject* parent, const QUrl& url);
|
ResourceRequest(QObject* parent, const QUrl& url);
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
Unsent = 0,
|
NotStarted = 0,
|
||||||
InProgress,
|
InProgress,
|
||||||
Finished
|
Finished
|
||||||
};
|
};
|
||||||
|
@ -51,7 +51,7 @@ protected:
|
||||||
virtual void doSend() = 0;
|
virtual void doSend() = 0;
|
||||||
|
|
||||||
QUrl _url;
|
QUrl _url;
|
||||||
State _state { Unsent };
|
State _state { NotStarted };
|
||||||
Result _result;
|
Result _result;
|
||||||
QByteArray _data;
|
QByteArray _data;
|
||||||
bool _cacheEnabled { true };
|
bool _cacheEnabled { true };
|
||||||
|
|
|
@ -78,8 +78,9 @@ SendQueue& Connection::getSendQueue() {
|
||||||
QObject::connect(_sendQueue.get(), &SendQueue::packetRetransmitted, this, &Connection::recordRetransmission);
|
QObject::connect(_sendQueue.get(), &SendQueue::packetRetransmitted, this, &Connection::recordRetransmission);
|
||||||
QObject::connect(_sendQueue.get(), &SendQueue::queueInactive, this, &Connection::queueInactive);
|
QObject::connect(_sendQueue.get(), &SendQueue::queueInactive, this, &Connection::queueInactive);
|
||||||
|
|
||||||
// set defaults on the send queue from our congestion control object
|
// set defaults on the send queue from our congestion control object and estimatedTimeout()
|
||||||
_sendQueue->setPacketSendPeriod(_congestionControl->_packetSendPeriod);
|
_sendQueue->setPacketSendPeriod(_congestionControl->_packetSendPeriod);
|
||||||
|
_sendQueue->setEstimatedTimeout(estimatedTimeout());
|
||||||
_sendQueue->setFlowWindowSize(std::min(_flowWindowSize, (int) _congestionControl->_congestionWindowSize));
|
_sendQueue->setFlowWindowSize(std::min(_flowWindowSize, (int) _congestionControl->_congestionWindowSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +238,7 @@ void Connection::sendLightACK() {
|
||||||
|
|
||||||
// create the light ACK packet, make it static so we can re-use it
|
// create the light ACK packet, make it static so we can re-use it
|
||||||
static const int LIGHT_ACK_PACKET_PAYLOAD_BYTES = sizeof(SequenceNumber);
|
static const int LIGHT_ACK_PACKET_PAYLOAD_BYTES = sizeof(SequenceNumber);
|
||||||
static auto lightACKPacket = ControlPacket::create(ControlPacket::ACK, LIGHT_ACK_PACKET_PAYLOAD_BYTES);
|
static auto lightACKPacket = ControlPacket::create(ControlPacket::LightACK, LIGHT_ACK_PACKET_PAYLOAD_BYTES);
|
||||||
|
|
||||||
// reset the lightACKPacket before we go to write the ACK to it
|
// reset the lightACKPacket before we go to write the ACK to it
|
||||||
lightACKPacket->reset();
|
lightACKPacket->reset();
|
||||||
|
@ -407,13 +408,13 @@ void Connection::processControl(std::unique_ptr<ControlPacket> controlPacket) {
|
||||||
switch (controlPacket->getType()) {
|
switch (controlPacket->getType()) {
|
||||||
case ControlPacket::ACK:
|
case ControlPacket::ACK:
|
||||||
if (_hasReceivedHandshakeACK) {
|
if (_hasReceivedHandshakeACK) {
|
||||||
if (controlPacket->getPayloadSize() == sizeof(SequenceNumber)) {
|
processACK(move(controlPacket));
|
||||||
processLightACK(move(controlPacket));
|
|
||||||
} else {
|
|
||||||
processACK(move(controlPacket));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ControlPacket::LightACK:
|
||||||
|
if (_hasReceivedHandshakeACK) {
|
||||||
|
processLightACK(move(controlPacket));
|
||||||
|
}
|
||||||
case ControlPacket::ACK2:
|
case ControlPacket::ACK2:
|
||||||
if (_hasReceivedHandshake) {
|
if (_hasReceivedHandshake) {
|
||||||
processACK2(move(controlPacket));
|
processACK2(move(controlPacket));
|
||||||
|
@ -727,9 +728,12 @@ void Connection::updateCongestionControlAndSendQueue(std::function<void ()> cong
|
||||||
// fire congestion control callback
|
// fire congestion control callback
|
||||||
congestionCallback();
|
congestionCallback();
|
||||||
|
|
||||||
|
auto& sendQueue = getSendQueue();
|
||||||
|
|
||||||
// now that we've updated the congestion control, update the packet send period and flow window size
|
// now that we've updated the congestion control, update the packet send period and flow window size
|
||||||
getSendQueue().setPacketSendPeriod(_congestionControl->_packetSendPeriod);
|
sendQueue.setPacketSendPeriod(_congestionControl->_packetSendPeriod);
|
||||||
getSendQueue().setFlowWindowSize(std::min(_flowWindowSize, (int) _congestionControl->_congestionWindowSize));
|
sendQueue.setEstimatedTimeout(estimatedTimeout());
|
||||||
|
sendQueue.setFlowWindowSize(std::min(_flowWindowSize, (int) _congestionControl->_congestionWindowSize));
|
||||||
|
|
||||||
// record connection stats
|
// record connection stats
|
||||||
_stats.recordPacketSendPeriod(_congestionControl->_packetSendPeriod);
|
_stats.recordPacketSendPeriod(_congestionControl->_packetSendPeriod);
|
||||||
|
|
|
@ -29,6 +29,7 @@ public:
|
||||||
enum Type : uint16_t {
|
enum Type : uint16_t {
|
||||||
ACK,
|
ACK,
|
||||||
ACK2,
|
ACK2,
|
||||||
|
LightACK,
|
||||||
NAK,
|
NAK,
|
||||||
TimeoutNAK,
|
TimeoutNAK,
|
||||||
Handshake,
|
Handshake,
|
||||||
|
|
|
@ -154,6 +154,9 @@ void SendQueue::sendPacket(const Packet& packet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendQueue::ack(SequenceNumber ack) {
|
void SendQueue::ack(SequenceNumber ack) {
|
||||||
|
// this is a response from the client, re-set our timeout expiry
|
||||||
|
_timeoutExpiryCount = 0;
|
||||||
|
|
||||||
if (_lastACKSequenceNumber == (uint32_t) ack) {
|
if (_lastACKSequenceNumber == (uint32_t) ack) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -177,6 +180,9 @@ void SendQueue::ack(SequenceNumber ack) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
|
void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
|
||||||
|
// this is a response from the client, re-set our timeout expiry
|
||||||
|
_timeoutExpiryCount = 0;
|
||||||
|
|
||||||
std::unique_lock<std::mutex> nakLocker(_naksLock);
|
std::unique_lock<std::mutex> nakLocker(_naksLock);
|
||||||
|
|
||||||
_naks.insert(start, end);
|
_naks.insert(start, end);
|
||||||
|
@ -189,6 +195,9 @@ void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) {
|
void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) {
|
||||||
|
// this is a response from the client, re-set our timeout expiry
|
||||||
|
_timeoutExpiryCount = 0;
|
||||||
|
|
||||||
std::unique_lock<std::mutex> nakLocker(_naksLock);
|
std::unique_lock<std::mutex> nakLocker(_naksLock);
|
||||||
_naks.clear();
|
_naks.clear();
|
||||||
|
|
||||||
|
@ -212,7 +221,7 @@ void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendQueue::handshakeACK() {
|
void SendQueue::handshakeACK() {
|
||||||
std::unique_lock<std::mutex> locker(_handshakeMutex);
|
std::unique_lock<std::mutex> locker { _handshakeMutex };
|
||||||
|
|
||||||
_hasReceivedHandshakeACK = true;
|
_hasReceivedHandshakeACK = true;
|
||||||
|
|
||||||
|
@ -258,7 +267,7 @@ void SendQueue::run() {
|
||||||
while (_isRunning) {
|
while (_isRunning) {
|
||||||
// Record how long the loop takes to execute
|
// Record how long the loop takes to execute
|
||||||
auto loopStartTimestamp = high_resolution_clock::now();
|
auto loopStartTimestamp = high_resolution_clock::now();
|
||||||
|
|
||||||
std::unique_lock<std::mutex> handshakeLock { _handshakeMutex };
|
std::unique_lock<std::mutex> handshakeLock { _handshakeMutex };
|
||||||
|
|
||||||
if (!_hasReceivedHandshakeACK) {
|
if (!_hasReceivedHandshakeACK) {
|
||||||
|
@ -295,22 +304,15 @@ void SendQueue::run() {
|
||||||
handshakeLock.unlock();
|
handshakeLock.unlock();
|
||||||
|
|
||||||
bool sentAPacket = maybeResendPacket();
|
bool sentAPacket = maybeResendPacket();
|
||||||
bool flowWindowFull = false;
|
|
||||||
|
|
||||||
// if we didn't find a packet to re-send AND we think we can fit a new packet on the wire
|
// if we didn't find a packet to re-send AND we think we can fit a new packet on the wire
|
||||||
// (this is according to the current flow window size) then we send out a new packet
|
// (this is according to the current flow window size) then we send out a new packet
|
||||||
if (_hasReceivedHandshakeACK && !sentAPacket) {
|
if (_hasReceivedHandshakeACK && !sentAPacket) {
|
||||||
flowWindowFull = (seqlen(SequenceNumber { (uint32_t) _lastACKSequenceNumber }, _currentSequenceNumber) >
|
if (seqlen(SequenceNumber { (uint32_t) _lastACKSequenceNumber }, _currentSequenceNumber) <= _flowWindowSize) {
|
||||||
_flowWindowSize);
|
sentAPacket = maybeSendNewPacket();
|
||||||
sentAPacket = maybeSendNewPacket();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep track of how long the flow window has been full for
|
|
||||||
if (flowWindowFull && !_flowWindowWasFull) {
|
|
||||||
_flowWindowFullSince = loopStartTimestamp;
|
|
||||||
}
|
|
||||||
_flowWindowWasFull = flowWindowFull;
|
|
||||||
|
|
||||||
// since we're a while loop, give the thread a chance to process events
|
// since we're a while loop, give the thread a chance to process events
|
||||||
QCoreApplication::processEvents();
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
|
@ -320,38 +322,90 @@ void SendQueue::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_hasReceivedHandshakeACK && !sentAPacket) {
|
if (_hasReceivedHandshakeACK && !sentAPacket) {
|
||||||
static const std::chrono::seconds CONSIDER_INACTIVE_AFTER { 5 };
|
// check if it is time to break this connection
|
||||||
|
|
||||||
if (flowWindowFull && (high_resolution_clock::now() - _flowWindowFullSince) > CONSIDER_INACTIVE_AFTER) {
|
// that will be the case if we have had 16 timeouts since hearing back from the client, and it has been
|
||||||
|
// at least 10 seconds
|
||||||
|
|
||||||
|
static const int NUM_TIMEOUTS_BEFORE_INACTIVE = 16;
|
||||||
|
|
||||||
|
if (_timeoutExpiryCount >= NUM_TIMEOUTS_BEFORE_INACTIVE) {
|
||||||
// If the flow window has been full for over CONSIDER_INACTIVE_AFTER,
|
// If the flow window has been full for over CONSIDER_INACTIVE_AFTER,
|
||||||
// then signal the queue is inactive and return so it can be cleaned up
|
// then signal the queue is inactive and return so it can be cleaned up
|
||||||
|
qDebug() << "SendQueue to" << _destination << "reached" << NUM_TIMEOUTS_BEFORE_INACTIVE << "timeouts and is"
|
||||||
|
<< "considered inactive. It is now being stopped.";
|
||||||
|
|
||||||
emit queueInactive();
|
emit queueInactive();
|
||||||
|
|
||||||
|
_isRunning = false;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// During our processing above we didn't send any packets and the flow window is not full.
|
// During our processing above we didn't send any packets
|
||||||
|
|
||||||
// If that is still the case we should use a condition_variable_any to sleep until we have data to handle.
|
// If that is still the case we should use a condition_variable_any to sleep until we have data to handle.
|
||||||
// To confirm that the queue of packets and the NAKs list are still both empty we'll need to use the DoubleLock
|
// To confirm that the queue of packets and the NAKs list are still both empty we'll need to use the DoubleLock
|
||||||
DoubleLock doubleLock(_packetsLock, _naksLock);
|
DoubleLock doubleLock(_packetsLock, _naksLock);
|
||||||
|
|
||||||
// The packets queue and loss list mutexes are now both locked - check if they're still both empty
|
if (doubleLock.try_lock()) {
|
||||||
if (doubleLock.try_lock() && _packets.empty() && _naks.getLength() == 0) {
|
// The packets queue and loss list mutexes are now both locked - check if they're still both empty
|
||||||
|
|
||||||
// both are empty - let's use a condition_variable_any to wait
|
if (_packets.empty() && _naks.getLength() == 0) {
|
||||||
auto cvStatus = _emptyCondition.wait_for(doubleLock, CONSIDER_INACTIVE_AFTER);
|
if (uint32_t(_lastACKSequenceNumber) == uint32_t(_currentSequenceNumber)) {
|
||||||
|
// we've sent the client as much data as we have (and they've ACKed it)
|
||||||
// we have the double lock again - Make sure to unlock it
|
// either wait for new data to send or 5 seconds before cleaning up the queue
|
||||||
doubleLock.unlock();
|
static const auto EMPTY_QUEUES_INACTIVE_TIMEOUT = std::chrono::seconds(5);
|
||||||
|
|
||||||
if (cvStatus == std::cv_status::timeout) {
|
// use our condition_variable_any to wait
|
||||||
// the wait_for released because we've been inactive for too long
|
auto cvStatus = _emptyCondition.wait_for(doubleLock, EMPTY_QUEUES_INACTIVE_TIMEOUT);
|
||||||
// so emit our inactive signal and return so the send queue can be cleaned up
|
|
||||||
emit queueInactive();
|
// we have the double lock again - Make sure to unlock it
|
||||||
return;
|
doubleLock.unlock();
|
||||||
}
|
|
||||||
|
if (cvStatus == std::cv_status::timeout) {
|
||||||
// skip to the next iteration
|
qDebug() << "SendQueue to" << _destination << "has been empty for"
|
||||||
continue;
|
<< EMPTY_QUEUES_INACTIVE_TIMEOUT.count()
|
||||||
|
<< "seconds and receiver has ACKed all packets."
|
||||||
|
<< "The queue is considered inactive and will be stopped.";
|
||||||
|
|
||||||
|
// this queue is inactive - emit that signal and stop the while
|
||||||
|
emit queueInactive();
|
||||||
|
|
||||||
|
_isRunning = false;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We think the client is still waiting for data (based on the sequence number gap)
|
||||||
|
// Let's wait either for a response from the client or until the estimated timeout
|
||||||
|
auto waitDuration = std::chrono::microseconds(_estimatedTimeout);
|
||||||
|
|
||||||
|
// use our condition_variable_any to wait
|
||||||
|
auto cvStatus = _emptyCondition.wait_for(doubleLock, waitDuration);
|
||||||
|
|
||||||
|
if (cvStatus == std::cv_status::timeout) {
|
||||||
|
// increase the number of timeouts
|
||||||
|
++_timeoutExpiryCount;
|
||||||
|
|
||||||
|
if (SequenceNumber(_lastACKSequenceNumber) < _currentSequenceNumber) {
|
||||||
|
// after a timeout if we still have sent packets that the client hasn't ACKed we
|
||||||
|
// add them to the loss list
|
||||||
|
|
||||||
|
// Note that thanks to the DoubleLock we have the _naksLock right now
|
||||||
|
_naks.append(SequenceNumber(_lastACKSequenceNumber) + 1, _currentSequenceNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have the double lock again - Make sure to unlock it
|
||||||
|
doubleLock.unlock();
|
||||||
|
|
||||||
|
// skip to the next iteration
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we got the try_lock but failed the other conditionals so we need to unlock
|
||||||
|
doubleLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -410,9 +464,10 @@ bool SendQueue::maybeSendNewPacket() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SendQueue::maybeResendPacket() {
|
bool SendQueue::maybeResendPacket() {
|
||||||
|
std::unique_lock<std::mutex> naksLocker(_naksLock);
|
||||||
|
|
||||||
// the following while makes sure that we find a packet to re-send, if there is one
|
// the following while makes sure that we find a packet to re-send, if there is one
|
||||||
while (true) {
|
while (true) {
|
||||||
std::unique_lock<std::mutex> naksLocker(_naksLock);
|
|
||||||
|
|
||||||
if (_naks.getLength() > 0) {
|
if (_naks.getLength() > 0) {
|
||||||
// pull the sequence number we need to re-send
|
// pull the sequence number we need to re-send
|
||||||
|
@ -452,4 +507,3 @@ bool SendQueue::maybeResendPacket() {
|
||||||
// No packet was resent
|
// No packet was resent
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,8 @@ public:
|
||||||
int getPacketSendPeriod() const { return _packetSendPeriod; }
|
int getPacketSendPeriod() const { return _packetSendPeriod; }
|
||||||
void setPacketSendPeriod(int newPeriod) { _packetSendPeriod = newPeriod; }
|
void setPacketSendPeriod(int newPeriod) { _packetSendPeriod = newPeriod; }
|
||||||
|
|
||||||
|
void setEstimatedTimeout(int estimatedTimeout) { _estimatedTimeout = estimatedTimeout; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
|
@ -104,11 +106,10 @@ private:
|
||||||
std::atomic<int> _packetSendPeriod { 0 }; // Interval between two packet send event in microseconds, set from CC
|
std::atomic<int> _packetSendPeriod { 0 }; // Interval between two packet send event in microseconds, set from CC
|
||||||
std::atomic<bool> _isRunning { false };
|
std::atomic<bool> _isRunning { false };
|
||||||
|
|
||||||
std::atomic<int> _flowWindowSize { 0 }; // Flow control window size (number of packets that can be on wire) - set from CC
|
std::atomic<int> _estimatedTimeout { 0 }; // Estimated timeout, set from CC
|
||||||
|
std::atomic<int> _timeoutExpiryCount { 0 }; // The number of times the timeout has expired without response from client
|
||||||
|
|
||||||
// Used to detect when the connection becomes inactive for too long
|
std::atomic<int> _flowWindowSize { 0 }; // Flow control window size (number of packets that can be on wire) - set from CC
|
||||||
bool _flowWindowWasFull = false;
|
|
||||||
time_point _flowWindowFullSince;
|
|
||||||
|
|
||||||
mutable std::mutex _naksLock; // Protects the naks list.
|
mutable std::mutex _naksLock; // Protects the naks list.
|
||||||
LossList _naks; // Sequence numbers of packets to resend
|
LossList _naks; // Sequence numbers of packets to resend
|
||||||
|
@ -117,7 +118,7 @@ private:
|
||||||
std::unordered_map<SequenceNumber, std::unique_ptr<Packet>> _sentPackets; // Packets waiting for ACK.
|
std::unordered_map<SequenceNumber, std::unique_ptr<Packet>> _sentPackets; // Packets waiting for ACK.
|
||||||
|
|
||||||
std::mutex _handshakeMutex; // Protects the handshake ACK condition_variable
|
std::mutex _handshakeMutex; // Protects the handshake ACK condition_variable
|
||||||
bool _hasReceivedHandshakeACK { false }; // flag for receipt of handshake ACK from client
|
std::atomic<bool> _hasReceivedHandshakeACK { false }; // flag for receipt of handshake ACK from client
|
||||||
std::condition_variable _handshakeACKCondition;
|
std::condition_variable _handshakeACKCondition;
|
||||||
|
|
||||||
std::condition_variable_any _emptyCondition;
|
std::condition_variable_any _emptyCondition;
|
||||||
|
|
|
@ -143,6 +143,8 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& soc
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection& Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) {
|
Connection& Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) {
|
||||||
|
QMutexLocker locker(&_connectionsMutex);
|
||||||
|
|
||||||
auto it = _connectionsHash.find(sockAddr);
|
auto it = _connectionsHash.find(sockAddr);
|
||||||
|
|
||||||
if (it == _connectionsHash.end()) {
|
if (it == _connectionsHash.end()) {
|
||||||
|
@ -160,12 +162,16 @@ void Socket::clearConnections() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QMutexLocker locker(&_connectionsMutex);
|
||||||
|
|
||||||
// clear all of the current connections in the socket
|
// clear all of the current connections in the socket
|
||||||
qDebug() << "Clearing all remaining connections in Socket.";
|
qDebug() << "Clearing all remaining connections in Socket.";
|
||||||
_connectionsHash.clear();
|
_connectionsHash.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Socket::cleanupConnection(HifiSockAddr sockAddr) {
|
void Socket::cleanupConnection(HifiSockAddr sockAddr) {
|
||||||
|
QMutexLocker locker(&_connectionsMutex);
|
||||||
|
|
||||||
qCDebug(networking) << "Socket::cleanupConnection called for UDT connection to" << sockAddr;
|
qCDebug(networking) << "Socket::cleanupConnection called for UDT connection to" << sockAddr;
|
||||||
_connectionsHash.erase(sockAddr);
|
_connectionsHash.erase(sockAddr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,8 @@ private:
|
||||||
std::unordered_map<HifiSockAddr, SequenceNumber> _unreliableSequenceNumbers;
|
std::unordered_map<HifiSockAddr, SequenceNumber> _unreliableSequenceNumbers;
|
||||||
std::unordered_map<HifiSockAddr, std::unique_ptr<Connection>> _connectionsHash;
|
std::unordered_map<HifiSockAddr, std::unique_ptr<Connection>> _connectionsHash;
|
||||||
|
|
||||||
|
QMutex _connectionsMutex; // guards concurrent access to connections hashs
|
||||||
|
|
||||||
int _synInterval = 10; // 10ms
|
int _synInterval = 10; // 10ms
|
||||||
QTimer _synTimer;
|
QTimer _synTimer;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue