diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 64f04fce1..aefd8952c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8585,37 +8585,21 @@ void CUDT::addLossRecord(std::vector& lr, int32_t lo, int32_t hi) } } -void CUDT::checkTimers() +void CUDT::checkACKTimer(uint64_t currtime_tk) { - // update CC parameters - updateCC(TEV_CHECKTIMER, TEV_CHT_INIT); - //uint64_t minint = (uint64_t)(m_ullCPUFrequency * m_pSndTimeWindow->getMinPktSndInt() * 0.9); - //if (m_ullInterval_tk < minint) - // m_ullInterval_tk = minint; - // NOTE: This commented-out ^^^ code was commented out in original UDT. Leaving for historical reasons - - uint64_t currtime_tk; - CTimer::rdtsc(currtime_tk); - - // This is a very heavy log, unblock only for temporary debugging! -#if 0 - HLOGC(mglog.Debug, log << CONID() << "checkTimers: nextacktime=" << FormatTime(m_ullNextACKTime_tk) - << " AckInterval=" << m_iACKInterval - << " pkt-count=" << m_iPktCount << " liteack-count=" << m_iLightACKCount); -#endif - if (currtime_tk > m_ullNextACKTime_tk // ACK time has come - // OR the number of sent packets since last ACK has reached - // the congctl-defined value of ACK Interval - // (note that none of the builtin congctls defines ACK Interval) - || (m_CongCtl->ACKInterval() > 0 && m_iPktCount >= m_CongCtl->ACKInterval())) + // OR the number of sent packets since last ACK has reached + // the congctl-defined value of ACK Interval + // (note that none of the builtin congctls defines ACK Interval) + || (m_CongCtl->ACKInterval() > 0 && m_iPktCount >= m_CongCtl->ACKInterval())) { // ACK timer expired or ACK interval is reached - sendCtrl(UMSG_ACK); CTimer::rdtsc(currtime_tk); - int ack_interval_tk = m_CongCtl->ACKPeriod() > 0 ? m_CongCtl->ACKPeriod() * m_ullCPUFrequency : m_ullACKInt_tk; + const int ack_interval_tk = m_CongCtl->ACKPeriod() > 0 + ? m_CongCtl->ACKPeriod() * m_ullCPUFrequency + : m_ullACKInt_tk; m_ullNextACKTime_tk = currtime_tk + ack_interval_tk; m_iPktCount = 0; @@ -8631,38 +8615,34 @@ void CUDT::checkTimers() { //send a "light" ACK sendCtrl(UMSG_ACK, NULL, NULL, SEND_LITE_ACK); - ++ m_iLightACKCount; + ++m_iLightACKCount; } +} - if (m_bRcvNakReport) +void CUDT::checkNAKTimer(uint64_t currtime_tk) +{ + if (!m_bRcvNakReport) + return; + + /* + * m_bRcvNakReport enables NAK reports for SRT. + * Retransmission based on timeout is bandwidth consuming, + * not knowing what to retransmit when the only NAK sent by receiver is lost, + * all packets past last ACK are retransmitted (rexmitMethod() == SRM_FASTREXMIT). + */ + if ((currtime_tk > m_ullNextNAKTime_tk) && (m_pRcvLossList->getLossLength() > 0)) { - /* - * Enable NAK reports for SRT. - * Retransmission based on timeout is bandwidth consuming, - * not knowing what to retransmit when the only NAK sent by receiver is lost, - * all packets past last ACK are retransmitted (rexmitMethod() == SRM_FASTREXMIT). - */ - if ((currtime_tk > m_ullNextNAKTime_tk) && (m_pRcvLossList->getLossLength() > 0)) - { - // NAK timer expired, and there is loss to be reported. - sendCtrl(UMSG_LOSSREPORT); + // NAK timer expired, and there is loss to be reported. + sendCtrl(UMSG_LOSSREPORT); - CTimer::rdtsc(currtime_tk); - m_ullNextNAKTime_tk = currtime_tk + m_ullNAKInt_tk; - } - } // ELSE { - // we are not sending back repeated NAK anymore and rely on the sender's EXP for retransmission - //if ((m_pRcvLossList->getLossLength() > 0) && (currtime_tk > m_ullNextNAKTime_tk)) - //{ - // // NAK timer expired, and there is loss to be reported. - // sendCtrl(UMSG_LOSSREPORT); - // - // CTimer::rdtsc(currtime_tk); - // m_ullNextNAKTime_tk = currtime_tk + m_ullNAKInt_tk; - //} - //} + CTimer::rdtsc(currtime_tk); + m_ullNextNAKTime_tk = currtime_tk + m_ullNAKInt_tk; + } +} +bool CUDT::checkExpTimer(uint64_t currtime_tk) +{ // In UDT the m_bUserDefinedRTO and m_iRTO were in CCC class. // There's nothing in the original code that alters these values. @@ -8679,157 +8659,169 @@ void CUDT::checkTimers() next_exp_time_tk = m_ullLastRspTime_tk + exp_int_tk; } - if (currtime_tk > next_exp_time_tk) + if (currtime_tk <= next_exp_time_tk) + return false; + + // ms -> us + const int PEER_IDLE_TMO_US = m_iOPT_PeerIdleTimeout * 1000; + // Haven't received any information from the peer, is it dead?! + // timeout: at least 16 expirations and must be greater than 5 seconds + if ((m_iEXPCount > COMM_RESPONSE_MAX_EXP) + && (currtime_tk - m_ullLastRspTime_tk > PEER_IDLE_TMO_US * m_ullCPUFrequency)) { - // ms -> us - const int PEER_IDLE_TMO_US = m_iOPT_PeerIdleTimeout*1000; - // Haven't received any information from the peer, is it dead?! - // timeout: at least 16 expirations and must be greater than 5 seconds - if ((m_iEXPCount > COMM_RESPONSE_MAX_EXP) - && (currtime_tk - m_ullLastRspTime_tk > PEER_IDLE_TMO_US * m_ullCPUFrequency)) - { - // - // Connection is broken. - // UDT does not signal any information about this instead of to stop quietly. - // Application will detect this when it calls any UDT methods next time. - // - HLOGC(mglog.Debug, log << "CONNECTION EXPIRED after " << ((currtime_tk - m_ullLastRspTime_tk)/m_ullCPUFrequency) << "ms"); - m_bClosing = true; - m_bBroken = true; - m_iBrokenCounter = 30; + // + // Connection is broken. + // UDT does not signal any information about this instead of to stop quietly. + // Application will detect this when it calls any UDT methods next time. + // + HLOGC(mglog.Debug, log << "CONNECTION EXPIRED after " << ((currtime_tk - m_ullLastRspTime_tk) / m_ullCPUFrequency) << "ms"); + m_bClosing = true; + m_bBroken = true; + m_iBrokenCounter = 30; - // update snd U list to remove this socket - m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); + // update snd U list to remove this socket + m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); - releaseSynch(); + releaseSynch(); - // app can call any UDT API to learn the connection_broken error - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN | UDT_EPOLL_OUT | UDT_EPOLL_ERR, true); + // app can call any UDT API to learn the connection_broken error + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN | UDT_EPOLL_OUT | UDT_EPOLL_ERR, true); - CTimer::triggerEvent(); + CTimer::triggerEvent(); - return; - } + return true; + } - HLOGC(mglog.Debug, log << "EXP TIMER: count=" << m_iEXPCount << "/" << (+COMM_RESPONSE_MAX_EXP) - << " elapsed=" << ((currtime_tk - m_ullLastRspTime_tk) / m_ullCPUFrequency) << "/" << (+PEER_IDLE_TMO_US) << "us"); + HLOGC(mglog.Debug, log << "EXP TIMER: count=" << m_iEXPCount << "/" << (+COMM_RESPONSE_MAX_EXP) + << " elapsed=" << ((currtime_tk - m_ullLastRspTime_tk) / m_ullCPUFrequency) << "/" << (+PEER_IDLE_TMO_US) << "us"); - /* - * This part is only used with FileCC. This retransmits - * unacknowledged packet only when nothing in the loss list. - * This does not work well for real-time data that is delayed too much. - * For LiveCC, see the case of SRM_FASTREXMIT later in function. - */ - if (m_CongCtl->rexmitMethod() == SrtCongestion::SRM_LATEREXMIT) - { - // sender: Insert all the packets sent after last received acknowledgement into the sender loss list. - // recver: Send out a keep-alive packet - if (m_pSndBuffer->getCurrBufSize() > 0) - { - // protect packet retransmission - CGuard::enterCS(m_AckLock); - - // LATEREXMIT works only under the following conditions: - // - the "ACK window" is nonempty (there are some packets sent, but not ACK-ed) - // - the sender loss list is empty (the receiver didn't send any LOSSREPORT, or LOSSREPORT was lost on track) - // Otherwise the rexmit will be done EXCLUSIVELY basing on the received LOSSREPORTs. - if ((CSeqNo::incseq(m_iSndCurrSeqNo) != m_iSndLastAck) && (m_pSndLossList->getLossLength() == 0)) - { - // resend all unacknowledged packets on timeout, but only if there is no packet in the loss list - int32_t csn = m_iSndCurrSeqNo; - int num = m_pSndLossList->insert(m_iSndLastAck, csn); - if (num > 0) { - CGuard::enterCS(m_StatsLock); - m_stats.traceSndLoss += 1; // num; - m_stats.sndLossTotal += 1; // num; - CGuard::leaveCS(m_StatsLock); - - HLOGC(mglog.Debug, log << CONID() << "ENFORCED LATEREXMIT by ACK-TMOUT (scheduling): " << CSeqNo::incseq(m_iSndLastAck) << "-" << csn - << " (" << CSeqNo::seqoff(m_iSndLastAck, csn) << " packets)"); - } - } - // protect packet retransmission - CGuard::leaveCS(m_AckLock); + ++m_iEXPCount; - checkSndTimers(DONT_REGEN_KM); - updateCC(TEV_CHECKTIMER, TEV_CHT_REXMIT); + /* + * (keepalive fix) + * duB: + * It seems there is confusion of the direction of the Response here. + * LastRspTime is supposed to be when receiving (data/ctrl) from peer + * as shown in processCtrl and processData, + * Here we set because we sent something? + * + * Disabling this code that prevent quick reconnection when peer disappear + */ + // Reset last response time since we've just sent a heart-beat. + // (fixed) m_ullLastRspTime_tk = currtime_tk; - // immediately restart transmission - m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); - } - else - { - // (fix keepalive) - // XXX (the fix was for Live transmission; this is used in file transmission. Restore?) - //sendCtrl(UMSG_KEEPALIVE); - } - } - ++ m_iEXPCount; + return false; +} - /* - * (keepalive fix) - * duB: - * It seems there is confusion of the direction of the Response here. - * LastRspTime is supposed to be when receiving (data/ctrl) from peer - * as shown in processCtrl and processData, - * Here we set because we sent something? - * - * Disabling this code that prevent quick reconnection when peer disappear - */ - // Reset last response time since we just sent a heart-beat. - // (fixed) m_ullLastRspTime_tk = currtime_tk; +void CUDT::checkRexmitTimer(uint64_t currtime_tk) +{ + /* There are two algorithms of blind packet retransmission: LATEREXMIT and FASTREXMIT. + * + * LATEREXMIT is only used with FileCC. + * The mode is triggered when some time has passed since the last ACK from + * the receiver, while there is still some unacknowledged data in the sender's buffer, + * and the loss list is empty. + * + * FASTREXMIT is only used with LiveCC. + * The mode is triggered if the receiver does not send periodic NAK reports, + * when some time has passed since the last ACK from the receiver, + * while there is still some unacknowledged data in the sender's buffer. + * + * In case the above conditions are met, the unacknowledged packets + * in the sender's buffer will be added to loss list and retransmitted. + */ - } - // sender: Insert some packets sent after last received acknowledgement into the sender loss list. - // This handles retransmission on timeout for lost NAK for peer sending only one NAK when loss detected. - // Not required if peer send Periodic NAK Reports. - if (m_CongCtl->rexmitMethod() == SrtCongestion::SRM_FASTREXMIT - // XXX Still, if neither FASTREXMIT nor LATEREXMIT part is executed, then - // there's no "blind rexmit" done at all. The only other rexmit method - // than LOSSREPORT-based is then NAKREPORT (the receiver sends LOSSREPORT - // again after it didn't get a "response" for the previous one). MIND that - // probably some method of "blind rexmit" MUST BE DONE, when TLPKTDROP is off. - && !m_bPeerNakReport - && m_pSndBuffer->getCurrBufSize() > 0) - { - uint64_t exp_int = (m_iReXmitCount * (m_iRTT + 4 * m_iRTTVar + 2 * COMM_SYN_INTERVAL_US) + COMM_SYN_INTERVAL_US) * m_ullCPUFrequency; + const uint64_t rtt_syn = (m_iRTT + 4 * m_iRTTVar + 2 * COMM_SYN_INTERVAL_US); + const uint64_t exp_int = (m_iReXmitCount * rtt_syn + COMM_SYN_INTERVAL_US) * m_ullCPUFrequency; - if (currtime_tk > (m_ullLastRspAckTime_tk + exp_int)) - { - // protect packet retransmission - CGuard::enterCS(m_AckLock); - if ((CSeqNo::seqoff(m_iSndLastAck, CSeqNo::incseq(m_iSndCurrSeqNo)) > 0)) - { - // resend all unacknowledged packets on timeout - int32_t csn = m_iSndCurrSeqNo; - int num = m_pSndLossList->insert(m_iSndLastAck, csn); - HLOGC(mglog.Debug, log << CONID() << "ENFORCED FASTREXMIT by ACK-TMOUT PREPARED: " << m_iSndLastAck << "-" << csn - << " (" << CSeqNo::seqoff(m_iSndLastAck, csn) << " packets)"); + if (currtime_tk <= (m_ullLastRspAckTime_tk + exp_int)) + return; - HLOGC(mglog.Debug, log << "timeout lost: pkts=" << num << " rtt+4*var=" << - m_iRTT + 4 * m_iRTTVar << " cnt=" << m_iReXmitCount << " diff=" - << (currtime_tk - (m_ullLastRspAckTime_tk + exp_int)) << ""); + // If there is no unacknowledged data in the sending buffer, + // then there is nothing to retransmit. + if (m_pSndBuffer->getCurrBufSize() <= 0) + return; - if (num > 0) { - CGuard::enterCS(m_StatsLock); - m_stats.traceSndLoss += 1; // num; - m_stats.sndLossTotal += 1; // num; - CGuard::leaveCS(m_StatsLock); - } - } - // protect packet retransmission - CGuard::leaveCS(m_AckLock); + const bool is_laterexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_LATEREXMIT; + const bool is_fastrexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_FASTREXMIT; + + // If the receiver will send periodic NAK reports, then FASTREXMIT is inactive. + // MIND that probably some method of "blind rexmit" MUST BE DONE, when TLPKTDROP is off. + if (is_fastrexmit && m_bPeerNakReport) + return; - ++m_iReXmitCount; + // We need to retransmit only when the data in the sender's buffer was already sent. + // Otherwise it might still be sent regulary. + bool retransmit = false; + // - the sender loss list is empty (the receiver didn't send any LOSSREPORT, or LOSSREPORT was lost on track) + if (is_laterexmit && (CSeqNo::incseq(m_iSndCurrSeqNo) != m_iSndLastAck) && m_pSndLossList->getLossLength() == 0) + retransmit = true; - checkSndTimers(DONT_REGEN_KM); - updateCC(TEV_CHECKTIMER, TEV_CHT_FASTREXMIT); + if (is_fastrexmit && (CSeqNo::seqoff(m_iSndLastAck, CSeqNo::incseq(m_iSndCurrSeqNo)) > 0)) + retransmit = true; + + if (retransmit) + { + // Sender: Insert all the packets sent after last received acknowledgement into the sender loss list. + CGuard acklock(m_AckLock); // Protect packet retransmission + // Resend all unacknowledged packets on timeout, but only if there is no packet in the loss list + const int32_t csn = m_iSndCurrSeqNo; + const int num = m_pSndLossList->insert(m_iSndLastAck, csn); + if (num > 0) + { + CGuard::enterCS(m_StatsLock); + m_stats.traceSndLoss += num; + m_stats.sndLossTotal += num; + CGuard::leaveCS(m_StatsLock); - // immediately restart transmission - m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); + HLOGC(mglog.Debug, log << CONID() << "ENFORCED " << (is_laterexmit ? "LATEREXMIT" : "FASTREXMIT") + << " by ACK-TMOUT (scheduling): " << CSeqNo::incseq(m_iSndLastAck) << "-" << csn + << " (" << CSeqNo::seqoff(m_iSndLastAck, csn) << " packets)"); } } + ++m_iReXmitCount; + + checkSndTimers(DONT_REGEN_KM); + const ECheckTimerStage stage = is_fastrexmit ? TEV_CHT_FASTREXMIT : TEV_CHT_REXMIT; + updateCC(TEV_CHECKTIMER, stage); + + // immediately restart transmission + m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); +} + +void CUDT::checkTimers() +{ + // update CC parameters + updateCC(TEV_CHECKTIMER, TEV_CHT_INIT); + //uint64_t minint = (uint64_t)(m_ullCPUFrequency * m_pSndTimeWindow->getMinPktSndInt() * 0.9); + //if (m_ullInterval_tk < minint) + // m_ullInterval_tk = minint; + // NOTE: This commented-out ^^^ code was commented out in original UDT. Leaving for historical reasons + + uint64_t currtime_tk; + CTimer::rdtsc(currtime_tk); + + // This is a very heavy log, unblock only for temporary debugging! +#if 0 + HLOGC(mglog.Debug, log << CONID() << "checkTimers: nextacktime=" << FormatTime(m_ullNextACKTime_tk) + << " AckInterval=" << m_iACKInterval + << " pkt-count=" << m_iPktCount << " liteack-count=" << m_iLightACKCount); +#endif + + // Check if it is time to send ACK + checkACKTimer(currtime_tk); + + // Check if it is time to send a loss report + checkNAKTimer(currtime_tk); + + // Check if the connection is expired + if (checkExpTimer(currtime_tk)) + return; + + // Check if FAST or LATE packet retransmission is required + checkRexmitTimer(currtime_tk); + // uint64_t exp_int = (m_iRTT + 4 * m_iRTTVar + COMM_SYN_INTERVAL_US) * m_ullCPUFrequency; if (currtime_tk > m_ullLastSndTime_tk + (COMM_KEEPALIVE_PERIOD_US * m_ullCPUFrequency)) { diff --git a/srtcore/core.h b/srtcore/core.h index 949557eab..1a9178f14 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -801,6 +801,10 @@ class CUDT uint64_t m_ullTargetTime_tk; // scheduled time of next packet sending void checkTimers(); + void checkACKTimer (uint64_t currtime_tk); + void checkNAKTimer(uint64_t currtime_tk); + bool checkExpTimer (uint64_t currtime_tk); // returns true if the connection is expired + void checkRexmitTimer(uint64_t currtime_tk); public: // For the use of CCryptoControl // HaiCrypt configuration