Merge pull request #5795 from birarda/phrc

fix high resolution timing for VS2012 in networking
This commit is contained in:
Ryan Huffman 2015-09-14 13:49:20 -07:00
commit 15b809bd16
10 changed files with 125 additions and 41 deletions

View file

@ -34,7 +34,7 @@ void CongestionControl::setPacketSendPeriod(double newSendPeriod) {
}
DefaultCC::DefaultCC() :
_lastRCTime(high_resolution_clock::now()),
_lastRCTime(p_high_resolution_clock::now()),
_slowStartLastAck(_sendCurrSeqNum),
_lastDecreaseMaxSeq(SequenceNumber {SequenceNumber::MAX })
{
@ -54,7 +54,7 @@ void DefaultCC::onACK(SequenceNumber ackNum) {
const double minimumIncrease = 0.01;
// we will only adjust once per sync interval so check that it has been at least that long now
auto now = high_resolution_clock::now();
auto now = p_high_resolution_clock::now();
if (duration_cast<microseconds>(now - _lastRCTime).count() < synInterval()) {
return;
}

View file

@ -12,11 +12,12 @@
#ifndef hifi_CongestionControl_h
#define hifi_CongestionControl_h
#include <chrono>
#include <memory>
#include <vector>
#include <memory>
#include <PortableHighResolutionClock.h>
#include "LossList.h"
#include "SequenceNumber.h"
@ -107,7 +108,7 @@ public:
private:
void stopSlowStart(); // stops the slow start on loss or timeout
std::chrono::high_resolution_clock::time_point _lastRCTime; // last rate increase time
p_high_resolution_clock::time_point _lastRCTime; // last rate increase time
bool _slowStart { true }; // if in slow start phase
SequenceNumber _slowStartLastAck; // last ACKed seq num
bool _loss { false }; // if loss happened since last rate increase

View file

@ -28,7 +28,7 @@ using namespace udt;
using namespace std::chrono;
Connection::Connection(Socket* parentSocket, HifiSockAddr destination, std::unique_ptr<CongestionControl> congestionControl) :
_connectionStart(high_resolution_clock::now()),
_connectionStart(p_high_resolution_clock::now()),
_parentSocket(parentSocket),
_destination(destination),
_congestionControl(move(congestionControl))
@ -154,7 +154,7 @@ void Connection::sync() {
static const int NUM_TIMEOUTS_BEFORE_EXPIRY = 16;
static const int MIN_SECONDS_BEFORE_EXPIRY = 5;
auto now = high_resolution_clock::now();
auto now = p_high_resolution_clock::now();
auto sincePacketReceive = now - _lastReceiveTime;
@ -185,7 +185,7 @@ void Connection::sync() {
if (_lossList.getLength() > 0) {
// check if we need to re-transmit a loss list
// we do this if it has been longer than the current nakInterval since we last sent
auto now = high_resolution_clock::now();
auto now = p_high_resolution_clock::now();
if (duration_cast<microseconds>(now - _lastNAKTime).count() >= _nakInterval) {
// Send a timeout NAK packet
@ -197,7 +197,7 @@ void Connection::sync() {
// this most likely means we were started erroneously
// check the start time for this connection and auto expire it after 5 seconds of not receiving or sending any data
static const int CONNECTION_NOT_USED_EXPIRY_SECONDS = 5;
auto secondsSinceStart = duration_cast<seconds>(high_resolution_clock::now() - _connectionStart).count();
auto secondsSinceStart = duration_cast<seconds>(p_high_resolution_clock::now() - _connectionStart).count();
if (secondsSinceStart >= CONNECTION_NOT_USED_EXPIRY_SECONDS) {
// it's been CONNECTION_NOT_USED_EXPIRY_SECONDS and nothing has actually happened with this connection
@ -222,8 +222,8 @@ void Connection::recordRetransmission() {
}
void Connection::sendACK(bool wasCausedBySyncTimeout) {
static high_resolution_clock::time_point lastACKSendTime;
auto currentTime = high_resolution_clock::now();
static p_high_resolution_clock::time_point lastACKSendTime;
auto currentTime = p_high_resolution_clock::now();
SequenceNumber nextACKNumber = nextACK();
Q_ASSERT_X(nextACKNumber >= _lastSentACK, "Connection::sendACK", "Sending lower ACK, something is wrong");
@ -280,7 +280,7 @@ void Connection::sendACK(bool wasCausedBySyncTimeout) {
ackPacket->writePrimitive(estimatedBandwidth);
// record this as the last ACK send time
lastACKSendTime = high_resolution_clock::now();
lastACKSendTime = p_high_resolution_clock::now();
}
// have the socket send off our packet
@ -290,7 +290,7 @@ void Connection::sendACK(bool wasCausedBySyncTimeout) {
"Connection::sendACK", "Adding an invalid ACK to _sentACKs");
// write this ACK to the map of sent ACKs
_sentACKs.push_back({ _currentACKSubSequenceNumber, { nextACKNumber, high_resolution_clock::now() }});
_sentACKs.push_back({ _currentACKSubSequenceNumber, { nextACKNumber, p_high_resolution_clock::now() }});
// reset the number of data packets received since last ACK
_packetsSinceACK = 0;
@ -358,7 +358,7 @@ void Connection::sendNAK(SequenceNumber sequenceNumberRecieved) {
_parentSocket->writeBasePacket(*lossReport, _destination);
// record our last NAK time
_lastNAKTime = high_resolution_clock::now();
_lastNAKTime = p_high_resolution_clock::now();
_stats.record(ConnectionStats::Stats::SentNAK);
}
@ -379,7 +379,7 @@ void Connection::sendTimeoutNAK() {
_parentSocket->writeBasePacket(*lossListPacket, _destination);
// record this as the last NAK time
_lastNAKTime = high_resolution_clock::now();
_lastNAKTime = p_high_resolution_clock::now();
_stats.record(ConnectionStats::Stats::SentTimeoutNAK);
}
@ -403,7 +403,7 @@ bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, in
_isReceivingData = true;
// mark our last receive time as now (to push the potential expiry farther)
_lastReceiveTime = high_resolution_clock::now();
_lastReceiveTime = p_high_resolution_clock::now();
// check if this is a packet pair we should estimate bandwidth from, or just a regular packet
if (((uint32_t) sequenceNumber & 0xF) == 0) {
@ -533,8 +533,8 @@ void Connection::processACK(std::unique_ptr<ControlPacket> controlPacket) {
// Check if we need send an ACK2 for this ACK
// This will be the case if it has been longer than the sync interval OR
// it looks like they haven't received our ACK2 for this ACK
auto currentTime = high_resolution_clock::now();
static high_resolution_clock::time_point lastACK2SendTime;
auto currentTime = p_high_resolution_clock::now();
static p_high_resolution_clock::time_point lastACK2SendTime;
microseconds sinceLastACK2 = duration_cast<microseconds>(currentTime - lastACK2SendTime);
@ -542,7 +542,7 @@ void Connection::processACK(std::unique_ptr<ControlPacket> controlPacket) {
// Send ACK2 packet
sendACK2(currentACKSubSequenceNumber);
lastACK2SendTime = high_resolution_clock::now();
lastACK2SendTime = p_high_resolution_clock::now();
}
// read the ACKed sequence number
@ -664,7 +664,7 @@ void Connection::processACK2(std::unique_ptr<ControlPacket> controlPacket) {
// update the RTT using the ACK window
// calculate the RTT (time now - time ACK sent)
auto now = high_resolution_clock::now();
auto now = p_high_resolution_clock::now();
int rtt = duration_cast<microseconds>(now - it->second.second).count();
updateRTT(rtt);
@ -779,13 +779,13 @@ void Connection::resetReceiveState() {
// clear the loss list and _lastNAKTime
_lossList.clear();
_lastNAKTime = high_resolution_clock::time_point();
_lastNAKTime = p_high_resolution_clock::time_point();
// the _nakInterval need not be reset, that will happen on loss
// clear sync variables
_isReceivingData = false;
_connectionStart = high_resolution_clock::now();
_connectionStart = p_high_resolution_clock::now();
_acksDuringSYN = 1;
_lightACKsDuringSYN = 1;

View file

@ -12,12 +12,13 @@
#ifndef hifi_Connection_h
#define hifi_Connection_h
#include <chrono>
#include <list>
#include <memory>
#include <QtCore/QObject>
#include <PortableHighResolutionClock.h>
#include "ConnectionStats.h"
#include "Constants.h"
#include "LossList.h"
@ -51,7 +52,7 @@ private:
class Connection : public QObject {
Q_OBJECT
public:
using SequenceNumberTimePair = std::pair<SequenceNumber, std::chrono::high_resolution_clock::time_point>;
using SequenceNumberTimePair = std::pair<SequenceNumber, p_high_resolution_clock::time_point>;
using ACKListPair = std::pair<SequenceNumber, SequenceNumberTimePair>;
using SentACKList = std::list<ACKListPair>;
@ -113,13 +114,13 @@ private:
int _nakInterval { -1 }; // NAK timeout interval, in microseconds, set on loss
int _minNAKInterval { 100000 }; // NAK timeout interval lower bound, default of 100ms
std::chrono::high_resolution_clock::time_point _lastNAKTime;
p_high_resolution_clock::time_point _lastNAKTime;
bool _hasReceivedHandshake { false }; // flag for receipt of handshake from server
bool _hasReceivedHandshakeACK { false }; // flag for receipt of handshake ACK from client
std::chrono::high_resolution_clock::time_point _connectionStart; // holds the time_point for creation of this connection
std::chrono::high_resolution_clock::time_point _lastReceiveTime; // holds the last time we received anything from sender
p_high_resolution_clock::time_point _connectionStart; // holds the time_point for creation of this connection
p_high_resolution_clock::time_point _lastReceiveTime; // holds the last time we received anything from sender
bool _isReceivingData { false }; // flag used for expiry of receipt portion of connection
LossList _lossList; // List of all missing packets

View file

@ -94,7 +94,7 @@ int32_t PacketTimeWindow::getEstimatedBandwidth() const {
void PacketTimeWindow::onPacketArrival() {
// take the current time
auto now = high_resolution_clock::now();
auto now = p_high_resolution_clock::now();
// record the interval between this packet and the last one
_packetIntervals[_currentPacketInterval++] = duration_cast<microseconds>(now - _lastPacketTime).count();
@ -110,12 +110,12 @@ void PacketTimeWindow::onPacketArrival() {
void PacketTimeWindow::onProbePair1Arrival() {
// take the current time as the first probe time
_firstProbeTime = high_resolution_clock::now();
_firstProbeTime = p_high_resolution_clock::now();
}
void PacketTimeWindow::onProbePair2Arrival() {
// store the interval between the two probes
auto now = high_resolution_clock::now();
auto now = p_high_resolution_clock::now();
_probeIntervals[_currentProbeInterval++] = duration_cast<microseconds>(now - _firstProbeTime).count();

View file

@ -14,9 +14,10 @@
#ifndef hifi_PacketTimeWindow_h
#define hifi_PacketTimeWindow_h
#include <chrono>
#include <vector>
#include <PortableHighResolutionClock.h>
namespace udt {
class PacketTimeWindow {
@ -41,8 +42,8 @@ private:
std::vector<int> _packetIntervals; // vector of microsecond intervals between packet arrivals
std::vector<int> _probeIntervals; // vector of microsecond intervals between probe pair arrivals
std::chrono::high_resolution_clock::time_point _lastPacketTime; // the time_point when last packet arrived
std::chrono::high_resolution_clock::time_point _firstProbeTime; // the time_point when first probe in pair arrived
p_high_resolution_clock::time_point _lastPacketTime; // the time_point when last packet arrived
p_high_resolution_clock::time_point _firstProbeTime; // the time_point when first probe in pair arrived
};
}

View file

@ -272,7 +272,7 @@ void SendQueue::run() {
while (_isRunning) {
// Record how long the loop takes to execute
auto loopStartTimestamp = high_resolution_clock::now();
auto loopStartTimestamp = p_high_resolution_clock::now();
std::unique_lock<std::mutex> handshakeLock { _handshakeMutex };
@ -281,12 +281,12 @@ void SendQueue::run() {
// if it has been at least 100ms since we last sent a handshake, send another now
// hold the time of last send in a static
static auto lastSendHandshake = high_resolution_clock::time_point();
static auto lastSendHandshake = p_high_resolution_clock::time_point();
static const auto HANDSHAKE_RESEND_INTERVAL_MS = std::chrono::milliseconds(100);
// calculation the duration since the last handshake send
auto sinceLastHandshake = std::chrono::duration_cast<std::chrono::milliseconds>(high_resolution_clock::now()
auto sinceLastHandshake = std::chrono::duration_cast<std::chrono::milliseconds>(p_high_resolution_clock::now()
- lastSendHandshake);
if (sinceLastHandshake >= HANDSHAKE_RESEND_INTERVAL_MS) {
@ -295,12 +295,12 @@ void SendQueue::run() {
static auto handshakePacket = ControlPacket::create(ControlPacket::Handshake, 0);
_socket->writeBasePacket(*handshakePacket, _destination);
lastSendHandshake = high_resolution_clock::now();
lastSendHandshake = p_high_resolution_clock::now();
}
// we wait for the ACK or the re-send interval to expire
_handshakeACKCondition.wait_until(handshakeLock,
high_resolution_clock::now()
p_high_resolution_clock::now()
+ HANDSHAKE_RESEND_INTERVAL_MS);
// Once we're here we've either received the handshake ACK or it's going to be time to re-send a handshake.
@ -420,7 +420,7 @@ void SendQueue::run() {
}
}
auto loopEndTimestamp = high_resolution_clock::now();
auto loopEndTimestamp = p_high_resolution_clock::now();
// sleep as long as we need until next packet send, if we can
auto timeToSleep = (loopStartTimestamp + std::chrono::microseconds(_packetSendPeriod)) - loopEndTimestamp;

View file

@ -13,7 +13,6 @@
#define hifi_SendQueue_h
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstdint>
#include <list>
@ -24,6 +23,8 @@
#include <QtCore/QObject>
#include <QtCore/QReadWriteLock>
#include <PortableHighResolutionClock.h>
#include "../HifiSockAddr.h"
#include "Constants.h"
@ -44,8 +45,7 @@ class SendQueue : public QObject {
Q_OBJECT
public:
using high_resolution_clock = std::chrono::high_resolution_clock;
using time_point = high_resolution_clock::time_point;
using time_point = p_high_resolution_clock::time_point;
static std::unique_ptr<SendQueue> create(Socket* socket, HifiSockAddr destination);

View file

@ -0,0 +1,30 @@
//
// PortableHighResolutionClock.cpp
// libraries/shared/src
//
// Created by Stephen Birarda on 2015-09-14.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#if defined(_MSC_VER) && _MSC_VER < 1900
#include "PortableHighResolutionClock.h"
namespace {
const long long g_Frequency = []() -> long long {
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
return frequency.QuadPart;
}();
}
win_high_resolution_clock::time_point win_high_resolution_clock::now() {
LARGE_INTEGER count;
QueryPerformanceCounter(&count);
return time_point(duration(count.QuadPart * static_cast<rep>(period::den) / g_Frequency));
}
#endif

View file

@ -0,0 +1,51 @@
//
// PortableHighResolutionClock.h
// libraries/shared/src
//
// Created by Stephen Birarda on 2015-09-14.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// I discovered the low-resolution nature of the VC2012 implementation of std::chrono::high_resolution_clock
// while debugging a UDT issue with packet receive speeds. That lead to
// http://stackoverflow.com/questions/16299029/resolution-of-stdchronohigh-resolution-clock-doesnt-correspond-to-measureme
// which is where the implementation of this class is from.
#pragma once
#ifndef hifi_PortableHighResolutionClock_h
#define hifi_PortableHighResolutionClock_h
#include <chrono>
#if defined(_MSC_VER) && _MSC_VER < 1900
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// The following struct is not compliant with the HF coding standard, but uses underscores to match the classes
// in std::chrono
struct win_high_resolution_clock {
typedef long long rep;
typedef std::nano period;
typedef std::chrono::duration<rep, period> duration;
typedef std::chrono::time_point<win_high_resolution_clock> time_point;
static const bool is_steady = true;
static time_point now();
};
using p_high_resolution_clock = win_high_resolution_clock;
#else
using p_high_resolution_clock = std::chrono::high_resolution_clock;
#endif
#endif // hifi_PortableHighResolutionClock_h