diff --git a/libraries/networking/src/SequenceNumberStats.cpp b/libraries/networking/src/SequenceNumberStats.cpp index 00a3e66877..a700e01f07 100644 --- a/libraries/networking/src/SequenceNumberStats.cpp +++ b/libraries/networking/src/SequenceNumberStats.cpp @@ -13,22 +13,67 @@ #include -SequenceNumberStats::SequenceNumberStats(int statsHistoryLength) +SequenceNumberStats::SequenceNumberStats(int statsHistoryLength, int maxRecursion) : _received(0), _lastReceivedSequence(0), _missingSet(), _stats(), _lastSenderUUID(), - _statsHistory(statsHistoryLength) + _statsHistory(statsHistoryLength), + + _unreasonableTracker(NULL), + _maxRecursion(maxRecursion) { } +SequenceNumberStats::SequenceNumberStats(const SequenceNumberStats& other) + : _received(other._received), + _lastReceivedSequence(other._lastReceivedSequence), + _missingSet(other._missingSet), + _stats(other._stats), + _lastSenderUUID(other._lastSenderUUID), + _statsHistory(other._statsHistory), + _unreasonableTracker(NULL), + _maxRecursion(other._maxRecursion) +{ + if (other._unreasonableTracker) { + _unreasonableTracker = new SequenceNumberStats(*other._unreasonableTracker); + } +} + +SequenceNumberStats& SequenceNumberStats::operator=(const SequenceNumberStats& rhs) { + _received = rhs._received; + _lastReceivedSequence = rhs._lastReceivedSequence; + _missingSet = rhs._missingSet; + _stats = rhs._stats; + _lastSenderUUID = rhs._lastSenderUUID; + _statsHistory = rhs._statsHistory; + _maxRecursion = rhs._maxRecursion; + + if (rhs._unreasonableTracker) { + _unreasonableTracker = new SequenceNumberStats(*rhs._unreasonableTracker); + } else { + _unreasonableTracker = NULL; + } + return *this; +} + +SequenceNumberStats::~SequenceNumberStats() { + if (_unreasonableTracker) { + delete _unreasonableTracker; + } +} + void SequenceNumberStats::reset() { _received = 0; _missingSet.clear(); _stats = PacketStreamStats(); _lastSenderUUID = QUuid(); _statsHistory.clear(); + + if (_unreasonableTracker) { + delete _unreasonableTracker; + } } static const int UINT16_RANGE = std::numeric_limits::max() + 1; @@ -78,10 +123,64 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui } else if (absGap > MAX_REASONABLE_SEQUENCE_GAP) { arrivalInfo._status = Unreasonable; + /* // ignore packet if gap is unreasonable qDebug() << "ignoring unreasonable sequence number:" << incoming << "previous:" << _lastReceivedSequence; _stats._unreasonable++; + */ + + // do not create a child tracker for unreasonable seq nums if this instance is the last one in the chain. + // otherwise, create one if we don't have one. + + if (!_unreasonableTracker && _maxRecursion > 0) { + _unreasonableTracker = new SequenceNumberStats(0, _maxRecursion - 1); + } + + + if (_unreasonableTracker) { + + // track this unreasonable seq number with our _unreasonableTracker. + ArrivalInfo unreasonableTrackerArrivalInfo = _unreasonableTracker->sequenceNumberReceived(incoming); + + + const int UNREASONABLE_TRACKER_RECEIVED_THRESHOLD = 10; + const float UNRUNREASONABLE_TRACKER_UNREASONABLE_RATE_THRESHOLD = 0.1f; + const float UNREASONABLE_TRACKER_OUT_OF_ORDER_RATE_THRESHOLD = 0.1f; + + // when our _unreasonableTracker has received enough seq nums and doesn't have an _unreasonableTracker of its own, + // we'll either inherit its state only if we think its stream is plausible. it will then be deleted. + // (if it has an _unreasonableTracker of its own, its _unreasonableTracker may be detecting a plausible stream + // while its parent does not, so we should let it accrue seq nums and decide plausibility first) + + if (!_unreasonableTracker->hasUnreasonableTracker() && + _unreasonableTracker->_received >= UNREASONABLE_TRACKER_RECEIVED_THRESHOLD) { + + if (_unreasonableTracker->getUnreasonableRate() < UNRUNREASONABLE_TRACKER_UNREASONABLE_RATE_THRESHOLD && + _unreasonableTracker->getStats().getOutOfOrderRate() < UNREASONABLE_TRACKER_OUT_OF_ORDER_RATE_THRESHOLD) { + + // the _unreasonableTracker has detected a plausible stream of seq numbers; + // copy its state to this tracker. + + _received = _unreasonableTracker->_received; + _lastReceivedSequence = _unreasonableTracker->_lastReceivedSequence; + _missingSet = _unreasonableTracker->_missingSet; + _stats = _unreasonableTracker->_stats; + + // don't copy _lastSenderUUID; _unreasonableTracker always has null UUID for that member. + // ours should be up-to-date. + + // don't copy _statsHistory; _unreasonableTracker keeps a history of length 0. + // simply clear ours. + _statsHistory.clear(); + + arrivalInfo = unreasonableTrackerArrivalInfo; + + } + // remove our _unreasonableTracker + delete _unreasonableTracker; + } + } return arrivalInfo; } diff --git a/libraries/networking/src/SequenceNumberStats.h b/libraries/networking/src/SequenceNumberStats.h index d611a494ad..1f740d7af8 100644 --- a/libraries/networking/src/SequenceNumberStats.h +++ b/libraries/networking/src/SequenceNumberStats.h @@ -18,6 +18,10 @@ const int MAX_REASONABLE_SEQUENCE_GAP = 1000; + +const int DEFAULT_MAX_RECURSION = 5; + + class PacketStreamStats { public: PacketStreamStats() @@ -30,7 +34,7 @@ public: _duplicate(0) {} - float getUnreasonableRate() const { return (float)_unreasonable / _expectedReceived; } + float getOutOfOrderRate() const { return (float)(_early + _late) / _expectedReceived; } float getEaryRate() const { return (float)_early / _expectedReceived; } float getLateRate() const { return (float)_late / _expectedReceived; } float getLostRate() const { return (float)_lost / _expectedReceived; } @@ -61,14 +65,19 @@ public: }; - SequenceNumberStats(int statsHistoryLength = 0); + SequenceNumberStats(int statsHistoryLength = 0, int maxRecursion = DEFAULT_MAX_RECURSION); + SequenceNumberStats(const SequenceNumberStats& other); + SequenceNumberStats& operator=(const SequenceNumberStats& rhs); + ~SequenceNumberStats(); +public: void reset(); ArrivalInfo sequenceNumberReceived(quint16 incoming, QUuid senderUUID = QUuid(), const bool wantExtraDebugging = false); void pruneMissingSet(const bool wantExtraDebugging = false); void pushStatsToHistory() { _statsHistory.insert(_stats); } quint32 getReceived() const { return _received; } + float getUnreasonableRate() const { return _stats._unreasonable / _received; } quint32 getExpectedReceived() const { return _stats._expectedReceived; } quint32 getUnreasonable() const { return _stats._unreasonable; } @@ -94,6 +103,14 @@ private: QUuid _lastSenderUUID; RingBufferHistory _statsHistory; + + + // to deal with the incoming seq nums going out of sync with this tracker, we'll create another instance + // of this class when we encounter an unreasonable + SequenceNumberStats* _unreasonableTracker; + int _maxRecursion; + + bool hasUnreasonableTracker() const { return _unreasonableTracker != NULL; } }; #endif // hifi_SequenceNumberStats_h