From 496e6a74338e8ac16fad261aeaa5343d3e16f4f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 22 Sep 2022 17:25:14 +0200 Subject: [PATCH 01/28] Exported fragment to a separate function --- srtcore/core.cpp | 517 +++++++++++++++++++++++++++-------------------- srtcore/core.h | 6 + 2 files changed, 303 insertions(+), 220 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 93224f418..1972d57c5 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9829,20 +9829,10 @@ bool srt::CUDT::overrideSndSeqNo(int32_t seq) return true; } -int srt::CUDT::processData(CUnit* in_unit) +int srt::CUDT::checkLazySpawnLatencyThread() { - if (m_bClosing) - return -1; - - CPacket &packet = in_unit->m_Packet; - - // Just heard from the peer, reset the expiration count. - m_iEXPCount = 1; - m_tsLastRspTime.store(steady_clock::now()); - const bool need_tsbpd = m_bTsbPd || m_bGroupTsbPd; - // We are receiving data, start tsbpd thread if TsbPd is enabled if (need_tsbpd && !m_RcvTsbPdThread.joinable()) { ScopedLock lock(m_RcvTsbPdStartupLock); @@ -9869,9 +9859,264 @@ int srt::CUDT::processData(CUnit* in_unit) return -1; } + return 0; +} + +CUDT::time_point srt::CUDT::getPacketPTS(void*, const CPacket& packet) +{ + return m_pRcvBuffer->getPktTsbPdTime(packet.getMsgTimeStamp()); +} + +static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; + + +int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, bool& w_reorder_prevent_lossreport, CUDT::loss_seqs_t& w_srt_loss_seqs) +{ + int initial_loss_ttl = 0; + if (m_bPeerRexmitFlag) + initial_loss_ttl = m_iReorderTolerance; + + bool excessive SRT_ATR_UNUSED = true; // stays true unless it was successfully added + w_reorder_prevent_lossreport = false; + + // Loop over all incoming packets that were filtered out. + // In case when there is no filter, there's just one packet in 'incoming', + // the one that came in the input of this function. + for (vector::const_iterator unitIt = incoming.begin(); unitIt != incoming.end(); ++unitIt) + { + CUnit * u = *unitIt; + CPacket &rpkt = u->m_Packet; + const int pktrexmitflag = m_bPeerRexmitFlag ? (rpkt.getRexmitFlag() ? 1 : 0) : 2; + + time_point pts = steady_clock::now() + milliseconds_from(m_iTsbPdDelay_ms); + IF_HEAVY_LOGGING(pts = getPacketPTS(NULL, rpkt)); + + int buffer_add_result; + bool adding_successful = true; + + // m_iRcvLastSkipAck is the base sequence number for the receiver buffer. + // This is the offset in the buffer; if this is negative, it means that + // this sequence is already in the past and the buffer is not interested. + // Meaning, this packet will be rejected, even if it could potentially be + // one of missing packets in the transmission. + int32_t offset = CSeqNo::seqoff(m_iRcvLastSkipAck, rpkt.m_iSeqNo); + + IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED"); + + if (offset < 0) + { + IF_HEAVY_LOGGING(exc_type = "BELATED"); + enterCS(m_StatsLock); + const double bltime = (double) CountIIR( + uint64_t(m_stats.traceBelatedTime) * 1000, + count_microseconds(steady_clock::now() - pts), 0.2); + + m_stats.traceBelatedTime = bltime / 1000.0; + m_stats.rcvr.recvdBelated.count(rpkt.getLength()); + leaveCS(m_StatsLock); + HLOGC(qrlog.Debug, + log << CONID() << "RECEIVED: seq=" << rpkt.m_iSeqNo << " offset=" << offset << " (BELATED/" + << rexmitstat[pktrexmitflag] << ") FLAGS: " << rpkt.MessageFlagStr()); + continue; + } + + int avail_bufsize = 0; // needed in logging + + // This is executed only when bonding is enabled and only + // with the new buffer (in which case the buffer is in the group). + + avail_bufsize = (int) getAvailRcvBufferSizeNoLock(); + if (offset >= avail_bufsize) + { + // This is already a sequence discrepancy. Probably there could be found + // some way to make it continue reception by overriding the sequence and + // make a kinda TLKPTDROP, but there has been found no reliable way to do this. + if (m_bTsbPd && m_bTLPktDrop && m_pRcvBuffer->empty()) + { + // Only in live mode. In File mode this shall not be possible + // because the sender should stop sending in this situation. + // In Live mode this means that there is a gap between the + // lowest sequence in the empty buffer and the incoming sequence + // that exceeds the buffer size. Receiving data in this situation + // is no longer possible and this is a point of no return. + + LOGC(qrlog.Error, log << CONID() << + "SEQUENCE DISCREPANCY. BREAKING CONNECTION." + " seq=" << rpkt.m_iSeqNo + << " buffer=(" << m_iRcvLastSkipAck + << ":" << m_iRcvCurrSeqNo // -1 = size to last index + << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, int(m_pRcvBuffer->capacity()) - 1) + << "), " << (offset-avail_bufsize+1) + << " past max. Reception no longer possible. REQUESTING TO CLOSE."); + + return -2; + } + else + { +#if ENABLE_NEW_RCVBUFFER + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + << ", insert offset " << offset << ". " + << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) + ); +#else + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + << ", insert offset " << offset << ". " + << m_pRcvBuffer->strFullnessState(steady_clock::now()) + ); +#endif + + return -1; + } + } + +#if ENABLE_NEW_RCVBUFFER + buffer_add_result = m_pRcvBuffer->insert(u); +#else + buffer_add_result = m_pRcvBuffer->addData(u, offset); +#endif + if (buffer_add_result < 0) + { + // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. + // So this packet is "redundant". + IF_HEAVY_LOGGING(exc_type = "UNACKED"); + adding_successful = false; + } + else + { + w_new_inserted = true; + + IF_HEAVY_LOGGING(exc_type = "ACCEPTED"); + excessive = false; + if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC) + { + EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; + if (rc != ENCS_CLEAR) + { + // Heavy log message because if seen once the message may happen very often. + HLOGC(qrlog.Debug, log << CONID() << "ERROR: packet not decrypted, dropping data."); + adding_successful = false; + IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED"); + + ScopedLock lg(m_StatsLock); + m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1)); + } + } + } + + if (adding_successful) + { + ScopedLock statslock(m_StatsLock); + m_stats.rcvr.recvdUnique.count(u->m_Packet.getLength()); + } + +#if ENABLE_HEAVY_LOGGING + std::ostringstream expectspec; + if (excessive) + expectspec << "EXCESSIVE(" << exc_type << ")"; + else + expectspec << "ACCEPTED"; + + std::ostringstream bufinfo; + + if (m_pRcvBuffer) + { + bufinfo << " BUFr=" << avail_bufsize + << " avail=" << getAvailRcvBufferSizeNoLock() + << " buffer=(" << m_iRcvLastSkipAck + << ":" << m_iRcvCurrSeqNo // -1 = size to last index + << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, m_pRcvBuffer->capacity()-1) + << ")"; + } + + // Empty buffer info in case of groupwise receiver. + // There's no way to obtain this information here. + + LOGC(qrlog.Debug, log << CONID() << "RECEIVED: seq=" << rpkt.m_iSeqNo + << " offset=" << offset + << bufinfo.str() + << " RSL=" << expectspec.str() + << " SN=" << rexmitstat[pktrexmitflag] + << " FLAGS: " + << rpkt.MessageFlagStr()); +#endif + + // Decryption should have made the crypto flags EK_NOENC. + // Otherwise it's an error. + if (adding_successful) + { + HLOGC(qrlog.Debug, + log << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); + + // Lame way to make an optional variable. + bool have_loss = false; + + if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. + { + int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); + int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); + + have_loss = true; + w_srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); + HLOGC(qrlog.Debug, log << "pkt/LOSS DETECTED: %" << seqlo << " - %" << seqhi); + } + + if (initial_loss_ttl && have_loss) + { + // pack loss list for (possibly belated) NAK + // The LOSSREPORT will be sent in a while. + + ScopedLock lg(m_RcvLossLock); + for (loss_seqs_t::iterator i = w_srt_loss_seqs.begin(); i != w_srt_loss_seqs.end(); ++i) + { + m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl)); + HLOGC(qrlog.Debug, + log << "FreshLoss: added sequences: %(" << i->first << "-" << i->second + << ") tolerance: " << initial_loss_ttl); + + } + w_reorder_prevent_lossreport = true; + } + } + + // Update the current largest sequence number that has been received. + // Or it is a retransmitted packet, remove it from receiver loss list. + // + // Group note: for the new group receiver the group hosts the receiver + // buffer, but the socket still maintains the losses. + if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0) + { + m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received + } + else + { + unlose(rpkt); // was BELATED or RETRANSMITTED + w_was_sent_in_order &= 0 != pktrexmitflag; + } + } + + return 0; +} + +int srt::CUDT::processData(CUnit* in_unit) +{ + if (m_bClosing) + return -1; + + CPacket &packet = in_unit->m_Packet; + + // Just heard from the peer, reset the expiration count. + m_iEXPCount = 1; + m_tsLastRspTime.store(steady_clock::now()); + + + // We are receiving data, start tsbpd thread if TsbPd is enabled + if (-1 == checkLazySpawnLatencyThread()) + { + return -1; + } + const int pktrexmitflag = m_bPeerRexmitFlag ? (packet.getRexmitFlag() ? 1 : 0) : 2; #if ENABLE_HEAVY_LOGGING - static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; string rexmit_reason; #endif @@ -10032,223 +10277,55 @@ int srt::CUDT::processData(CUnit* in_unit) } #endif + // NULL time by default + time_point next_tsbpd_avail; + bool new_inserted = false; + + if (m_PacketFilter) + { + // Stuff this data into the filter + m_PacketFilter.receive(in_unit, (incoming), (filter_loss_seqs)); + HLOGC(qrlog.Debug, + log << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs) + << " loss to report, " + << (m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS ? "FIND & REPORT LOSSES YOURSELF" + : "REPORT ONLY THOSE")); + } + else + { + // Stuff in just one packet that has come in. + incoming.push_back(in_unit); + } + { // Start of offset protected section // Prevent TsbPd thread from modifying Ack position while adding data // offset from RcvLastAck in RcvBuffer must remain valid between seqoff() and addData() UniqueLock recvbuf_acklock(m_RcvBufferLock); - - // vector undec_units; - if (m_PacketFilter) - { - // Stuff this data into the filter - m_PacketFilter.receive(in_unit, (incoming), (filter_loss_seqs)); - HLOGC(qrlog.Debug, - log << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs) - << " loss to report, " - << (m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS ? "FIND & REPORT LOSSES YOURSELF" - : "REPORT ONLY THOSE")); - } - else - { - // Stuff in just one packet that has come in. - incoming.push_back(in_unit); - } - - bool excessive = true; // stays true unless it was successfully added - // Needed for possibly check for needsQuickACK. bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.m_iSeqNo, m_iRcvLastSkipAck) < 0); - // Loop over all incoming packets that were filtered out. - // In case when there is no filter, there's just one packet in 'incoming', - // the one that came in the input of this function. - for (vector::iterator unitIt = incoming.begin(); unitIt != incoming.end(); ++unitIt) - { - CUnit * u = *unitIt; - CPacket &rpkt = u->m_Packet; - - // m_iRcvLastSkipAck is the base sequence number for the receiver buffer. - // This is the offset in the buffer; if this is negative, it means that - // this sequence is already in the past and the buffer is not interested. - // Meaning, this packet will be rejected, even if it could potentially be - // one of missing packets in the transmission. - int32_t offset = CSeqNo::seqoff(m_iRcvLastSkipAck, rpkt.m_iSeqNo); - - IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED"); - - if (offset < 0) - { - IF_HEAVY_LOGGING(exc_type = "BELATED"); - steady_clock::time_point tsbpdtime = m_pRcvBuffer->getPktTsbPdTime(rpkt.getMsgTimeStamp()); - const double bltime = (double) CountIIR( - uint64_t(m_stats.traceBelatedTime) * 1000, - count_microseconds(steady_clock::now() - tsbpdtime), 0.2); - - enterCS(m_StatsLock); - m_stats.traceBelatedTime = bltime / 1000.0; - m_stats.rcvr.recvdBelated.count(rpkt.getLength()); - leaveCS(m_StatsLock); - HLOGC(qrlog.Debug, - log << CONID() << "RECEIVED: seq=" << packet.m_iSeqNo << " offset=" << offset << " (BELATED/" - << rexmitstat[pktrexmitflag] << rexmit_reason << ") FLAGS: " << packet.MessageFlagStr()); - continue; - } - - const int avail_bufsize = (int) getAvailRcvBufferSizeNoLock(); - if (offset >= avail_bufsize) - { - // This is already a sequence discrepancy. Probably there could be found - // some way to make it continue reception by overriding the sequence and - // make a kinda TLKPTDROP, but there has been found no reliable way to do this. - if (m_bTsbPd && m_bTLPktDrop && m_pRcvBuffer->empty()) - { - // Only in live mode. In File mode this shall not be possible - // because the sender should stop sending in this situation. - // In Live mode this means that there is a gap between the - // lowest sequence in the empty buffer and the incoming sequence - // that exceeds the buffer size. Receiving data in this situation - // is no longer possible and this is a point of no return. - - LOGC(qrlog.Error, log << CONID() << - "SEQUENCE DISCREPANCY. BREAKING CONNECTION." - " seq=" << rpkt.m_iSeqNo - << " buffer=(" << m_iRcvLastSkipAck - << ":" << m_iRcvCurrSeqNo // -1 = size to last index - << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, int(m_pRcvBuffer->capacity()) - 1) - << "), " << (offset-avail_bufsize+1) - << " past max. Reception no longer possible. REQUESTING TO CLOSE."); - - // This is a scoped lock with AckLock, but for the moment - // when processClose() is called this lock must be taken out, - // otherwise this will cause a deadlock. We don't need this - // lock anymore, and at 'return' it will be unlocked anyway. - recvbuf_acklock.unlock(); - processClose(); - return -1; - } - else - { -#if ENABLE_NEW_RCVBUFFER - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo - << ", insert offset " << offset << ". " - << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) - ); -#else - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo - << ", insert offset " << offset << ". " - << m_pRcvBuffer->strFullnessState(steady_clock::now()) - ); -#endif - - return -1; - } - } + int res = handleSocketPacketReception(incoming, + (new_inserted), + (was_sent_in_order), + (reorder_prevent_lossreport), + (srt_loss_seqs)); - bool adding_successful = true; -#if ENABLE_NEW_RCVBUFFER - if (m_pRcvBuffer->insert(u) < 0) -#else - if (m_pRcvBuffer->addData(u, offset) < 0) -#endif - { - // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. - // So this packet is "redundant". - IF_HEAVY_LOGGING(exc_type = "UNACKED"); - adding_successful = false; - } - else - { - IF_HEAVY_LOGGING(exc_type = "ACCEPTED"); - excessive = false; - if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC) - { - EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; - if (rc != ENCS_CLEAR) - { - // Heavy log message because if seen once the message may happen very often. - HLOGC(qrlog.Debug, log << CONID() << "ERROR: packet not decrypted, dropping data."); - adding_successful = false; - IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED"); - - ScopedLock lg(m_StatsLock); - m_stats.rcvr.undecrypted.count(stats::BytesPackets(pktsz, 1)); - } - } - } - - if (adding_successful) - { - ScopedLock statslock(m_StatsLock); - m_stats.rcvr.recvdUnique.count(u->m_Packet.getLength()); - } - -#if ENABLE_HEAVY_LOGGING - std::ostringstream expectspec; - if (excessive) - expectspec << "EXCESSIVE(" << exc_type << rexmit_reason << ")"; - else - expectspec << "ACCEPTED"; - - LOGC(qrlog.Debug, log << CONID() << "RECEIVED: seq=" << rpkt.m_iSeqNo - << " offset=" << offset - << " BUFr=" << avail_bufsize - << " avail=" << getAvailRcvBufferSizeNoLock() - << " buffer=(" << m_iRcvLastSkipAck - << ":" << m_iRcvCurrSeqNo // -1 = size to last index - << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, m_pRcvBuffer->capacity()-1) - << ") " - << " RSL=" << expectspec.str() - << " SN=" << rexmitstat[pktrexmitflag] - << " FLAGS: " - << rpkt.MessageFlagStr()); -#endif - - // Decryption should have made the crypto flags EK_NOENC. - // Otherwise it's an error. - if (adding_successful) - { - // XXX move this code do CUDT::defaultPacketArrival and call it from here: - // srt_loss_seqs = CALLBACK_CALL(m_cbPacketArrival, rpkt); - - HLOGC(qrlog.Debug, - log << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); - - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. - { - int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); - int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); - - srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); - - if (initial_loss_ttl) - { - // pack loss list for (possibly belated) NAK - // The LOSSREPORT will be sent in a while. + if (res == -2) + { + // This is a scoped lock with AckLock, but for the moment + // when processClose() is called this lock must be taken out, + // otherwise this will cause a deadlock. We don't need this + // lock anymore, and at 'return' it will be unlocked anyway. + recvbuf_acklock.unlock(); + processClose(); - for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i) - { - m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl)); - } - HLOGC(qrlog.Debug, - log << "FreshLoss: added sequences: " << Printable(srt_loss_seqs) - << " tolerance: " << initial_loss_ttl); - reorder_prevent_lossreport = true; - } - } - } + return -1; + } - // Update the current largest sequence number that has been received. - // Or it is a retransmitted packet, remove it from receiver loss list. - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0) - { - m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received - } - else - { - unlose(rpkt); // was BELATED or RETRANSMITTED - was_sent_in_order &= 0 != pktrexmitflag; - } + if (res == -1) + { + return -1; } // This is moved earlier after introducing filter because it shouldn't @@ -10269,11 +10346,11 @@ int srt::CUDT::processData(CUnit* in_unit) } } - if (excessive) + if (!new_inserted) { return -1; } - } // End of recvbuf_acklock + } if (m_bClosing) { diff --git a/srtcore/core.h b/srtcore/core.h index fa58ca7c2..72946a5bf 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1062,6 +1062,12 @@ class CUDT std::pair packData(CPacket& packet); int processData(CUnit* unit); + int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, bool& w_reorder_prevent_loss, CUDT::loss_seqs_t& w_srt_loss_seqs); + + // Group passed here by void* because in the current imp it's + // unused and shall not be used in case when bonding is off + time_point getPacketPTS(void* grp, const CPacket& packet); + int checkLazySpawnLatencyThread(); void processClose(); /// Process the request after receiving the handshake from caller. From 03214ff8bf36706e0378335af8288211fcd92fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 30 Sep 2022 18:03:29 +0200 Subject: [PATCH 02/28] Applied changes from PR 2467-before merging --- srtcore/core.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 1972d57c5..4c50d30cf 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9940,14 +9940,12 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // that exceeds the buffer size. Receiving data in this situation // is no longer possible and this is a point of no return. - LOGC(qrlog.Error, log << CONID() << - "SEQUENCE DISCREPANCY. BREAKING CONNECTION." - " seq=" << rpkt.m_iSeqNo - << " buffer=(" << m_iRcvLastSkipAck - << ":" << m_iRcvCurrSeqNo // -1 = size to last index - << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, int(m_pRcvBuffer->capacity()) - 1) - << "), " << (offset-avail_bufsize+1) - << " past max. Reception no longer possible. REQUESTING TO CLOSE."); + LOGC(qrlog.Error, + log << CONID() << "SEQUENCE DISCREPANCY. BREAKING CONNECTION. seq=" << rpkt.m_iSeqNo + << " buffer=(" << m_iRcvLastAck << ":" << m_iRcvCurrSeqNo // -1 = size to last index + << "+" << CSeqNo::incseq(m_iRcvLastAck, int(m_pRcvBuffer->capacity()) - 1) << "), " + << (offset - avail_bufsize + 1) + << " past max. Reception no longer possible. REQUESTING TO CLOSE."); return -2; } @@ -10020,8 +10018,8 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& if (m_pRcvBuffer) { - bufinfo << " BUFr=" << avail_bufsize - << " avail=" << getAvailRcvBufferSizeNoLock() + bufinfo + << " avail=" << avail_bufsize << " buffer=(" << m_iRcvLastSkipAck << ":" << m_iRcvCurrSeqNo // -1 = size to last index << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, m_pRcvBuffer->capacity()-1) From 96e92dd558285ed484d69e2bbc611b2ff401fc3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 25 Oct 2022 15:51:32 +0200 Subject: [PATCH 03/28] Adjusted to the latest changes in the group branch, per changes in master --- srtcore/core.cpp | 192 +++++++++++++++++++---------------------------- srtcore/core.h | 2 +- 2 files changed, 77 insertions(+), 117 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 4c50d30cf..1355daf01 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9870,14 +9870,9 @@ CUDT::time_point srt::CUDT::getPacketPTS(void*, const CPacket& packet) static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; -int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, bool& w_reorder_prevent_lossreport, CUDT::loss_seqs_t& w_srt_loss_seqs) +int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs) { - int initial_loss_ttl = 0; - if (m_bPeerRexmitFlag) - initial_loss_ttl = m_iReorderTolerance; - bool excessive SRT_ATR_UNUSED = true; // stays true unless it was successfully added - w_reorder_prevent_lossreport = false; // Loop over all incoming packets that were filtered out. // In case when there is no filter, there's just one packet in 'incoming', @@ -9940,38 +9935,29 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // that exceeds the buffer size. Receiving data in this situation // is no longer possible and this is a point of no return. - LOGC(qrlog.Error, - log << CONID() << "SEQUENCE DISCREPANCY. BREAKING CONNECTION. seq=" << rpkt.m_iSeqNo - << " buffer=(" << m_iRcvLastAck << ":" << m_iRcvCurrSeqNo // -1 = size to last index - << "+" << CSeqNo::incseq(m_iRcvLastAck, int(m_pRcvBuffer->capacity()) - 1) << "), " - << (offset - avail_bufsize + 1) - << " past max. Reception no longer possible. REQUESTING TO CLOSE."); + LOGC(qrlog.Error, log << CONID() << + "SEQUENCE DISCREPANCY. BREAKING CONNECTION." + " seq=" << rpkt.m_iSeqNo + << " buffer=(" << m_iRcvLastSkipAck + << ":" << m_iRcvCurrSeqNo // -1 = size to last index + << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, int(m_pRcvBuffer->capacity()) - 1) + << "), " << (offset-avail_bufsize+1) + << " past max. Reception no longer possible. REQUESTING TO CLOSE."); return -2; } else { -#if ENABLE_NEW_RCVBUFFER LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo << ", insert offset " << offset << ". " << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) ); -#else - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo - << ", insert offset " << offset << ". " - << m_pRcvBuffer->strFullnessState(steady_clock::now()) - ); -#endif return -1; } } -#if ENABLE_NEW_RCVBUFFER buffer_add_result = m_pRcvBuffer->insert(u); -#else - buffer_add_result = m_pRcvBuffer->addData(u, offset); -#endif if (buffer_add_result < 0) { // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. @@ -10018,8 +10004,8 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& if (m_pRcvBuffer) { - bufinfo - << " avail=" << avail_bufsize + bufinfo << " BUFr=" << avail_bufsize + << " avail=" << getAvailRcvBufferSizeNoLock() << " buffer=(" << m_iRcvLastSkipAck << ":" << m_iRcvCurrSeqNo // -1 = size to last index << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, m_pRcvBuffer->capacity()-1) @@ -10045,35 +10031,15 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& HLOGC(qrlog.Debug, log << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); - // Lame way to make an optional variable. - bool have_loss = false; - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. { int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); - have_loss = true; w_srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); HLOGC(qrlog.Debug, log << "pkt/LOSS DETECTED: %" << seqlo << " - %" << seqhi); } - if (initial_loss_ttl && have_loss) - { - // pack loss list for (possibly belated) NAK - // The LOSSREPORT will be sent in a while. - - ScopedLock lg(m_RcvLossLock); - for (loss_seqs_t::iterator i = w_srt_loss_seqs.begin(); i != w_srt_loss_seqs.end(); ++i) - { - m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl)); - HLOGC(qrlog.Debug, - log << "FreshLoss: added sequences: %(" << i->first << "-" << i->second - << ") tolerance: " << initial_loss_ttl); - - } - w_reorder_prevent_lossreport = true; - } } // Update the current largest sequence number that has been received. @@ -10189,7 +10155,6 @@ int srt::CUDT::processData(CUnit* in_unit) loss_seqs_t srt_loss_seqs; vector incoming; bool was_sent_in_order = true; - bool reorder_prevent_lossreport = false; // If the peer doesn't understand REXMIT flag, send rexmit request // always immediately. @@ -10197,37 +10162,26 @@ int srt::CUDT::processData(CUnit* in_unit) if (m_bPeerRexmitFlag) initial_loss_ttl = m_iReorderTolerance; - // After introduction of packet filtering, the "recordable loss detection" - // does not exactly match the true loss detection. When a FEC filter is - // working, for example, then getting one group filled with all packet but - // the last one and the FEC control packet, in this special case this packet - // won't be notified at all as lost because it will be recovered by the - // filter immediately before anyone notices what happened (and the loss - // detection for the further functionality is checked only afterwards, - // and in this case the immediate recovery makes the loss to not be noticed - // at all). - // - // Because of that the check for losses must happen BEFORE passing the packet - // to the filter and before the filter could recover the packet before anyone - // notices :) - - if (packet.getMsgSeq() != SRT_MSGNO_CONTROL) // disregard filter-control packets, their seq may mean nothing - { - int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo); - // Difference between these two sequence numbers is expected to be: - // 0 - duplicated last packet (theory only) - // 1 - subsequent packet (alright) - // <0 - belated or recovered packet - // >1 - jump over a packet loss (loss = seqdiff-1) + // Track packet loss in statistics early, because a packet filter (e.g. FEC) might recover it later on, + // supply the missing packet(s), and the loss will no longer be visible for the code that follows. + if (packet.getMsgSeq(m_bPeerRexmitFlag) != SRT_MSGNO_CONTROL) // disregard filter-control packets, their seq may mean nothing + { + const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo); + // Difference between these two sequence numbers is expected to be: + // 0 - duplicated last packet (theory only) + // 1 - subsequent packet (alright) + // <0 - belated or recovered packet + // >1 - jump over a packet loss (loss = seqdiff-1) if (diff > 1) { const int loss = diff - 1; // loss is all that is above diff == 1 + ScopedLock lg(m_StatsLock); const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); m_stats.rcvr.lost.count(stats::BytesPackets(loss * avgpayloadsz, (uint32_t) loss)); HLOGC(qrlog.Debug, - log << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " + log << CONID() << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " << CSeqNo::decseq(packet.m_iSeqNo) << "]"); } @@ -10256,20 +10210,21 @@ int srt::CUDT::processData(CUnit* in_unit) // This check is needed as after getting the lock the socket // could be potentially removed. It is however granted that as long // as gi is non-NULL iterator, the group does exist and it does contain - // this socket as member (that is, 'gi' cannot be a dangling iterator). + // this socket as member (that is, 'gi' cannot be a dangling pointer). if (gi != NULL) { if (gi->rcvstate < SRT_GST_RUNNING) // PENDING or IDLE, tho PENDING is unlikely { HLOGC(qrlog.Debug, - log << "processData: IN-GROUP rcv state transition " << srt_log_grp_state[gi->rcvstate] + log << CONID() << "processData: IN-GROUP rcv state transition " << srt_log_grp_state[gi->rcvstate] << " -> RUNNING."); gi->rcvstate = SRT_GST_RUNNING; } else { - HLOGC(qrlog.Debug, log << "processData: IN-GROUP rcv state transition NOT DONE - state:" - << srt_log_grp_state[gi->rcvstate]); + HLOGC(qrlog.Debug, + log << CONID() << "processData: IN-GROUP rcv state transition NOT DONE - state:" + << srt_log_grp_state[gi->rcvstate]); } } } @@ -10284,7 +10239,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Stuff this data into the filter m_PacketFilter.receive(in_unit, (incoming), (filter_loss_seqs)); HLOGC(qrlog.Debug, - log << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs) + log << CONID() << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs) << " loss to report, " << (m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS ? "FIND & REPORT LOSSES YOURSELF" : "REPORT ONLY THOSE")); @@ -10306,7 +10261,6 @@ int srt::CUDT::processData(CUnit* in_unit) int res = handleSocketPacketReception(incoming, (new_inserted), (was_sent_in_order), - (reorder_prevent_lossreport), (srt_loss_seqs)); if (res == -2) @@ -10326,6 +10280,25 @@ int srt::CUDT::processData(CUnit* in_unit) return -1; } + if (!srt_loss_seqs.empty()) + { + ScopedLock lock(m_RcvLossLock); + + HLOGC(qrlog.Debug, + log << CONID() << "processData: RECORDING LOSS: " << Printable(srt_loss_seqs) + << " tolerance=" << initial_loss_ttl); + + for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i) + { + m_pRcvLossList->insert(i->first, i->second); + if (initial_loss_ttl) + { + // The LOSSREPORT will be sent after initial_loss_ttl. + m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl)); + } + } + } + // This is moved earlier after introducing filter because it shouldn't // be executed in case when the packet was rejected by the receiver buffer. // However now the 'excessive' condition may be true also in case when @@ -10370,37 +10343,21 @@ int srt::CUDT::processData(CUnit* in_unit) if (!srt_loss_seqs.empty()) { - // A loss is detected - { - // TODO: Can unlock rcvloss after m_pRcvLossList->insert(...)? - // And probably protect m_FreshLoss as well. - - HLOGC(qrlog.Debug, log << "processData: LOSS DETECTED, %: " << Printable(srt_loss_seqs) << " - RECORDING."); - // if record_loss == false, nothing will be contained here - // Insert lost sequence numbers to the receiver loss list - ScopedLock lg(m_RcvLossLock); - for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i) - { - // If loss found, insert them to the receiver loss list - m_pRcvLossList->insert(i->first, i->second); - } - } - const bool report_recorded_loss = !m_PacketFilter || m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS; - if (!reorder_prevent_lossreport && report_recorded_loss) + if (!initial_loss_ttl && report_recorded_loss) { - HLOGC(qrlog.Debug, log << "WILL REPORT LOSSES (SRT): " << Printable(srt_loss_seqs)); + HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (SRT): " << Printable(srt_loss_seqs)); sendLossReport(srt_loss_seqs); } if (m_bTsbPd) { - HLOGC(qrlog.Debug, log << "loss: signaling TSBPD cond"); + HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); } else { - HLOGC(qrlog.Debug, log << "loss: socket is not TSBPD, not signaling"); + HLOGC(qrlog.Debug, log << CONID() << "loss: socket is not TSBPD, not signaling"); } } @@ -10411,12 +10368,12 @@ int srt::CUDT::processData(CUnit* in_unit) // With NEVER, nothing is to be reported. if (!filter_loss_seqs.empty()) { - HLOGC(qrlog.Debug, log << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); + HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); sendLossReport(filter_loss_seqs); if (m_bTsbPd) { - HLOGC(qrlog.Debug, log << "loss: signaling TSBPD cond"); + HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); } } @@ -10781,7 +10738,7 @@ void srt::CUDT::dropFromLossLists(int32_t from, int32_t to) ScopedLock lg(m_RcvLossLock); m_pRcvLossList->remove(from, to); - HLOGF(qrlog.Debug, "%sTLPKTDROP seq %d-%d (%d packets)", CONID().c_str(), from, to, CSeqNo::seqoff(from, to)); + HLOGF(qrlog.Debug, "%sTLPKTDROP seq %d-%d (%d packets)", CONID().c_str(), from, to, CSeqNo::seqlen(from, to)); if (m_bPeerRexmitFlag == 0 || m_iReorderTolerance == 0) return; @@ -10883,12 +10840,12 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // XXX ASSUMPTIONS: // [[using assert(packet.m_iID == 0)]] - HLOGC(cnlog.Debug, log << "processConnectRequest: received a connection request"); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: received a connection request"); if (m_bClosing) { m_RejectReason = SRT_REJ_CLOSE; - HLOGC(cnlog.Debug, log << "processConnectRequest: ... NOT. Rejecting because closing."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... NOT. Rejecting because closing."); return m_RejectReason; } @@ -10900,7 +10857,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (m_bBroken) { m_RejectReason = SRT_REJ_CLOSE; - HLOGC(cnlog.Debug, log << "processConnectRequest: ... NOT. Rejecting because broken."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... NOT. Rejecting because broken."); return m_RejectReason; } // When CHandShake::m_iContentSize is used in log, the file fails to link! @@ -10916,8 +10873,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) { m_RejectReason = SRT_REJ_ROGUE; HLOGC(cnlog.Debug, - log << "processConnectRequest: ... NOT. Wrong size: " << packet.getLength() << " (expected: " << exp_len - << ")"); + log << CONID() << "processConnectRequest: ... NOT. Wrong size: " << packet.getLength() + << " (expected: " << exp_len << ")"); return m_RejectReason; } @@ -10927,7 +10884,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (!packet.isControl(UMSG_HANDSHAKE)) { m_RejectReason = SRT_REJ_ROGUE; - LOGC(cnlog.Error, log << "processConnectRequest: the packet received as handshake is not a handshake message"); + LOGC(cnlog.Error, + log << CONID() << "processConnectRequest: the packet received as handshake is not a handshake message"); return m_RejectReason; } @@ -10945,14 +10903,15 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) int32_t cookie_val = bake(addr); - HLOGC(cnlog.Debug, log << "processConnectRequest: new cookie: " << hex << cookie_val); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: new cookie: " << hex << cookie_val); // REQUEST:INDUCTION. // Set a cookie, a target ID, and send back the same as // RESPONSE:INDUCTION. if (hs.m_iReqType == URQ_INDUCTION) { - HLOGC(cnlog.Debug, log << "processConnectRequest: received type=induction, sending back with cookie+socket"); + HLOGC(cnlog.Debug, + log << CONID() << "processConnectRequest: received type=induction, sending back with cookie+socket"); // XXX That looks weird - the calculated md5 sum out of the given host/port/timestamp // is 16 bytes long, but CHandShake::m_iCookie has 4 bytes. This then effectively copies @@ -10981,7 +10940,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) hs.m_iType = SrtHSRequest::wrapFlags(true /*put SRT_MAGIC_CODE in HSFLAGS*/, m_config.iSndCryptoKeyLen); bool whether SRT_ATR_UNUSED = m_config.iSndCryptoKeyLen != 0; HLOGC(cnlog.Debug, - log << "processConnectRequest: " << (whether ? "" : "NOT ") + log << CONID() << "processConnectRequest: " << (whether ? "" : "NOT ") << " Advertising PBKEYLEN - value = " << m_config.iSndCryptoKeyLen); size_t size = packet.getLength(); @@ -10989,7 +10948,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) setPacketTS(packet, steady_clock::now()); // Display the HS before sending it to peer - HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING HS (i): " << hs.show()); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (i): " << hs.show()); m_pSndQueue->sendto(addr, packet); return SRT_REJ_UNKNOWN; // EXCEPTION: this is a "no-error" code. @@ -11002,13 +10961,14 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (!hs.valid()) { - LOGC(cnlog.Error, log << "processConnectRequest: ROGUE HS RECEIVED. Rejecting"); + LOGC(cnlog.Error, log << CONID() << "processConnectRequest: ROGUE HS RECEIVED. Rejecting"); m_RejectReason = SRT_REJ_ROGUE; return SRT_REJ_ROGUE; } HLOGC(cnlog.Debug, - log << "processConnectRequest: received type=" << RequestTypeStr(hs.m_iReqType) << " - checking cookie..."); + log << CONID() << "processConnectRequest: received type=" << RequestTypeStr(hs.m_iReqType) + << " - checking cookie..."); if (hs.m_iCookie != cookie_val) { cookie_val = bake(addr, cookie_val, -1); // SHOULD generate an earlier, distracted cookie @@ -11016,15 +10976,15 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (hs.m_iCookie != cookie_val) { m_RejectReason = SRT_REJ_RDVCOOKIE; - HLOGC(cnlog.Debug, log << "processConnectRequest: ...wrong cookie " << hex << cookie_val << ". Ignoring."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ...wrong cookie " << hex << cookie_val << ". Ignoring."); return m_RejectReason; } - HLOGC(cnlog.Debug, log << "processConnectRequest: ... correct (FIXED) cookie. Proceeding."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (FIXED) cookie. Proceeding."); } else { - HLOGC(cnlog.Debug, log << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding."); } int32_t id = hs.m_iID; @@ -11069,15 +11029,15 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (!accepted_hs) { HLOGC(cnlog.Debug, - log << "processConnectRequest: version/type mismatch. Sending REJECT code:" << m_RejectReason - << " MSG: " << srt_rejectreason_str(m_RejectReason)); + log << CONID() << "processConnectRequest: version/type mismatch. Sending REJECT code:" << m_RejectReason + << " MSG: " << srt_rejectreason_str(m_RejectReason)); // mismatch, reject the request hs.m_iReqType = URQFailure(m_RejectReason); size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); packet.m_iID = id; setPacketTS(packet, steady_clock::now()); - HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING HS (e): " << hs.show()); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (e): " << hs.show()); m_pSndQueue->sendto(addr, packet); } else diff --git a/srtcore/core.h b/srtcore/core.h index 72946a5bf..da113f93f 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1062,7 +1062,7 @@ class CUDT std::pair packData(CPacket& packet); int processData(CUnit* unit); - int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, bool& w_reorder_prevent_loss, CUDT::loss_seqs_t& w_srt_loss_seqs); + int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); // Group passed here by void* because in the current imp it's // unused and shall not be used in case when bonding is off From 89e927f99daa1371289c77bd835bfc0c8c5726bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 26 Oct 2022 17:17:58 +0200 Subject: [PATCH 04/28] Added lacking fix for AEAD --- srtcore/core.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 72fee0e45..048d3f903 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9817,7 +9817,12 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& excessive = false; if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC) { - EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; + // TODO: reset and restore the timestamp if TSBPD is disabled. + // Reset retransmission flag (must be excluded from GCM auth tag). + u->m_Packet.setRexmitFlag(false); + const EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; + u->m_Packet.setRexmitFlag(retransmitted); // Recover the flag. + if (rc != ENCS_CLEAR) { // Heavy log message because if seen once the message may happen very often. @@ -9825,6 +9830,21 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& adding_successful = false; IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED"); + if (m_config.iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) + { + // Drop a packet from the receiver buffer. + // Dropping depends on the configuration mode. If message mode is enabled, we have to drop the whole message. + // Otherwise just drop the exact packet. + if (m_config.bMessageAPI) + m_pRcvBuffer->dropMessage(SRT_SEQNO_NONE, SRT_SEQNO_NONE, u->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); + else + m_pRcvBuffer->dropMessage(u->m_Packet.getSeqNo(), u->m_Packet.getSeqNo(), SRT_MSGNO_NONE); + + LOGC(qrlog.Error, log << CONID() << "AEAD decryption failed, breaking the connection."); + m_bBroken = true; + m_iBrokenCounter = 0; + } + ScopedLock lg(m_StatsLock); m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1)); } From a56c84d5b98e3b3e96d1aea6b4d3623bab997abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 27 Oct 2022 16:05:29 +0200 Subject: [PATCH 05/28] Fixed: block static variable that might be unused when no logging (travis report) --- srtcore/core.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 048d3f903..42ac4aab9 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9711,8 +9711,7 @@ CUDT::time_point srt::CUDT::getPacketPTS(void*, const CPacket& packet) return m_pRcvBuffer->getPktTsbPdTime(packet.getMsgTimeStamp()); } -static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; - +SRT_ATR_UNUSED static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs) { From 0168c5ea583ffd2963bac90e91cbfcb97390f297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 1 Nov 2022 15:06:33 +0100 Subject: [PATCH 06/28] Fixed after PR comments --- srtcore/core.cpp | 4 ++-- srtcore/core.h | 24 +++++++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 42ac4aab9..6d12656fd 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9706,7 +9706,7 @@ int srt::CUDT::checkLazySpawnLatencyThread() return 0; } -CUDT::time_point srt::CUDT::getPacketPTS(void*, const CPacket& packet) +CUDT::time_point srt::CUDT::getPacketPlayTime(void*, const CPacket& packet) { return m_pRcvBuffer->getPktTsbPdTime(packet.getMsgTimeStamp()); } @@ -9728,7 +9728,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& const bool retransmitted = pktrexmitflag == 1; time_point pts = steady_clock::now() + milliseconds_from(m_iTsbPdDelay_ms); - IF_HEAVY_LOGGING(pts = getPacketPTS(NULL, rpkt)); + IF_HEAVY_LOGGING(pts = getPacketPlayTime(NULL, rpkt)); int buffer_add_result; bool adding_successful = true; diff --git a/srtcore/core.h b/srtcore/core.h index 93674cb3b..8cbf47650 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1053,11 +1053,29 @@ class CUDT std::pair packData(CPacket& packet); int processData(CUnit* unit); + + /// This function passes the incoming packet to the initial processing + /// (like packet filter) and is about to store it effectively to the + /// receiver buffer and do some postprocessing (decryption) if necessary + /// and report the status thereof. + /// + /// @param incoming [in] The packet coming from the network medium + /// @param w_new_inserted [out] Set false, if the packet already exists, otherwise true (packet added) + /// @param w_was_sent_in_order [out] Set false, if the packet was belated, but had no R flag set. + /// @param w_srt_loss_seqs [out] Gets inserted a loss, if this function has detected it. + /// + /// @return 0 The call was successful (regardless if the packet was accepted or not). + /// @return -1 The call has failed: no space left in the buffer. + /// @return -2 The incoming packet exceeds the expected sequence by more than a length of the buffer (irrepairable discrepancy). int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); - // Group passed here by void* because in the current imp it's - // unused and shall not be used in case when bonding is off - time_point getPacketPTS(void* grp, const CPacket& packet); + // This function is to return the packet's play time (time when + // it is submitted to the reading application) of the given packet. + // This grp passed here by void* because in the current imp it's + // unused and shall not be used in case when ENABLE_BONDING=0. + time_point getPacketPlayTime(void* grp, const CPacket& packet); + + /// Checks and spawns the TSBPD thread if required. int checkLazySpawnLatencyThread(); void processClose(); From 870509f7e2b1a5ff72eb4520912ce25058d8ddf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 3 Nov 2022 12:08:19 +0100 Subject: [PATCH 07/28] Renamed getPacketPTS. Moved call to obtain PTS under belated condition --- srtcore/core.cpp | 7 +++---- srtcore/core.h | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 6d12656fd..37a13dc94 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9706,7 +9706,7 @@ int srt::CUDT::checkLazySpawnLatencyThread() return 0; } -CUDT::time_point srt::CUDT::getPacketPlayTime(void*, const CPacket& packet) +CUDT::time_point srt::CUDT::getPktTsbPdTime(void*, const CPacket& packet) { return m_pRcvBuffer->getPktTsbPdTime(packet.getMsgTimeStamp()); } @@ -9727,9 +9727,6 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& const int pktrexmitflag = m_bPeerRexmitFlag ? (rpkt.getRexmitFlag() ? 1 : 0) : 2; const bool retransmitted = pktrexmitflag == 1; - time_point pts = steady_clock::now() + milliseconds_from(m_iTsbPdDelay_ms); - IF_HEAVY_LOGGING(pts = getPacketPlayTime(NULL, rpkt)); - int buffer_add_result; bool adding_successful = true; @@ -9745,6 +9742,8 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& if (offset < 0) { IF_HEAVY_LOGGING(exc_type = "BELATED"); + time_point pts = getPktTsbPdTime(NULL, rpkt); + enterCS(m_StatsLock); const double bltime = (double) CountIIR( uint64_t(m_stats.traceBelatedTime) * 1000, diff --git a/srtcore/core.h b/srtcore/core.h index 8cbf47650..81979bb83 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1073,7 +1073,7 @@ class CUDT // it is submitted to the reading application) of the given packet. // This grp passed here by void* because in the current imp it's // unused and shall not be used in case when ENABLE_BONDING=0. - time_point getPacketPlayTime(void* grp, const CPacket& packet); + time_point getPktTsbPdTime(void* grp, const CPacket& packet); /// Checks and spawns the TSBPD thread if required. int checkLazySpawnLatencyThread(); From 652e35da0e7a9702496f7ef9fa95049b9e13c5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 7 Nov 2022 16:56:33 +0100 Subject: [PATCH 08/28] Applied changes for improved TSBPD and receiver buffer --- srtcore/buffer_rcv.cpp | 637 ++++++++++++++++++++++++++++++++------- srtcore/buffer_rcv.h | 301 +++++++++++++++--- srtcore/core.cpp | 264 +++++++++------- srtcore/core.h | 6 +- test/test_buffer_rcv.cpp | 5 +- 5 files changed, 952 insertions(+), 261 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 7bfb00ad8..ede2033cb 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -98,7 +98,7 @@ namespace { * RcvBufferNew (circular buffer): * * |<------------------- m_iSize ----------------------------->| - * | |<----------- m_iMaxPosInc ------------>| | + * | |<----------- m_iMaxPosOff ------------>| | * | | | | * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ * | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] @@ -112,20 +112,22 @@ namespace { * thread safety: * m_iStartPos: CUDT::m_RecvLock * m_iLastAckPos: CUDT::m_AckLock - * m_iMaxPosInc: none? (modified on add and ack + * m_iMaxPosOff: none? (modified on add and ack */ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI) : m_entries(size) , m_szSize(size) // TODO: maybe just use m_entries.size() , m_pUnitQueue(unitqueue) - , m_iStartSeqNo(initSeqNo) + , m_iStartSeqNo(initSeqNo) // NOTE: SRT_SEQNO_NONE is allowed here. , m_iStartPos(0) + , m_iEndPos(0) + , m_iDropPos(0) , m_iFirstNonreadPos(0) - , m_iMaxPosInc(0) + , m_iMaxPosOff(0) , m_iNotch(0) - , m_numOutOfOrderPackets(0) - , m_iFirstReadableOutOfOrder(-1) + , m_numRandomPackets(0) + , m_iFirstRandomMsgPos(-1) , m_bPeerRexmitFlag(true) , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) @@ -137,7 +139,7 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b CRcvBuffer::~CRcvBuffer() { - // Can be optimized by only iterating m_iMaxPosInc from m_iStartPos. + // Can be optimized by only iterating m_iMaxPosOff from m_iStartPos. for (FixedArray::iterator it = m_entries.begin(); it != m_entries.end(); ++it) { if (!it->pUnit) @@ -148,7 +150,13 @@ CRcvBuffer::~CRcvBuffer() } } -int CRcvBuffer::insert(CUnit* unit) +void CRcvBuffer::debugShowState(const char* source SRT_ATR_UNUSED) +{ + HLOGC(brlog.Debug, log << "RCV-BUF-STATE(" << source << ") start=" << m_iStartPos << " end=" << m_iEndPos + << " drop=" << m_iDropPos << " max-off=+" << m_iMaxPosOff << " seq[start]=%" << m_iStartSeqNo); +} + +CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) { SRT_ASSERT(unit != NULL); const int32_t seqno = unit->m_Packet.getSeqNo(); @@ -159,53 +167,264 @@ int CRcvBuffer::insert(CUnit* unit) IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << unit->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); IF_RCVBUF_DEBUG(scoped_log.ss << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); + int32_t avail_seq; + int avail_range; + if (offset < 0) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -2"); - return -2; + return InsertInfo(InsertInfo::BELATED); } + IF_HEAVY_LOGGING(string debug_source = "insert %" + Sprint(seqno)); if (offset >= (int)capacity()) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -3"); - return -3; + + // Calculation done for the sake of possible discrepancy + // in order to inform the caller what to do. + if (m_entries[m_iStartPos].status == EntryState_Avail) + { + avail_seq = packetAt(m_iStartPos).getSeqNo(); + avail_range = m_iEndPos - m_iStartPos; + } + else if (m_iDropPos == m_iEndPos) + { + avail_seq = SRT_SEQNO_NONE; + avail_range = 0; + } + else + { + avail_seq = packetAt(m_iDropPos).getSeqNo(); + + // We don't know how many packets follow it exactly, + // but in this case it doesn't matter. We know that + // at least one is there. + avail_range = 1; + } + + IF_HEAVY_LOGGING(debugShowState((debug_source + " overflow").c_str())); + + return InsertInfo(InsertInfo::DISCREPANCY, avail_seq, avail_range); } // TODO: Don't do assert here. Process this situation somehow. // If >= 2, then probably there is a long gap, and buffer needs to be reset. SRT_ASSERT((m_iStartPos + offset) / m_szSize < 2); - const int pos = (m_iStartPos + offset) % m_szSize; - if (offset >= m_iMaxPosInc) - m_iMaxPosInc = offset + 1; + const int newpktpos = incPos(m_iStartPos, offset); + const int prev_max_off = m_iMaxPosOff; + bool extended_end = false; + if (offset >= m_iMaxPosOff) + { + m_iMaxPosOff = offset + 1; + extended_end = true; + } // Packet already exists - SRT_ASSERT(pos >= 0 && pos < int(m_szSize)); - if (m_entries[pos].status != EntryState_Empty) + // (NOTE: the above extension of m_iMaxPosOff is + // possible even before checking that the packet + // exists because existence of a packet beyond + // the current max position is not possible). + SRT_ASSERT(newpktpos >= 0 && newpktpos < int(m_szSize)); + if (m_entries[newpktpos].status != EntryState_Empty) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -1"); - return -1; + IF_HEAVY_LOGGING(debugShowState((debug_source + " redundant").c_str())); + return InsertInfo(InsertInfo::REDUNDANT); } - SRT_ASSERT(m_entries[pos].pUnit == NULL); + SRT_ASSERT(m_entries[newpktpos].pUnit == NULL); m_pUnitQueue->makeUnitTaken(unit); - m_entries[pos].pUnit = unit; - m_entries[pos].status = EntryState_Avail; + m_entries[newpktpos].pUnit = unit; + m_entries[newpktpos].status = EntryState_Avail; countBytes(1, (int)unit->m_Packet.getLength()); + // Set to a value, if due to insertion there was added + // a packet that is earlier to be retrieved than the earliest + // currently available packet. + time_point earlier_time; + + int prev_max_pos = incPos(m_iStartPos, prev_max_off); + + // Update flags + // Case [A] + if (extended_end) + { + // THIS means that the buffer WAS CONTIGUOUS BEFORE. + if (m_iEndPos == prev_max_pos) + { + // THIS means that the new packet didn't CAUSE a gap + if (m_iMaxPosOff == prev_max_off + 1) + { + // This means that m_iEndPos now shifts by 1, + // and m_iDropPos must be shifted together with it, + // as there's no drop to point. + m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); + m_iDropPos = m_iEndPos; + } + else + { + // Otherwise we have a drop-after-gap candidate + // which is the currently inserted packet. + // Therefore m_iEndPos STAYS WHERE IT IS. + m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + } + } + } + // + // Since this place, every newpktpos is in the range + // between m_iEndPos (inclusive) and a position for m_iMaxPosOff. + + // Here you can use prev_max_pos as the position represented + // by m_iMaxPosOff, as if !extended_end, it was unchanged. + else if (newpktpos == m_iEndPos) + { + // Case [D]: inserted a packet at the first gap following the + // contiguous region. This makes a potential to extend the + // contiguous region and we need to find its end. + + // If insertion happened at the very first packet, it is the + // new earliest packet now. In any other situation under this + // condition there's some contiguous packet range preceding + // this position. + if (m_iEndPos == m_iStartPos) + { + earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); + } + + updateGapInfo(prev_max_pos); + } + // XXX Not sure if that's the best performant comparison + // What is meant here is that newpktpos is between + // m_iEndPos and m_iDropPos, though we know it's after m_iEndPos. + // CONSIDER: make m_iDropPos rather m_iDropOff, this will make + // this comparison a simple subtraction. Note that offset will + // have to be updated on every shift of m_iStartPos. + else if (cmpPos(newpktpos, m_iDropPos) < 0) + { + // Case [C]: the newly inserted packet precedes the + // previous earliest delivery position after drop, + // that is, there is now a "better" after-drop delivery + // candidate. + + // New position updated a valid packet on an earlier + // position than the drop position was before, although still + // following a gap. + // + // We know it because if the position has filled a gap following + // a valid packet, this preceding valid packet would be pointed + // by m_iDropPos, or it would point to some earlier packet in a + // contiguous series of valid packets following a gap, hence + // the above condition wouldn't be satisfied. + m_iDropPos = newpktpos; + + // If there's an inserted packet BEFORE drop-pos (which makes it + // a new drop-pos), while the very first packet is absent (the + // below condition), it means we have a new earliest-available + // packet. Otherwise we would have only a newly updated drop + // position, but still following some earlier contiguous range + // of valid packets - so it's earlier than previous drop, but + // not earlier than the earliest packet. + if (m_iStartPos == m_iEndPos) + { + earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); + } + } + // OTHERWISE: case [D] in which nothing is to be updated. + // If packet "in order" flag is zero, it can be read out of order. // With TSBPD enabled packets are always assumed in order (the flag is ignored). if (!m_tsbpd.isEnabled() && m_bMessageAPI && !unit->m_Packet.getMsgOrderFlag()) { - ++m_numOutOfOrderPackets; - onInsertNotInOrderPacket(pos); + ++m_numRandomPackets; + onInsertNotInOrderPacket(newpktpos); } updateNonreadPos(); + + CPacket* avail_packet = NULL; + + if (m_entries[m_iStartPos].pUnit && m_entries[m_iStartPos].status == EntryState_Avail) + { + avail_packet = &packetAt(m_iStartPos); + avail_range = m_iEndPos - m_iStartPos; + } + else if (!m_tsbpd.isEnabled() && m_iFirstRandomMsgPos != -1) + { + // In case when TSBPD is off, we take into account the message mode + // where messages may potentially span for multiple packets, therefore + // the only "next deliverable" is the first complete message that satisfies + // the order requirement. + avail_packet = &packetAt(m_iFirstRandomMsgPos); + avail_range = 1; + } + else if (m_iDropPos != m_iEndPos) + { + avail_packet = &packetAt(m_iDropPos); + avail_range = 1; + } + IF_RCVBUF_DEBUG(scoped_log.ss << " returns 0 (OK)"); - return 0; + IF_HEAVY_LOGGING(debugShowState((debug_source + " ok").c_str())); + + if (avail_packet) + return InsertInfo(InsertInfo::INSERTED, avail_packet->getSeqNo(), avail_range, earlier_time); + else + return InsertInfo(InsertInfo::INSERTED); // No packet candidate (NOTE: impossible in live mode) } +// This function should be called after having m_iEndPos +// has somehow be set to position of a non-empty cell. +// This can happen by two reasons: +// - the cell has been filled by incoming packet +// - the value has been reset due to shifted m_iStartPos +// This means that you have to search for a new gap and +// update the m_iEndPos and m_iDropPos fields, or set them +// both to the end of range. +// +// prev_max_pos should be the position represented by m_iMaxPosOff. +// Passed because it is already calculated in insert(), otherwise +// it would have to be calculated here again. +void CRcvBuffer::updateGapInfo(int prev_max_pos) +{ + int pos = m_iEndPos; + + // First, search for the next gap, max until m_iMaxPosOff. + for ( ; pos != prev_max_pos; pos = incPos(pos)) + { + if (m_entries[pos].status == EntryState_Empty) + { + break; + } + } + if (pos == prev_max_pos) + { + // Reached the end and found no gaps. + m_iEndPos = prev_max_pos; + m_iDropPos = prev_max_pos; + } + else + { + // Found a gap at pos + m_iEndPos = pos; + m_iDropPos = pos; // fallback, although SHOULD be impossible + // So, search for the first position to drop up to. + for ( ; pos != prev_max_pos; pos = incPos(pos)) + { + if (m_entries[pos].status != EntryState_Empty) + { + m_iDropPos = pos; + break; + } + } + } +} + +/// Request to remove from the receiver buffer +/// all packets with earlier sequence than @a seqno. +/// (Meaning, the packet with given sequence shall +/// be the first packet in the buffer after the operation). int CRcvBuffer::dropUpTo(int32_t seqno) { IF_RCVBUF_DEBUG(ScopedLog scoped_log); @@ -218,9 +437,9 @@ int CRcvBuffer::dropUpTo(int32_t seqno) return 0; } - m_iMaxPosInc -= len; - if (m_iMaxPosInc < 0) - m_iMaxPosInc = 0; + m_iMaxPosOff -= len; + if (m_iMaxPosOff < 0) + m_iMaxPosOff = 0; const int iDropCnt = len; while (len > 0) @@ -235,13 +454,21 @@ int CRcvBuffer::dropUpTo(int32_t seqno) // Update positions m_iStartSeqNo = seqno; // Move forward if there are "read/drop" entries. + // (This call MAY shift m_iStartSeqNo further.) releaseNextFillerEntries(); + + // Start from here and search fort the next gap + m_iEndPos = m_iDropPos = m_iStartPos; + updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); + // Set nonread position to the starting position before updating, // because start position was increased, and preceeding packets are invalid. m_iFirstNonreadPos = m_iStartPos; updateNonreadPos(); if (!m_tsbpd.isEnabled() && m_bMessageAPI) - updateFirstReadableOutOfOrder(); + updateFirstReadableRandom(); + + IF_HEAVY_LOGGING(debugShowState(("drop %" + Sprint(seqno)).c_str())); return iDropCnt; } @@ -250,7 +477,7 @@ int CRcvBuffer::dropAll() if (empty()) return 0; - const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosInc); + const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); return dropUpTo(end_seqno); } @@ -259,7 +486,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropMessage: seqnolo " << seqnolo << " seqnohi " << seqnohi << " m_iStartSeqNo " << m_iStartSeqNo); // TODO: count bytes as removed? - const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); + const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); if (msgno > 0) // including SRT_MSGNO_NONE and SRT_MSGNO_CONTROL { IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << msgno); @@ -286,6 +513,11 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) // Check if units before m_iFirstNonreadPos are dropped. bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); releaseNextFillerEntries(); + + // Start from here and search fort the next gap + m_iEndPos = m_iDropPos = m_iStartSeqNo; + updateGapInfo(end_pos); + if (needUpdateNonreadPos) { m_iFirstNonreadPos = m_iStartPos; @@ -293,10 +525,11 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) } if (!m_tsbpd.isEnabled() && m_bMessageAPI) { - if (!checkFirstReadableOutOfOrder()) - m_iFirstReadableOutOfOrder = -1; - updateFirstReadableOutOfOrder(); + if (!checkFirstReadableRandom()) + m_iFirstRandomMsgPos = -1; + updateFirstReadableRandom(); } + IF_HEAVY_LOGGING(debugShowState(("dropmsg off %" + Sprint(seqnolo)).c_str())); return iDropCnt; } @@ -341,24 +574,47 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) } if (!m_tsbpd.isEnabled() && m_bMessageAPI) { - if (!checkFirstReadableOutOfOrder()) - m_iFirstReadableOutOfOrder = -1; - updateFirstReadableOutOfOrder(); + if (!checkFirstReadableRandom()) + m_iFirstRandomMsgPos = -1; + updateFirstReadableRandom(); } + IF_HEAVY_LOGGING(debugShowState(("dropmsg off %" + Sprint(seqnolo)).c_str())); return iDropCnt; } -int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) +bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const +{ + if (m_iStartPos == m_iEndPos) + { + // Initial contiguous region empty (including empty buffer). + HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo); + w_seq = m_iStartSeqNo; + return m_iMaxPosOff > 0; + } + + int end_off = offPos(m_iStartPos, m_iEndPos); + + w_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + + HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << end_off << " maxD=" << m_iMaxPosOff << " base=%" << m_iStartSeqNo + << " end=%" << w_seq); + + return (end_off < m_iMaxPosOff); +} + +int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair* pw_seqrange) { const bool canReadInOrder = hasReadableInorderPkts(); - if (!canReadInOrder && m_iFirstReadableOutOfOrder < 0) + if (!canReadInOrder && m_iFirstRandomMsgPos < 0) { LOGC(rbuflog.Warn, log << "CRcvBuffer.readMessage(): nothing to read. Ignored isRcvDataReady() result?"); return 0; } - const int readPos = canReadInOrder ? m_iStartPos : m_iFirstReadableOutOfOrder; + //const bool canReadInOrder = m_iFirstNonreadPos != m_iStartPos; + const int readPos = canReadInOrder ? m_iStartPos : m_iFirstRandomMsgPos; + const bool isReadingFromStart = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::readMessage. m_iStartSeqNo " << m_iStartSeqNo << " m_iStartPos " << m_iStartPos << " readPos " << readPos); @@ -367,7 +623,10 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) char* dst = data; int pkts_read = 0; int bytes_extracted = 0; // The total number of bytes extracted from the buffer. - const bool updateStartPos = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed + + int32_t out_seqlo = SRT_SEQNO_NONE; + int32_t out_seqhi = SRT_SEQNO_NONE; + for (int i = readPos;; i = incPos(i)) { SRT_ASSERT(m_entries[i].pUnit); @@ -381,6 +640,11 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) const size_t pktsize = packet.getLength(); const int32_t pktseqno = packet.getSeqNo(); + if (out_seqlo == SRT_SEQNO_NONE) + out_seqlo = pktseqno; + + out_seqhi = pktseqno; + // unitsize can be zero const size_t unitsize = std::min(remain, pktsize); memcpy(dst, packet.m_pcData, unitsize); @@ -393,8 +657,8 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) if (m_tsbpd.isEnabled()) updateTsbPdTimeBase(packet.getMsgTimeStamp()); - if (m_numOutOfOrderPackets && !packet.getMsgOrderFlag()) - --m_numOutOfOrderPackets; + if (m_numRandomPackets && !packet.getMsgOrderFlag()) + --m_numRandomPackets; const bool pbLast = packet.getMsgBoundary() & PB_LAST; if (msgctrl && (packet.getMsgBoundary() & PB_FIRST)) @@ -409,11 +673,26 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) msgctrl->pktseq = pktseqno; releaseUnitInPos(i); - if (updateStartPos) + if (isReadingFromStart) { m_iStartPos = incPos(i); - --m_iMaxPosInc; - SRT_ASSERT(m_iMaxPosInc >= 0); + --m_iMaxPosOff; + + // m_iEndPos and m_iDropPos should be + // equal to m_iStartPos only if the buffer + // is empty - but in this case the extraction will + // not be done. Otherwise m_iEndPos should + // point to the first empty cell, and m_iDropPos + // point to the first busy cell after a gap, or + // at worst be equal to m_iEndPos. + + // Therefore none of them should be updated + // because they should be constantly updated + // on an incoming packet, while this function + // should not read further than to the first + // empty cell at worst. + + SRT_ASSERT(m_iMaxPosOff >= 0); m_iStartSeqNo = CSeqNo::incseq(pktseqno); } else @@ -424,8 +703,8 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) if (pbLast) { - if (readPos == m_iFirstReadableOutOfOrder) - m_iFirstReadableOutOfOrder = -1; + if (readPos == m_iFirstRandomMsgPos) + m_iFirstRandomMsgPos = -1; break; } } @@ -434,16 +713,59 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) releaseNextFillerEntries(); - if (!isInRange(m_iStartPos, m_iMaxPosInc, m_szSize, m_iFirstNonreadPos)) + if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; //updateNonreadPos(); } + // Now that we have m_iStartPos potentially shifted, reinitialize + // m_iEndPos and m_iDropPos. + + int pend_pos = incPos(m_iStartPos, m_iMaxPosOff); + + // First check: is anything in the beginning + if (m_entries[m_iStartPos].status == EntryState_Avail) + { + // If so, shift m_iEndPos up to the first nonexistent unit + // XXX Try to optimize search by splitting into two loops if necessary. + + m_iEndPos = incPos(m_iStartPos); + while (m_entries[m_iEndPos].status == EntryState_Avail) + { + m_iEndPos = incPos(m_iEndPos); + if (m_iEndPos == pend_pos) + break; + } + + // If we had first packet available, then there's also no drop pos. + m_iDropPos = m_iEndPos; + + } + else + { + // If not, reset m_iEndPos and search for the first after-drop candidate. + m_iEndPos = m_iStartPos; + m_iDropPos = m_iEndPos; + + while (m_entries[m_iDropPos].status != EntryState_Avail) + { + m_iDropPos = incPos(m_iDropPos); + if (m_iDropPos == pend_pos) + { + // Nothing found - set drop pos equal to end pos, + // which means there's no drop + m_iDropPos = m_iEndPos; + break; + } + } + } + + if (!m_tsbpd.isEnabled()) - // We need updateFirstReadableOutOfOrder() here even if we are reading inorder, + // We need updateFirstReadableRandom() here even if we are reading inorder, // incase readable inorder packets are all read out. - updateFirstReadableOutOfOrder(); + updateFirstReadableRandom(); const int bytes_read = int(dst - data); if (bytes_read < bytes_extracted) @@ -453,6 +775,10 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) IF_RCVBUF_DEBUG(scoped_log.ss << " pldi64 " << *reinterpret_cast(data)); + if (pw_seqrange) + *pw_seqrange = make_pair(out_seqlo, out_seqhi); + + IF_HEAVY_LOGGING(debugShowState("readmsg")); return bytes_read; } @@ -530,8 +856,8 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) m_iNotch = 0; m_iStartPos = p; - --m_iMaxPosInc; - SRT_ASSERT(m_iMaxPosInc >= 0); + --m_iMaxPosOff; + SRT_ASSERT(m_iMaxPosOff >= 0); m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); } else @@ -547,7 +873,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) // Update positions // Set nonread position to the starting position before updating, // because start position was increased, and preceeding packets are invalid. - if (!isInRange(m_iStartPos, m_iMaxPosInc, m_szSize, m_iFirstNonreadPos)) + if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; } @@ -557,6 +883,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos); } + IF_HEAVY_LOGGING(debugShowState("readbuf")); return iBytesRead; } @@ -572,15 +899,12 @@ int CRcvBuffer::readBufferToFile(fstream& ofs, int len) bool CRcvBuffer::hasAvailablePackets() const { - return hasReadableInorderPkts() || (m_numOutOfOrderPackets > 0 && m_iFirstReadableOutOfOrder != -1); + return hasReadableInorderPkts() || (m_numRandomPackets > 0 && m_iFirstRandomMsgPos != -1); } int CRcvBuffer::getRcvDataSize() const { - if (m_iFirstNonreadPos >= m_iStartPos) - return m_iFirstNonreadPos - m_iStartPos; - - return int(m_szSize + m_iFirstNonreadPos - m_iStartPos); + return offPos(m_iStartPos, m_iFirstNonreadPos); } int CRcvBuffer::getTimespan_ms() const @@ -588,10 +912,10 @@ int CRcvBuffer::getTimespan_ms() const if (!m_tsbpd.isEnabled()) return 0; - if (m_iMaxPosInc == 0) + if (m_iMaxPosOff == 0) return 0; - const int lastpos = incPos(m_iStartPos, m_iMaxPosInc - 1); + const int lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); // Should not happen if TSBPD is enabled (reading out of order is not allowed). SRT_ASSERT(m_entries[lastpos].pUnit != NULL); if (m_entries[lastpos].pUnit == NULL) @@ -631,35 +955,28 @@ int CRcvBuffer::getRcvDataSize(int& bytes, int& timespan) const CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const { - const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); - for (int i = m_iStartPos; i != end_pos; i = incPos(i)) + // Check the state of the very first packet first + if (m_entries[m_iStartPos].status == EntryState_Avail) { - // TODO: Maybe check status? - if (!m_entries[i].pUnit) - continue; - - const CPacket& packet = m_entries[i].pUnit->m_Packet; - const PacketInfo info = { packet.getSeqNo(), i != m_iStartPos, getPktTsbPdTime(packet.getMsgTimeStamp()) }; - return info; + SRT_ASSERT(m_entries[m_iStartPos].pUnit); + return (PacketInfo) { m_iStartSeqNo, false /*no gap*/, getPktTsbPdTime(packetAt(m_iStartPos).getMsgTimeStamp()) }; + } + // If not, get the information from the drop + if (m_iDropPos != m_iEndPos) + { + const CPacket& pkt = packetAt(m_iDropPos); + return (PacketInfo) { pkt.getSeqNo(), true, getPktTsbPdTime(pkt.getMsgTimeStamp()) }; } - const PacketInfo info = { -1, false, time_point() }; - return info; + return (PacketInfo) { SRT_SEQNO_NONE, false, time_point() }; } std::pair CRcvBuffer::getAvailablePacketsRange() const { - const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, (int) countReadable()); + const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, offPos(m_iStartPos, m_iFirstNonreadPos)); return std::pair(m_iStartSeqNo, seqno_last); } -size_t CRcvBuffer::countReadable() const -{ - if (m_iFirstNonreadPos >= m_iStartPos) - return m_iFirstNonreadPos - m_iStartPos; - return m_szSize + m_iFirstNonreadPos - m_iStartPos; -} - bool CRcvBuffer::isRcvDataReady(time_point time_now) const { const bool haveInorderPackets = hasReadableInorderPkts(); @@ -668,8 +985,8 @@ bool CRcvBuffer::isRcvDataReady(time_point time_now) const if (haveInorderPackets) return true; - SRT_ASSERT((!m_bMessageAPI && m_numOutOfOrderPackets == 0) || m_bMessageAPI); - return (m_numOutOfOrderPackets > 0 && m_iFirstReadableOutOfOrder != -1); + SRT_ASSERT((!m_bMessageAPI && m_numRandomPackets == 0) || m_bMessageAPI); + return (m_numRandomPackets > 0 && m_iFirstRandomMsgPos != -1); } if (!haveInorderPackets) @@ -693,11 +1010,11 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no const PacketInfo info = {packet.getSeqNo(), false, time_point()}; return info; } - SRT_ASSERT((!m_bMessageAPI && m_numOutOfOrderPackets == 0) || m_bMessageAPI); - if (m_iFirstReadableOutOfOrder >= 0) + SRT_ASSERT((!m_bMessageAPI && m_numRandomPackets == 0) || m_bMessageAPI); + if (m_iFirstRandomMsgPos >= 0) { - SRT_ASSERT(m_numOutOfOrderPackets > 0); - const CPacket& packet = m_entries[m_iFirstReadableOutOfOrder].pUnit->m_Packet; + SRT_ASSERT(m_numRandomPackets > 0); + const CPacket& packet = m_entries[m_iFirstRandomMsgPos].pUnit->m_Packet; const PacketInfo info = {packet.getSeqNo(), true, time_point()}; return info; } @@ -742,9 +1059,9 @@ bool CRcvBuffer::dropUnitInPos(int pos) } else if (m_bMessageAPI && !m_entries[pos].pUnit->m_Packet.getMsgOrderFlag()) { - --m_numOutOfOrderPackets; - if (pos == m_iFirstReadableOutOfOrder) - m_iFirstReadableOutOfOrder = -1; + --m_numRandomPackets; + if (pos == m_iFirstRandomMsgPos) + m_iFirstRandomMsgPos = -1; } releaseUnitInPos(pos); return true; @@ -759,24 +1076,24 @@ void CRcvBuffer::releaseNextFillerEntries() releaseUnitInPos(pos); pos = incPos(pos); m_iStartPos = pos; - --m_iMaxPosInc; - if (m_iMaxPosInc < 0) - m_iMaxPosInc = 0; + --m_iMaxPosOff; + if (m_iMaxPosOff < 0) + m_iMaxPosOff = 0; } } // TODO: Is this function complete? There are some comments left inside. void CRcvBuffer::updateNonreadPos() { - if (m_iMaxPosInc == 0) + if (m_iMaxPosOff == 0) return; - const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); // The empty position right after the last valid entry. + const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. int pos = m_iFirstNonreadPos; while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail) { - if (m_bMessageAPI && (m_entries[pos].pUnit->m_Packet.getMsgBoundary() & PB_FIRST) == 0) + if (m_bMessageAPI && (packetAt(pos).getMsgBoundary() & PB_FIRST) == 0) break; for (int i = pos; i != end_pos; i = incPos(i)) @@ -786,8 +1103,12 @@ void CRcvBuffer::updateNonreadPos() break; } + // m_iFirstNonreadPos is moved to the first position BEHIND + // the PB_LAST packet of the message. There's no guaratnee that + // the cell at this position isn't empty. + // Check PB_LAST only in message mode. - if (!m_bMessageAPI || m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) + if (!m_bMessageAPI || packetAt(i).getMsgBoundary() & PB_LAST) { m_iFirstNonreadPos = incPos(i); break; @@ -818,7 +1139,7 @@ int CRcvBuffer::findLastMessagePkt() void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) { - if (m_numOutOfOrderPackets == 0) + if (m_numRandomPackets == 0) return; // If the following condition is true, there is already a packet, @@ -827,20 +1148,20 @@ void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) // // There might happen that the packet being added precedes the previously found one. // However, it is allowed to re bead out of order, so no need to update the position. - if (m_iFirstReadableOutOfOrder >= 0) + if (m_iFirstRandomMsgPos >= 0) return; // Just a sanity check. This function is called when a new packet is added. // So the should be unacknowledged packets. - SRT_ASSERT(m_iMaxPosInc > 0); + SRT_ASSERT(m_iMaxPosOff > 0); SRT_ASSERT(m_entries[insertPos].pUnit); - const CPacket& pkt = m_entries[insertPos].pUnit->m_Packet; + const CPacket& pkt = packetAt(insertPos); const PacketBoundary boundary = pkt.getMsgBoundary(); //if ((boundary & PB_FIRST) && (boundary & PB_LAST)) //{ // // This packet can be read out of order - // m_iFirstReadableOutOfOrder = insertPos; + // m_iFirstRandomMsgPos = insertPos; // return; //} @@ -856,18 +1177,18 @@ void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) if (firstPktPos < 0) return; - m_iFirstReadableOutOfOrder = firstPktPos; + m_iFirstRandomMsgPos = firstPktPos; return; } -bool CRcvBuffer::checkFirstReadableOutOfOrder() +bool CRcvBuffer::checkFirstReadableRandom() { - if (m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder < 0 || m_iMaxPosInc == 0) + if (m_numRandomPackets <= 0 || m_iFirstRandomMsgPos < 0 || m_iMaxPosOff == 0) return false; - const int endPos = incPos(m_iStartPos, m_iMaxPosInc); + const int endPos = incPos(m_iStartPos, m_iMaxPosOff); int msgno = -1; - for (int pos = m_iFirstReadableOutOfOrder; pos != endPos; pos = incPos(pos)) + for (int pos = m_iFirstRandomMsgPos; pos != endPos; pos = incPos(pos)) { if (!m_entries[pos].pUnit) return false; @@ -888,20 +1209,20 @@ bool CRcvBuffer::checkFirstReadableOutOfOrder() return false; } -void CRcvBuffer::updateFirstReadableOutOfOrder() +void CRcvBuffer::updateFirstReadableRandom() { - if (hasReadableInorderPkts() || m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder >= 0) + if (hasReadableInorderPkts() || m_numRandomPackets <= 0 || m_iFirstRandomMsgPos >= 0) return; - if (m_iMaxPosInc == 0) + if (m_iMaxPosOff == 0) return; // TODO: unused variable outOfOrderPktsRemain? - int outOfOrderPktsRemain = (int) m_numOutOfOrderPackets; + int outOfOrderPktsRemain = (int) m_numRandomPackets; // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosInc - 1) % m_szSize; + const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; int posFirst = -1; int posLast = -1; @@ -940,7 +1261,7 @@ void CRcvBuffer::updateFirstReadableOutOfOrder() if (boundary & PB_LAST) { - m_iFirstReadableOutOfOrder = posFirst; + m_iFirstRandomMsgPos = posFirst; return; } @@ -955,7 +1276,7 @@ int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const { // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosInc - 1) % m_szSize; + const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; if (startPos == lastPos) return -1; @@ -997,7 +1318,7 @@ int CRcvBuffer::scanNotInOrderMessageLeft(const int startPos, int msgNo) const if (!m_entries[pos].pUnit) return -1; - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { @@ -1055,19 +1376,19 @@ string CRcvBuffer::strFullnessState(bool enable_debug_log, int iFirstUnackSeqNo, if (enable_debug_log) { ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo - << " m_iStartPos=" << m_iStartPos << " m_iMaxPosInc=" << m_iMaxPosInc << ". "; + << " m_iStartPos=" << m_iStartPos << " m_iMaxPosInc=" << m_iMaxPosOff << ". "; } ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; - if (m_tsbpd.isEnabled() && m_iMaxPosInc > 0) + if (m_tsbpd.isEnabled() && m_iMaxPosOff > 0) { const PacketInfo nextValidPkt = getFirstValidPacketInfo(); ss << "(TSBPD ready in "; if (!is_zero(nextValidPkt.tsbpd_time)) { ss << count_milliseconds(nextValidPkt.tsbpd_time - tsNow) << "ms"; - const int iLastPos = incPos(m_iStartPos, m_iMaxPosInc - 1); + const int iLastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); if (m_entries[iLastPos].pUnit) { ss << ", timespan "; @@ -1116,4 +1437,92 @@ void CRcvBuffer::updRcvAvgDataSize(const steady_clock::time_point& now) m_mavg.update(now, pkts, bytes, timespan_ms); } +int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) +{ + int offset = CSeqNo::seqoff(m_iStartSeqNo, fromseq); + + // Check if it's still inside the buffer + if (offset < 0 || offset >= m_iMaxPosOff) + { + HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset << " for %" << fromseq + << " (with max=" << m_iMaxPosOff << ") - NO LOSS FOUND"); + return SRT_SEQNO_NONE; + } + + // Start position + int pos = incPos(m_iStartPos, offset); + + // Ok; likely we should stand at the m_iEndPos position. + // If this given position is earlier than this, then + // m_iEnd stands on the first loss, unless it's equal + // to the position pointed by m_iMaxPosOff. + + int32_t ret_seq = SRT_SEQNO_NONE; + int ret_off = m_iMaxPosOff; + + int end_off = offPos(m_iStartPos, m_iEndPos); + if (pos < end_off) + { + // If m_iEndPos has such a value, then there are + // no loss packets at all. + if (end_off != m_iMaxPosOff) + { + ret_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + ret_off = end_off; + } + } + else + { + // Could be strange, but just as the caller wishes: + // find the first loss since this point on + // You can't rely on m_iEndPos, you are beyond that now. + // So simply find the next hole. + + // REUSE offset as a control variable + for (; offset < m_iMaxPosOff; ++offset) + { + int pos = incPos(m_iStartPos, offset); + if (m_entries[pos].status == EntryState_Empty) + { + ret_off = offset; + ret_seq = CSeqNo::incseq(m_iStartSeqNo, offset); + break; + } + } + } + + // If found no loss, just return this value and do not + // rewrite nor look for anything. + + // Also no need to search anything if only the beginning was + // being looked for. + if (ret_seq == SRT_SEQNO_NONE || !pw_end) + return ret_seq; + + // We want also the end range, so continue from where you + // stopped. + + // Start from ret_off + 1 because we know already that ret_off + // points to an empty cell. + for (int off = ret_off + 1; off < m_iMaxPosOff; ++off) + { + int pos = incPos(m_iStartPos, off); + if (m_entries[pos].status != EntryState_Empty) + { + *pw_end = CSeqNo::incseq(m_iStartSeqNo, off - 1); + return ret_seq; + } + } + + // Fallback - this should be impossible, so issue a log. + LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << pos << " %" << CSeqNo::incseq(m_iStartSeqNo, ret_off) << " not followed by any valid cell"); + + // Return this in the last resort - this could only be a situation when + // a packet has somehow disappeared, but it contains empty cells up to the + // end of buffer occupied range. This shouldn't be possible at all because + // there must be a valid packet at least at the last occupied cell. + return SRT_SEQNO_NONE; +} + + } // namespace srt diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 52e927f22..1cdce3ad8 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -20,28 +20,193 @@ namespace srt { -/* - * Circular receiver buffer. - * - * |<------------------- m_szSize ---------------------------->| - * | |<------------ m_iMaxPosInc ----------->| | - * | | | | - * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ - * | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] - * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ - * | | - * | \__last pkt received - * | - * \___ m_iStartPos: first message to read - * - * m_pUnit[i]->status_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?) - * - * thread safety: - * start_pos_: CUDT::m_RecvLock - * first_unack_pos_: CUDT::m_AckLock - * max_pos_inc_: none? (modified on add and ack - * first_nonread_pos_: - */ +// +// Circular receiver buffer. +// +// |<------------------- m_szSize ---------------------------->| +// | |<------------ m_iMaxPosOff ----------->| | +// | | | | +// +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ +// | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] +// +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ +// | | | | +// | | | \__last pkt received +// | | | +// | | \___ m_iDropPos +// | | +// | \___ m_iEndPos +// | +// \___ m_iStartPos: first packet position in the buffer +// +// m_pUnit[i]->status_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?) +// +// thread safety: +// start_pos_: CUDT::m_RecvLock +// first_unack_pos_: CUDT::m_AckLock +// max_pos_inc_: none? (modified on add and ack +// first_nonread_pos_: +// +// +// m_iStartPos: the first packet that should be read (might be empty) +// m_iEndPos: the end of contiguous range. Empty if m_iEndPos == m_iStartPos +// m_iDropPos: a packet available for retrieval after a drop. If == m_iEndPos, no such packet. +// +// Operational rules: +// +// Initially: +// m_iStartPos = 0 +// m_iEndPos = 0 +// m_iDropPos = 0 +// +// When a packet has arrived, then depending on where it landed: +// +// 1. Position: next to the last read one and newest +// +// m_iStartPos unchanged. +// m_iEndPos shifted by 1 +// m_iDropPos = m_iEndPos +// +// 2. Position: after a loss, newest. +// +// m_iStartPos unchanged. +// m_iEndPos unchanged. +// m_iDropPos: +// - if it was == m_iEndPos, set to this +// - otherwise unchanged +// +// 3. Position: after a loss, but belated (retransmitted) -- not equal to m_iEndPos +// +// m_iStartPos unchanged. +// m_iEndPos unchanged. +// m_iDropPos: +// - if m_iDropPos == m_iEndPos, set to this +// - if m_iDropPos %> this sequence, set to this +// - otherwise unchanged +// +// 4. Position: after a loss, sealing -- seq equal to position of m_iEndPos +// +// m_iStartPos unchanged. +// m_iEndPos: +// - since this position, search the first free cell +// - if reached the end of filled region (m_iMaxPosOff), stay there. +// m_iDropPos: +// - start from the value equal to m_iEndPos +// - walk at maximum to m_iMaxPosOff +// - find the first existing packet +// NOTE: +// If there are no "after gap" packets, then m_iMaxPosOff == m_iEndPos. +// If there is one existing packet, then one loss, then one packet, it +// should be that m_iEndPos = m_iStartPos %+ 1, m_iDropPos can reach +// to m_iStartPos %+ 2 position, and m_iMaxPosOff == m_iStartPos %+ 3. +// +// To wrap up: +// +// Let's say we have the following possibilities in a general scheme: +// +// +// [D] [C] [B] [A] (insertion cases) +// | (start) --- (end) ===[gap]=== (after-loss) ... (max-pos) | +// +// WHEN INSERTING A NEW PACKET: +// +// If the incoming sequence maps to newpktpos that is: +// +// * newpktpos <% (start) : discard the packet and exit +// * newpktpos %> (size) : report discrepancy, discard and exit +// * newpktpos %> (start) and: +// * EXISTS: discard and exit (NOTE: could be also < (end)) +// [A]* seq == m_iMaxPosOff +// --> INC m_iMaxPosOff +// * m_iEndPos == previous m_iMaxPosOff +// * previous m_iMaxPosOff + 1 == m_iMaxPosOff +// --> m_iEndPos = m_iMaxPosOff +// --> m_iDropPos = m_iEndPos +// * otherwise (means the new packet caused a gap) +// --> m_iEndPos REMAINS UNCHANGED +// --> m_iDropPos = POSITION(m_iMaxPosOff) +// COMMENT: +// If this above condition isn't satisfied, then there are +// gaps, first at m_iEndPos, and m_iDropPos is at furthest +// equal to m_iMaxPosOff %- 1. The inserted packet is outside +// both the contiguous region and the following scratched region, +// so no updates on m_iEndPos and m_iDropPos are necessary. +// +// NOTE +// SINCE THIS PLACE seq cannot be a sequence of an existing packet, +// which means that earliest newpktpos == m_iEndPos, up to == m_iMaxPosOff -% 2. +// +// * otherwise (newpktpos <% max-pos): +// [D]* newpktpos == m_iEndPos: +// --> (search FIRST GAP and FIRST AFTER-GAP) +// --> m_iEndPos: increase until reaching m_iMaxPosOff +// * m_iEndPos <% m_iMaxPosOff: +// --> m_iDropPos = first VALID packet since m_iEndPos +% 1 +// * otherwise: +// --> m_iDropPos = m_iEndPos +// [B]* newpktpos %> m_iDropPos +// --> store, but do not update anything +// [C]* otherwise (newpktpos %> m_iEndPos && newpktpos <% m_iDropPos) +// --> store +// --> set m_iDropPos = newpktpos +// COMMENT: +// It is guaratneed that between m_iEndPos and m_iDropPos +// there is only a gap (series of empty cells). So wherever +// this packet lands, if it's next to m_iEndPos and before m_iDropPos +// it will be the only packet that violates the gap, hence this +// can be the only drop pos preceding the previous m_iDropPos. +// +// -- information returned to the caller should contain: +// 1. Whether adding to the buffer was successful. +// 2. Whether the "freshest" retrievable packet has been changed, that is: +// * in live mode, a newly added packet has earlier delivery time than one before +// * in stream mode, the newly added packet was at cell[0] +// * in message mode, if the newly added packet has: +// * completed the very first message +// * completed any message further than first that has out-of-order flag +// +// The information about a changed packet is important for the caller in +// live mode in order to notify the TSBPD thread. +// +// +// +// WHEN CHECKING A PACKET +// +// 1. Check the position at m_iStartPos. If there is a packet, +// return info at its position. +// +// 2. If position on m_iStartPos is empty, get the value of m_iDropPos. +// +// NOTE THAT: +// * if the buffer is empty, m_iDropPos == m_iStartPos and == m_iEndPos; +// note that m_iDropPos == m_iStartPos suffices to check that +// * if there is a packet in the buffer, but the first cell is empty, +// then m_iDropPos points to this packet, while m_iEndPos == m_iStartPos. +// Check then m_iStartPos == m_iEndPos to recognize it, and if then +// m_iDropPos isn't equal to them, you can read with dropping. +// * If cell[0] is valid, there could be only at worst cell[1] empty +// and cell[2] pointed by m_iDropPos. +// +// 3. In case of time-based checking for live mode, return empty packet info, +// if this packet's time is later than given time. +// +// WHEN EXTRACTING A PACKET +// +// 1. Extraction is only possible if there is a packet at cell[0]. +// 2. If there's no packet at cell[0], the application may request to +// drop up to the given packet, or drop the whole message up to +// the beginning of the next message. +// 3. In message mode, extraction can only extract a full message, so +// if there's no full message ready, nothing is extracted. +// 4. When the extraction region is defined, the m_iStartPos is shifted +// by the number of extracted packets. +// 5. If m_iEndPos <% m_iStartPos (after update), m_iEndPos should be +// set by searching from m_iStartPos up to m_iMaxPosOff for an empty cell. +// 6. m_iDropPos must be always updated. If m_iEndPos == m_iMaxPosOff, +// m_iDropPos is set to their value. Otherwise start from m_iEndPos +// and search a valid packet up to m_iMaxPosOff. +// 7. NOTE: m_iMaxPosOff is a delta, hence it must be set anew after update +// for m_iStartPos. +// class CRcvBuffer { @@ -54,6 +219,32 @@ class CRcvBuffer ~CRcvBuffer(); public: + + void debugShowState(const char* source); + + struct InsertInfo + { + enum Result { INSERTED = 0, REDUNDANT = -1, BELATED = -2, DISCREPANCY = -3 } result; + + // Below fields are valid only if result == INSERTED. Otherwise they have trap repro. + + int first_seq; // sequence of the first available readable packet + time_point first_time; // Time of the new, earlier packet that appeared ready, or null-time if this didn't change. + int avail_range; + + InsertInfo(Result r, int fp_seq = SRT_SEQNO_NONE, int range = 0, + time_point fp_time = time_point()) + : result(r), first_seq(fp_seq), first_time(fp_time), avail_range(range) + { + } + + InsertInfo() + : result(REDUNDANT), first_seq(SRT_SEQNO_NONE), avail_range(0) + { + } + + }; + /// Insert a unit into the buffer. /// Similar to CRcvBuffer::addData(CUnit* unit, int offset) /// @@ -63,7 +254,8 @@ class CRcvBuffer /// @return 0 on success, -1 if packet is already in buffer, -2 if packet is before m_iStartSeqNo. /// -3 if a packet is offset is ahead the buffer capacity. // TODO: Previously '-2' also meant 'already acknowledged'. Check usage of this value. - int insert(CUnit* unit); + InsertInfo insert(CUnit* unit); + void updateGapInfo(int prev_max_pos); /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). /// @param [in] seqno drop units up to this sequence number @@ -84,6 +276,17 @@ class CRcvBuffer /// @return the number of packets actually dropped. int dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno); + /// Extract the "expected next" packet sequence. + /// Extract the past-the-end sequence for the first packet + /// that is expected to arrive next with preserving the packet order. + /// If the buffer is empty or the very first cell is lacking a packet, + /// it returns the sequence assigned to the first cell. Otherwise it + /// returns the sequence representing the first empty cell (the next + /// cell to the last received packet, if there are no loss-holes). + /// @param [out] w_seq: returns the sequence (always valid) + /// @return true if this sequence is followed by any valid packets + bool getContiguousEnd(int32_t& w_seq) const; + /// Read the whole message from one or several packets. /// /// @param [in,out] data buffer to write the message into. @@ -93,7 +296,7 @@ class CRcvBuffer /// @return actual number of bytes extracted from the buffer. /// 0 if nothing to read. /// -1 on failure. - int readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl = NULL); + int readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl = NULL, std::pair* pw_seqrange = NULL); /// Read acknowledged data into a user buffer. /// @param [in, out] dst pointer to the target user buffer. @@ -179,11 +382,11 @@ class CRcvBuffer /// @note CSeqNo::seqoff(first, second) is 0 if nothing to read. std::pair getAvailablePacketsRange() const; - size_t countReadable() const; + int32_t getFirstLossSeq(int32_t fromseq, int32_t* opt_end = NULL); bool empty() const { - return (m_iMaxPosInc == 0); + return (m_iMaxPosOff == 0); } /// Return buffer capacity. @@ -195,6 +398,14 @@ class CRcvBuffer return m_szSize - 1; } + /// Returns the currently used number of cells, including + /// gaps with empty cells, or in other words, the distance + /// between the initial position and the youngest received packet. + size_t size() const + { + return m_iMaxPosOff; + } + int64_t getDrift() const { return m_tsbpd.drift(); } // TODO: make thread safe? @@ -225,6 +436,18 @@ class CRcvBuffer inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : int(m_szSize + pos2 - pos1); } + inline int cmpPos(int pos2, int pos1) const + { + // XXX maybe not the best implementation, but this keeps up to the rule + int off1 = pos1 >= m_iStartPos ? pos1 - m_iStartPos : pos1 + m_szSize - m_iStartPos; + int off2 = pos2 >= m_iStartPos ? pos2 - m_iStartPos : pos2 + m_szSize - m_iStartPos; + + return off2 - off1; + } + + // NOTE: Assumes that pUnit != NULL + CPacket& packetAt(int pos) { return m_entries[pos].pUnit->m_Packet; } + const CPacket& packetAt(int pos) const { return m_entries[pos].pUnit->m_Packet; } private: void countBytes(int pkts, int bytes); @@ -247,9 +470,9 @@ class CRcvBuffer /// Scan for availability of out of order packets. void onInsertNotInOrderPacket(int insertpos); - // Check if m_iFirstReadableOutOfOrder is still readable. - bool checkFirstReadableOutOfOrder(); - void updateFirstReadableOutOfOrder(); + // Check if m_iFirstRandomMsgPos is still readable. + bool checkFirstReadableRandom(); + void updateFirstReadableRandom(); int scanNotInOrderMessageRight(int startPos, int msgNo) const; int scanNotInOrderMessageLeft(int startPos, int msgNo) const; @@ -303,20 +526,26 @@ class CRcvBuffer //static Entry emptyEntry() { return Entry { NULL, EntryState_Empty }; } - FixedArray m_entries; + typedef FixedArray entries_t; + entries_t m_entries; const size_t m_szSize; // size of the array of units (buffer) CUnitQueue* m_pUnitQueue; // the shared unit queue int m_iStartSeqNo; int m_iStartPos; // the head position for I/O (inclusive) + int m_iEndPos; // past-the-end of the contiguous region since m_iStartPos + int m_iDropPos; // points past m_iEndPos to the first deliverable after a gap, or == m_iEndPos if no such packet int m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) - int m_iMaxPosInc; // the furthest data position - int m_iNotch; // the starting read point of the first unit + int m_iMaxPosOff; // the furthest data position + int m_iNotch; // index of the first byte to read in the first ready-to-read packet (used in file/stream mode) + + size_t m_numRandomPackets; // The number of stored packets with "inorder" flag set to false - size_t m_numOutOfOrderPackets; // The number of stored packets with "inorder" flag set to false - int m_iFirstReadableOutOfOrder; // In case of out ouf order packet, points to a position of the first such packet to - // read + /// Points to the first packet of a message that has out-of-order flag + /// and is complete (all packets from first to last are in the buffer). + /// If there is no such message in the buffer, it contains -1. + int m_iFirstRandomMsgPos; bool m_bPeerRexmitFlag; // Needed to read message number correctly const bool m_bMessageAPI; // Operation mode flag: message or stream. @@ -342,6 +571,8 @@ class CRcvBuffer time_point getTsbPdTimeBase(uint32_t usPktTimestamp) const; void updateTsbPdTimeBase(uint32_t usPktTimestamp); + bool isTsbPd() const { return m_tsbpd.isEnabled(); } + /// Form a string of the current buffer fullness state. /// number of packets acknowledged, TSBPD readiness, etc. std::string strFullnessState(bool enable_debug_log, int iFirstUnackSeqNo, const time_point& tsNow) const; diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c614844dd..68824dd8e 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -292,7 +292,7 @@ void srt::CUDT::construct() m_bPeerTsbPd = false; m_iPeerTsbPdDelay_ms = 0; m_bTsbPd = false; - m_bTsbPdAckWakeup = false; + m_bWakeOnRecv = false; m_bGroupTsbPd = false; m_bPeerTLPktDrop = false; @@ -5254,7 +5254,7 @@ void * srt::CUDT::tsbpd(void* param) CUniqueSync recvdata_lcc (self->m_RecvLock, self->m_RecvDataCond); CSync tsbpd_cc(self->m_RcvTsbPdCond, recvdata_lcc.locker()); - self->m_bTsbPdAckWakeup = true; + self->m_bWakeOnRecv = true; while (!self->m_bClosing) { steady_clock::time_point tsNextDelivery; // Next packet delivery time @@ -5272,6 +5272,12 @@ void * srt::CUDT::tsbpd(void* param) const bool is_time_to_deliver = !is_zero(info.tsbpd_time) && (tnow >= info.tsbpd_time); tsNextDelivery = info.tsbpd_time; + HLOGC(tslog.Debug, log << self->CONID() << "grp/tsbpd: packet check: %" + << info.seqno << " T=" << FormatTime(tsNextDelivery) + << " diff-now-playtime=" << FormatDuration(tnow - tsNextDelivery) + << " ready=" << is_time_to_deliver + << " ondrop=" << info.seq_gap); + if (!self->m_bTLPktDrop) { rxready = !info.seq_gap && is_time_to_deliver; @@ -5374,7 +5380,7 @@ void * srt::CUDT::tsbpd(void* param) * Buffer at head of queue is not ready to play. * Schedule wakeup when it will be. */ - self->m_bTsbPdAckWakeup = false; + self->m_bWakeOnRecv = false; HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno << " T=" << FormatTime(tsNextDelivery) << " - waiting " << count_milliseconds(timediff) << "ms"); @@ -5396,7 +5402,7 @@ void * srt::CUDT::tsbpd(void* param) * - Closing the connection */ HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: no data, scheduling wakeup at ack"); - self->m_bTsbPdAckWakeup = true; + self->m_bWakeOnRecv = true; THREAD_PAUSED(); tsbpd_cc.wait(); THREAD_RESUMED(); @@ -7782,10 +7788,28 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp m_tsLastSndTime.store(steady_clock::now()); } +bool srt::CUDT::getFirstNoncontSequence(int32_t& w_seq, string& w_log_reason) +{ + if (!m_pRcvBuffer) + { + LOGP(cnlog.Error, "IPE: ack can't be sent, buffer doesn't exist and no group membership"); + return false; + } + { + ScopedLock buflock (m_RcvBufferLock); + bool has_followers = m_pRcvBuffer->getContiguousEnd((w_seq)); + if (has_followers) + w_log_reason = "first lost"; + else + w_log_reason = "expected next"; + } + + return true; +} + int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { SRT_ASSERT(ctrlpkt.getMsgTimeStamp() != 0); - int32_t ack; // First unacknowledged packet seqnuence number (acknowledge up to ack). int nbsent = 0; int local_prevack = 0; @@ -7800,39 +7824,17 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) (void)l_saveback; // kill compiler warning: unused variable `l_saveback` [-Wunused-variable] local_prevack = m_iDebugPrevLastAck; - - string reason = "first lost"; // just for "a reason" of giving particular % for ACK #endif + string reason; // just for "a reason" of giving particular % for ACK -#if ENABLE_BONDING - dropToGroupRecvBase(); -#endif - - // The TSBPD thread may change the first lost sequence record (TLPKTDROP). - // To avoid it the m_RcvBufferLock has to be acquired. - UniqueLock bufflock(m_RcvBufferLock); - - { - // If there is no loss, the ACK is the current largest sequence number plus 1; - // Otherwise it is the smallest sequence number in the receiver loss list. - ScopedLock lock(m_RcvLossLock); - // TODO: Consider the Fresh Loss list as well!!! - ack = m_pRcvLossList->getFirstLostSeq(); - } - - // We don't need to check the length prematurely, - // if length is 0, this will return SRT_SEQNO_NONE. - // If so happened, simply use the latest received pkt + 1. - if (ack == SRT_SEQNO_NONE) - { - ack = CSeqNo::incseq(m_iRcvCurrSeqNo); - IF_HEAVY_LOGGING(reason = "expected next"); - } + int32_t ack; // First unacknowledged packet sequence number (acknowledge up to ack). + if (!getFirstNoncontSequence((ack), (reason))) + return nbsent; if (m_iRcvLastAckAck == ack) { HLOGC(xtlog.Debug, - log << CONID() << "sendCtrl(UMSG_ACK): last ACK %" << ack << "(" << reason << ") == last ACKACK"); + log << CONID() << "sendCtrl(UMSG_ACK): last ACK %" << ack << "(" << reason << ") == last ACKACK"); return nbsent; } @@ -7840,7 +7842,6 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // to save time on buffer processing and bandwidth/AS measurement, a lite ACK only feeds back an ACK number if (size == SEND_LITE_ACK) { - bufflock.unlock(); ctrlpkt.pack(UMSG_ACK, NULL, &ack, size); ctrlpkt.m_iID = m_PeerID; nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); @@ -7848,6 +7849,16 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) return nbsent; } + // Lock the group existence until this function ends. This will be useful + // also on other places. +#if ENABLE_BONDING + CUDTUnited::GroupKeeper gkeeper (uglobal(), m_parent); +#endif + + // There are new received packets to acknowledge, update related information. + /* tsbpd thread may also call ackData when skipping packet so protect code */ + UniqueLock bufflock(m_RcvBufferLock); + // IF ack %> m_iRcvLastAck // There are new received packets to acknowledge, update related information. if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0) @@ -7894,21 +7905,32 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) #endif IF_HEAVY_LOGGING(int32_t oldack = m_iRcvLastSkipAck); - // If TSBPD is enabled, then INSTEAD OF signaling m_RecvDataCond, - // signal m_RcvTsbPdCond. This will kick in the tsbpd thread, which - // will signal m_RecvDataCond when there's time to play for particular - // data packet. + // Signalling m_RecvDataCond is not dane when TSBPD is on. + // This signalling is done in file mode in order to keep the + // API reader thread sleeping until there is a "bigger portion" + // of data to read. In TSBPD mode this isn't done because every + // packet has its individual delivery time and its readiness is signed + // off by the TSBPD thread. HLOGC(xtlog.Debug, log << CONID() << "ACK: clip %" << oldack << "-%" << ack << ", REVOKED " << CSeqNo::seqoff(ack, m_iRcvLastAck) << " from RCV buffer"); if (m_bTsbPd) { - /* Newly acknowledged data, signal TsbPD thread */ + /* + There's no need to update TSBPD in the wake-on-recv state + from ACK because it is being done already in the receiver thread + when a newly inserted packet caused provision of a new candidate + that could be delivered soon. Also, this flag is only used in TSBPD + mode and can be only set to true in the TSBPD thread. + + // Newly acknowledged data, signal TsbPD thread // CUniqueSync tslcc (m_RecvLock, m_RcvTsbPdCond); - // m_bTsbPdAckWakeup is protected by m_RecvLock in the tsbpd() thread - if (m_bTsbPdAckWakeup) + // m_bWakeOnRecv is protected by m_RecvLock in the tsbpd() thread + if (m_bWakeOnRecv) tslcc.notify_one(); + + */ } else { @@ -7970,7 +7992,8 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) else { // Not possible (m_iRcvCurrSeqNo+1 <% m_iRcvLastAck ?) - LOGC(xtlog.Error, log << CONID() << "sendCtrl(UMSG_ACK): IPE: curr %" << ack << " <% last %" << m_iRcvLastAck); + LOGC(xtlog.Error, log << CONID()<< "sendCtrl(UMSG_ACK): IPE: curr(" << reason << ") %" << ack + << " <% last %" << m_iRcvLastAck); return nbsent; } @@ -8763,8 +8786,28 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) // is currently in the ACK-waiting state, it may never exit. if (m_bTsbPd) { - HLOGP(inlog.Debug, "DROPREQ: signal TSBPD"); - rcvtscc.notify_one(); + // XXX Likely this is not necessary because: + // 1. In the recv-waiting state, that is, when TSBPD thread + // sleeps forever, it will be woken up anyway on packet + // reception. + // 2. If there are any packets in the buffer and the initial + // packet cell is empty (in which situation any drop could + // occur), TSBPD thread is sleeping timely, until the playtime + // of the first "drop up to" packet. Dropping changes nothing here. + // 3. If the buffer is empty, there's nothing "to drop up to", so + // this function will not change anything in the buffer and so + // in the reception state as well. + // 4. If the TSBPD thread is waiting until a play-ready packet is + // retrieved by the API call (in which case it is also sleeping + // forever until it's woken up by the API call), it may remain + // stalled forever, if the application isn't reading the play-ready + // packet. But this means that the application got stalled anyway. + // TSBPD when woken up could at best state that there's still a + // play-ready packet that is still not retrieved and fall back + // to sleep (forever). + + //HLOGP(inlog.Debug, "DROPREQ: signal TSBPD"); + //rcvtscc.notify_one(); } } @@ -9701,14 +9744,14 @@ CUDT::time_point srt::CUDT::getPktTsbPdTime(void*, const CPacket& packet) SRT_ATR_UNUSED static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; -int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs) +int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, steady_clock::time_point& w_next_tsbpd, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs) { bool excessive SRT_ATR_UNUSED = true; // stays true unless it was successfully added // Loop over all incoming packets that were filtered out. // In case when there is no filter, there's just one packet in 'incoming', // the one that came in the input of this function. - for (vector::const_iterator unitIt = incoming.begin(); unitIt != incoming.end(); ++unitIt) + for (vector::const_iterator unitIt = incoming.begin(); unitIt != incoming.end() && !m_bBroken; ++unitIt) { CUnit * u = *unitIt; CPacket &rpkt = u->m_Packet; @@ -9787,7 +9830,26 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& } } - buffer_add_result = m_pRcvBuffer->insert(u); + CRcvBuffer::InsertInfo info = m_pRcvBuffer->insert(u); + + // Remember this value in order to CHECK if there's a need + // to request triggering TSBPD in case when TSBPD is in the + // state of waiting forever and wants to know if there's any + // possible time to wake up known earlier than that. + + // Note that in case of the "builtin group reader" (its own + // buffer), there's no need to do it here because it has also + // its own TSBPD thread. + + if (info.result == CRcvBuffer::InsertInfo::INSERTED) + { + // This may happen multiple times in the loop, so update only if earlier. + if (w_next_tsbpd == time_point() || w_next_tsbpd > info.first_time) + w_next_tsbpd = info.first_time; + w_new_inserted = true; + } + buffer_add_result = int(info.result); + if (buffer_add_result < 0) { // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. @@ -9797,8 +9859,6 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& } else { - w_new_inserted = true; - IF_HEAVY_LOGGING(exc_type = "ACCEPTED"); excessive = false; if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC) @@ -10107,6 +10167,7 @@ int srt::CUDT::processData(CUnit* in_unit) int res = handleSocketPacketReception(incoming, (new_inserted), + (next_tsbpd_avail), (was_sent_in_order), (srt_loss_seqs)); @@ -10181,6 +10242,42 @@ int srt::CUDT::processData(CUnit* in_unit) return -1; } + // 1. This is set to true in case when TSBPD during the last check + // has seen no packet candidate to ever deliver, hence it needs + // an update on that. Note that this is also false if TSBPD thread + // isn't running. + // 2. If next_tsbpd_avail is set, it means that in the buffer there is + // a new packet that precedes the previously earliest available packet. + // This means that if TSBPD was sleeping up to the time of this earliest + // delivery (after drop), this time we have received a packet to be delivered + // earlier than that, so we need to notify TSBPD immediately so that it + // updates this itself, not sleep until the previously set time. + + // The meaning of m_bWakeOnRecv: + // - m_bWakeOnRecv is set by TSBPD thread and means that it wishes to be woken up + // on every received packet. Hence we signal always if a new packet was inserted. + // - even if TSBPD doesn't wish to be woken up on every reception (because it sleeps + // until the play time of the next deliverable packet), it will be woken up when + // next_tsbpd_avail is set because it means this time is earlier than the time until + // which TSBPD sleeps, so it must be woken up prematurely. It might be more performant + // to simply update the sleeping end time of TSBPD, but there's no way to do it, so + // we simply wake TSBPD up and count on that it will update its sleeping settings. + + // XXX Consider: as CUniqueSync locks m_RecvLock, it means that the next instruction + // gets run only when TSBPD falls asleep again. Might be a good idea to record the + // TSBPD end sleeping time - as an alternative to m_bWakeOnRecv - and after locking + // a mutex check this time again and compare it against next_tsbpd_avail; might be + // that if this difference is smaller than "dirac" (could be hard to reliably compare + // this time, unless it's set from this very value), there's no need to wake the TSBPD + // thread because it will wake up on time requirement at the right time anyway. + if (m_bTsbPd && ((m_bWakeOnRecv && new_inserted) || next_tsbpd_avail != time_point())) + { + HLOGC(qrlog.Debug, log << "processData: will SIGNAL TSBPD for socket. WakeOnRecv=" << m_bWakeOnRecv + << " new_inserted=" << new_inserted << " next_tsbpd_avail=" << FormatTime(next_tsbpd_avail)); + CUniqueSync tsbpd_cc(m_RecvLock, m_RcvTsbPdCond); + tsbpd_cc.notify_all(); + } + if (incoming.empty()) { // Treat as excessive. This is when a filter cumulates packets @@ -10197,6 +10294,10 @@ int srt::CUDT::processData(CUnit* in_unit) sendLossReport(srt_loss_seqs); } + // This should not be required with the new receiver buffer because + // signalling happens on every packet reception, if it has changed the + // earliest packet position. + /* if (m_bTsbPd) { HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); @@ -10206,6 +10307,7 @@ int srt::CUDT::processData(CUnit* in_unit) { HLOGC(qrlog.Debug, log << CONID() << "loss: socket is not TSBPD, not signaling"); } + */ } // Separately report loss records of those reported by a filter. @@ -10218,6 +10320,8 @@ int srt::CUDT::processData(CUnit* in_unit) HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); sendLossReport(filter_loss_seqs); + // XXX unsure as to whether this should change anything in the TSBPD conditions. + // Might be that this trigger is not necessary. if (m_bTsbPd) { HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); @@ -10357,67 +10461,6 @@ void srt::CUDT::updateIdleLinkFrom(CUDT* source) setInitialRcvSeq(source->m_iRcvLastSkipAck); } -// XXX This function is currently unused. It should be fixed and put into use. -// See the blocked call in CUDT::processData(). -// XXX REVIEW LOCKS WHEN REACTIVATING! -srt::CUDT::loss_seqs_t srt::CUDT::defaultPacketArrival(void* vself, CPacket& pkt) -{ -// [[using affinity(m_pRcvBuffer->workerThread())]]; - CUDT* self = (CUDT*)vself; - loss_seqs_t output; - - // XXX When an alternative packet arrival callback is installed - // in case of groups, move this part to the groupwise version. - - if (self->m_parent->m_GroupOf) - { - groups::SocketData* gi = self->m_parent->m_GroupMemberData; - if (gi->rcvstate < SRT_GST_RUNNING) // PENDING or IDLE, tho PENDING is unlikely - { - HLOGC(qrlog.Debug, log << "defaultPacketArrival: IN-GROUP rcv state transition to RUNNING. NOT checking for loss"); - gi->rcvstate = SRT_GST_RUNNING; - return output; - } - } - - const int initial_loss_ttl = (self->m_bPeerRexmitFlag) ? self->m_iReorderTolerance : 0; - - int seqdiff = CSeqNo::seqcmp(pkt.m_iSeqNo, self->m_iRcvCurrSeqNo); - - HLOGC(qrlog.Debug, log << "defaultPacketArrival: checking sequence " << pkt.m_iSeqNo - << " against latest " << self->m_iRcvCurrSeqNo << " (distance: " << seqdiff << ")"); - - // Loss detection. - if (seqdiff > 1) // packet is later than the very subsequent packet - { - const int32_t seqlo = CSeqNo::incseq(self->m_iRcvCurrSeqNo); - const int32_t seqhi = CSeqNo::decseq(pkt.m_iSeqNo); - - { - // If loss found, insert them to the receiver loss list - ScopedLock lg (self->m_RcvLossLock); - self->m_pRcvLossList->insert(seqlo, seqhi); - - if (initial_loss_ttl) - { - // pack loss list for (possibly belated) NAK - // The LOSSREPORT will be sent in a while. - self->m_FreshLoss.push_back(CRcvFreshLoss(seqlo, seqhi, initial_loss_ttl)); - HLOGF(qrlog.Debug, "defaultPacketArrival: added loss sequence %d-%d (%d) with tolerance %d", seqlo, seqhi, - 1+CSeqNo::seqcmp(seqhi, seqlo), initial_loss_ttl); - } - } - - if (!initial_loss_ttl) - { - // old code; run immediately when tolerance = 0 - // or this feature isn't used because of the peer - output.push_back(make_pair(seqlo, seqhi)); - } - } - - return output; -} #endif /// This function is called when a packet has arrived, which was behind the current @@ -11089,7 +11132,10 @@ int srt::CUDT::checkNAKTimer(const steady_clock::time_point& currtime) * not knowing what to retransmit when the only NAK sent by receiver is lost, * all packets past last ACK are retransmitted (rexmitMethod() == SRM_FASTREXMIT). */ + enterCS(m_RcvLossLock); const int loss_len = m_pRcvLossList->getLossLength(); + leaveCS(m_RcvLossLock); + SRT_ASSERT(loss_len >= 0); int debug_decision = BECAUSE_NO_REASON; diff --git a/srtcore/core.h b/srtcore/core.h index 81979bb83..5bb66d436 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -661,6 +661,7 @@ class CUDT /// the receiver fresh loss list. void unlose(const CPacket& oldpacket); void dropFromLossLists(int32_t from, int32_t to); + bool getFirstNoncontSequence(int32_t& w_seq, std::string& w_log_reason); void checkSndTimers(Whether2RegenKm regen = DONT_REGEN_KM); void handshakeDone() @@ -925,7 +926,7 @@ class CUDT sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock - bool m_bTsbPdAckWakeup; // Signal TsbPd thread on Ack sent + sync::atomic m_bWakeOnRecv; // Expected to be woken up when received a packet sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining CallbackHolder m_cbAcceptHook; @@ -1061,13 +1062,14 @@ class CUDT /// /// @param incoming [in] The packet coming from the network medium /// @param w_new_inserted [out] Set false, if the packet already exists, otherwise true (packet added) + /// @param w_next_tsbpd [out] Get the TSBPD time of the earliest playable packet after insertion /// @param w_was_sent_in_order [out] Set false, if the packet was belated, but had no R flag set. /// @param w_srt_loss_seqs [out] Gets inserted a loss, if this function has detected it. /// /// @return 0 The call was successful (regardless if the packet was accepted or not). /// @return -1 The call has failed: no space left in the buffer. /// @return -2 The incoming packet exceeds the expected sequence by more than a length of the buffer (irrepairable discrepancy). - int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); + int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, sync::steady_clock::time_point& w_next_tsbpd, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); // This function is to return the packet's play time (time when // it is submitted to the reading application) of the given packet. diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 4b0d9c833..7d70d6d92 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -72,7 +72,10 @@ class CRcvBufferReadMsg EXPECT_TRUE(packet.getMsgOrderFlag()); } - return m_rcv_buffer->insert(unit); + auto info = m_rcv_buffer->insert(unit); + // XXX extra checks? + + return int(info.result); } /// @returns 0 on success, the result of rcv_buffer::insert(..) once it failed From 36c1f674f39a1c4b116c0496ed873936a8a78ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 8 Nov 2022 17:24:59 +0100 Subject: [PATCH 09/28] Updated usage of shortcuts and new names --- srtcore/buffer_rcv.cpp | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index ede2033cb..3c0ce3e5c 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -75,15 +75,15 @@ namespace { #define IF_RCVBUF_DEBUG(instr) (void)0 - // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosInc) % iSize]. + // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize]. // The right edge is included because we expect iFirstNonreadPos to be // right after the last valid packet position if all packets are available. - bool isInRange(int iStartPos, int iMaxPosInc, size_t iSize, int iFirstNonreadPos) + bool isInRange(int iStartPos, int iMaxPosOff, size_t iSize, int iFirstNonreadPos) { if (iFirstNonreadPos == iStartPos) return true; - const int iLastPos = (iStartPos + iMaxPosInc) % iSize; + const int iLastPos = (iStartPos + iMaxPosOff) % iSize; const bool isOverrun = iLastPos < iStartPos; if (isOverrun) @@ -499,7 +499,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) continue; // TODO: Break the loop if a massege has been found. No need to search further. - const int32_t msgseq = m_entries[i].pUnit->m_Packet.getMsgSeq(m_bPeerRexmitFlag); + const int32_t msgseq = packetAt(i).getMsgSeq(m_bPeerRexmitFlag); if (msgseq == msgno) { ++iDropCnt; @@ -551,7 +551,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) { // Don't drop messages, if all its packets are already in the buffer. // TODO: Don't drop a several-packet message if all packets are in the buffer. - if (m_entries[i].pUnit && m_entries[i].pUnit->m_Packet.getMsgBoundary() == PB_SOLO) + if (m_entries[i].pUnit && packetAt(i).getMsgBoundary() == PB_SOLO) continue; dropUnitInPos(i); @@ -636,7 +636,7 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pairm_Packet; + const CPacket& packet = packetAt(i); const size_t pktsize = packet.getLength(); const int32_t pktseqno = packet.getSeqNo(); @@ -828,7 +828,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) return -1; } - const srt::CPacket& pkt = m_entries[p].pUnit->m_Packet; + const srt::CPacket& pkt = packetAt(p); if (bTsbPdEnabled) { @@ -935,8 +935,8 @@ int CRcvBuffer::getTimespan_ms() const return 0; const steady_clock::time_point startstamp = - getPktTsbPdTime(m_entries[startpos].pUnit->m_Packet.getMsgTimeStamp()); - const steady_clock::time_point endstamp = getPktTsbPdTime(m_entries[lastpos].pUnit->m_Packet.getMsgTimeStamp()); + getPktTsbPdTime(packetAt(startpos).getMsgTimeStamp()); + const steady_clock::time_point endstamp = getPktTsbPdTime(packetAt(lastpos).getMsgTimeStamp()); if (endstamp < startstamp) return 0; @@ -1006,7 +1006,7 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no { if (hasInorderPackets) { - const CPacket& packet = m_entries[m_iStartPos].pUnit->m_Packet; + const CPacket& packet = packetAt(m_iStartPos); const PacketInfo info = {packet.getSeqNo(), false, time_point()}; return info; } @@ -1014,7 +1014,7 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no if (m_iFirstRandomMsgPos >= 0) { SRT_ASSERT(m_numRandomPackets > 0); - const CPacket& packet = m_entries[m_iFirstRandomMsgPos].pUnit->m_Packet; + const CPacket& packet = packetAt(m_iFirstRandomMsgPos); const PacketInfo info = {packet.getSeqNo(), true, time_point()}; return info; } @@ -1055,9 +1055,9 @@ bool CRcvBuffer::dropUnitInPos(int pos) return false; if (m_tsbpd.isEnabled()) { - updateTsbPdTimeBase(m_entries[pos].pUnit->m_Packet.getMsgTimeStamp()); + updateTsbPdTimeBase(packetAt(pos).getMsgTimeStamp()); } - else if (m_bMessageAPI && !m_entries[pos].pUnit->m_Packet.getMsgOrderFlag()) + else if (m_bMessageAPI && !packetAt(pos).getMsgOrderFlag()) { --m_numRandomPackets; if (pos == m_iFirstRandomMsgPos) @@ -1128,7 +1128,7 @@ int CRcvBuffer::findLastMessagePkt() { SRT_ASSERT(m_entries[i].pUnit); - if (m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) + if (packetAt(i).getMsgBoundary() & PB_LAST) { return i; } @@ -1193,7 +1193,7 @@ bool CRcvBuffer::checkFirstReadableRandom() if (!m_entries[pos].pUnit) return false; - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgOrderFlag()) return false; @@ -1236,7 +1236,7 @@ void CRcvBuffer::updateFirstReadableRandom() continue; } - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgOrderFlag()) // Skip in order packet { @@ -1287,7 +1287,7 @@ int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const if (!m_entries[pos].pUnit) break; - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { @@ -1376,7 +1376,7 @@ string CRcvBuffer::strFullnessState(bool enable_debug_log, int iFirstUnackSeqNo, if (enable_debug_log) { ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo - << " m_iStartPos=" << m_iStartPos << " m_iMaxPosInc=" << m_iMaxPosOff << ". "; + << " m_iStartPos=" << m_iStartPos << " m_iMaxPosOff=" << m_iMaxPosOff << ". "; } ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; @@ -1392,7 +1392,7 @@ string CRcvBuffer::strFullnessState(bool enable_debug_log, int iFirstUnackSeqNo, if (m_entries[iLastPos].pUnit) { ss << ", timespan "; - const uint32_t usPktTimestamp = m_entries[iLastPos].pUnit->m_Packet.getMsgTimeStamp(); + const uint32_t usPktTimestamp = packetAt(iLastPos).getMsgTimeStamp(); ss << count_milliseconds(m_tsbpd.getPktTsbPdTime(usPktTimestamp) - nextValidPkt.tsbpd_time); ss << " ms"; } From 8335cbec1a7477a387be35f470e068779ccef41a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 10 Nov 2022 14:58:42 +0100 Subject: [PATCH 10/28] Fixed some logs formatting --- srtcore/core.cpp | 34 +++++++++++++++++++++++----------- srtcore/packet.cpp | 22 ++++++++++++++++++---- srtcore/queue.cpp | 4 ++-- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index b480acbd3..950cb0b72 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5274,11 +5274,20 @@ void * srt::CUDT::tsbpd(void* param) const bool is_time_to_deliver = !is_zero(info.tsbpd_time) && (tnow >= info.tsbpd_time); tsNextDelivery = info.tsbpd_time; - HLOGC(tslog.Debug, log << self->CONID() << "grp/tsbpd: packet check: %" - << info.seqno << " T=" << FormatTime(tsNextDelivery) - << " diff-now-playtime=" << FormatDuration(tnow - tsNextDelivery) - << " ready=" << is_time_to_deliver - << " ondrop=" << info.seq_gap); +#if ENABLE_HEAVY_LOGGING + if (info.seqno == SRT_SEQNO_NONE) + { + HLOGC(tslog.Debug, log << self->CONID() << "sok/tsbpd: packet check: NO PACKETS"); + } + else + { + HLOGC(tslog.Debug, log << self->CONID() << "sok/tsbpd: packet check: %" + << info.seqno << " T=" << FormatTime(tsNextDelivery) + << " diff-now-playtime=" << FormatDuration(tnow - tsNextDelivery) + << " ready=" << is_time_to_deliver + << " ondrop=" << info.seq_gap); + } +#endif if (!self->m_bTLPktDrop) { @@ -5316,7 +5325,7 @@ void * srt::CUDT::tsbpd(void* param) { HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: PLAYING PACKET seq=" << info.seqno << " (belated " - << (count_milliseconds(steady_clock::now() - info.tsbpd_time)) << "ms)"); + << FormatDuration(steady_clock::now() - info.tsbpd_time) << ")"); /* * There are packets ready to be delivered * signal a waiting "recv" call if there is any data available @@ -5375,6 +5384,8 @@ void * srt::CUDT::tsbpd(void* param) tsNextDelivery = steady_clock::time_point(); // Ready to read, nothing to wait for. } + SRT_ATR_UNUSED bool wakeup_on_signal = true; + if (!is_zero(tsNextDelivery)) { IF_HEAVY_LOGGING(const steady_clock::duration timediff = tsNextDelivery - tnow); @@ -5385,9 +5396,9 @@ void * srt::CUDT::tsbpd(void* param) self->m_bWakeOnRecv = false; HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno - << " T=" << FormatTime(tsNextDelivery) << " - waiting " << count_milliseconds(timediff) << "ms"); + << " T=" << FormatTime(tsNextDelivery) << " - waiting " << FormatDuration(timediff)); THREAD_PAUSED(); - tsbpd_cc.wait_until(tsNextDelivery); + wakeup_on_signal = tsbpd_cc.wait_until(tsNextDelivery); THREAD_RESUMED(); } else @@ -5410,7 +5421,8 @@ void * srt::CUDT::tsbpd(void* param) THREAD_RESUMED(); } - HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP!!!"); + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP [" << (wakeup_on_signal ? "signal" : "timeout") << "]!!! - " + << "NOW=" << FormatTime(steady_clock::now())); } THREAD_EXIT(); HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING"); @@ -7488,8 +7500,8 @@ bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) m_dCongestionWindow = m_CongCtl->cgWindowSize(); #if ENABLE_HEAVY_LOGGING HLOGC(rslog.Debug, - log << CONID() << "updateCC: updated values from congctl: interval=" << count_microseconds(m_tdSendInterval) << " us (" - << "tk (" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" + log << CONID() << "updateCC: updated values from congctl: interval=" << FormatDuration(m_tdSendInterval) + << " (cfg:" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" << std::setprecision(3) << m_dCongestionWindow); #endif } diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index cbe4dd90d..e7ddb9db1 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -263,7 +263,7 @@ void CPacket::setLength(size_t len, size_t cap) #if ENABLE_HEAVY_LOGGING // Debug only -static std::string FormatNumbers(UDTMessageType pkttype, const int32_t* lparam, void* rparam, size_t size) +static std::string FormatNumbers(UDTMessageType pkttype, const int32_t* lparam, void* rparam, const size_t size) { // This may be changed over time, so use special interpretation // only for certain types, and still display all data, no matter @@ -288,9 +288,15 @@ static std::string FormatNumbers(UDTMessageType pkttype, const int32_t* lparam, } bool interp_as_seq = (pkttype == UMSG_LOSSREPORT || pkttype == UMSG_DROPREQ); + bool display_dec = (pkttype == UMSG_ACK || pkttype == UMSG_ACKACK || pkttype == UMSG_DROPREQ); out << " [ "; - for (size_t i = 0; i < size; ++i) + + // Will be effective only for hex/oct. + out << std::showbase; + + const size_t size32 = size/4; + for (size_t i = 0; i < size32; ++i) { int32_t val = ((int32_t*)rparam)[i]; if (interp_as_seq) @@ -302,8 +308,16 @@ static std::string FormatNumbers(UDTMessageType pkttype, const int32_t* lparam, } else { - out << std::showpos << std::hex << val << "/" << std::dec << val; + if (!display_dec) + { + out << std::hex; + out << val << "/"; + out << std::dec; + } + out << val; + } + out << " "; } out << "]"; @@ -315,7 +329,7 @@ void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, { // Set (bit-0 = 1) and (bit-1~15 = type) setControl(pkttype); - HLOGC(inlog.Debug, log << "pack: type=" << MessageTypeStr(pkttype) << FormatNumbers(pkttype, lparam, rparam, size)); + HLOGC(inlog.Debug, log << "pack: type=" << MessageTypeStr(pkttype) << " " << FormatNumbers(pkttype, lparam, rparam, size)); // Set additional information and control information field switch (pkttype) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 8d6727268..1627a0c6b 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1042,8 +1042,8 @@ bool srt::CRendezvousQueue::qualifyToHandle(EReadStatus rst, if ((rst == RST_AGAIN || i->m_iID != iDstSockID) && tsNow <= tsRepeat) { HLOGC(cnlog.Debug, - log << "RID:@" << i->m_iID << std::fixed << count_microseconds(tsNow - tsLastReq) / 1000.0 - << " ms passed since last connection request."); + log << "RID:@" << i->m_iID << " " << FormatDuration(tsNow - tsLastReq) + << " passed since last connection request."); continue; } From 4bb7b47998c5e1c82b8649b95ba2150e5bd073e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 19 Sep 2023 10:18:04 +0200 Subject: [PATCH 11/28] Added mutex spec to a function --- srtcore/core.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/srtcore/core.h b/srtcore/core.h index 6ae9c7e89..538df167c 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -668,6 +668,8 @@ class CUDT /// the receiver fresh loss list. void unlose(const CPacket& oldpacket); void dropFromLossLists(int32_t from, int32_t to); + + SRT_ATTR_EXCLUDES(m_RcvBufferLock) bool getFirstNoncontSequence(int32_t& w_seq, std::string& w_log_reason); SRT_ATTR_EXCLUDES(m_ConnectionLock) From d1798784a1e6573461a9caa493603eba63ba2fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 19 Sep 2023 13:05:27 +0200 Subject: [PATCH 12/28] Added more thread check entries --- srtcore/api.h | 2 ++ srtcore/core.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/srtcore/api.h b/srtcore/api.h index 9ba77d23a..9a5c6081c 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -123,6 +123,8 @@ class CUDTSocket void construct(); + // XXX Controversial as to whether it should be guarded by this lock. + // It is used in many places without the lock, and it is also atomic. SRT_ATTR_GUARDED_BY(m_ControlLock) sync::atomic m_Status; //< current socket state diff --git a/srtcore/core.h b/srtcore/core.h index 538df167c..b8a24032a 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -318,6 +318,7 @@ class CUDT #endif int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; } + SRT_ATTR_REQUIRES(m_RecvAckLock) int flowWindowSize() const { return m_iFlowWindowSize; } int32_t deliveryRate() const { return m_iDeliveryRate; } int bandwidth() const { return m_iBandwidth; } @@ -365,6 +366,7 @@ class CUDT /// Returns the number of packets in flight (sent, but not yet acknowledged). /// @returns The number of packets in flight belonging to the interval [0; ...) + SRT_ATTR_REQUIRES(m_RecvAckLock) int32_t getFlightSpan() const { return getFlightSpan(m_iSndLastAck, m_iSndCurrSeqNo); From 8ad69ed81243466e633bc02960ef0f87cb786da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 15 Feb 2024 13:38:20 +0100 Subject: [PATCH 13/28] Fixed a suggested uninitialized variable --- srtcore/buffer_rcv.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 488b73f35..235909e90 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -364,6 +364,11 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) avail_packet = &packetAt(m_iDropPos); avail_range = 1; } + else + { + avail_packet = NULL; + avail_range = 0; + } IF_RCVBUF_DEBUG(scoped_log.ss << " returns 0 (OK)"); IF_HEAVY_LOGGING(debugShowState((debug_source + " ok").c_str())); From bbced79792597a1d11f9051be9ac157658878fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Feb 2024 09:19:47 +0100 Subject: [PATCH 14/28] Renamed eclipsed variable --- srtcore/buffer_rcv.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 235909e90..a7693a839 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -1495,7 +1495,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) } // Start position - int pos = incPos(m_iStartPos, offset); + int frompos = incPos(m_iStartPos, offset); // Ok; likely we should stand at the m_iEndPos position. // If this given position is earlier than this, then @@ -1506,7 +1506,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) int ret_off = m_iMaxPosOff; int end_off = offPos(m_iStartPos, m_iEndPos); - if (pos < end_off) + if (frompos < end_off) { // If m_iEndPos has such a value, then there are // no loss packets at all. @@ -1560,7 +1560,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) } // Fallback - this should be impossible, so issue a log. - LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << pos << " %" << CSeqNo::incseq(m_iStartSeqNo, ret_off) << " not followed by any valid cell"); + LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << frompos << " %" << CSeqNo::incseq(m_iStartSeqNo, ret_off) << " not followed by any valid cell"); // Return this in the last resort - this could only be a situation when // a packet has somehow disappeared, but it contains empty cells up to the From 5136ca2c3eb6ffb8c6c92fe55cc154cb2513151d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Feb 2024 10:32:39 +0100 Subject: [PATCH 15/28] Added doxy description for some functions. Applied NonOrder in names for out-of-order concerns --- srtcore/buffer_rcv.cpp | 100 ++++++++++++++++++----------------------- srtcore/buffer_rcv.h | 59 +++++++++++++++++------- srtcore/utilities.h | 2 +- 3 files changed, 89 insertions(+), 72 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index a7693a839..831840967 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -126,8 +126,8 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b , m_iFirstNonreadPos(0) , m_iMaxPosOff(0) , m_iNotch(0) - , m_numRandomPackets(0) - , m_iFirstRandomMsgPos(-1) + , m_numNonOrderPackets(0) + , m_iFirstNonOrderMsgPos(-1) , m_bPeerRexmitFlag(true) , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) @@ -337,8 +337,8 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // With TSBPD enabled packets are always assumed in order (the flag is ignored). if (!m_tsbpd.isEnabled() && m_bMessageAPI && !unit->m_Packet.getMsgOrderFlag()) { - ++m_numRandomPackets; - onInsertNotInOrderPacket(newpktpos); + ++m_numNonOrderPackets; + onInsertNonOrderPacket(newpktpos); } updateNonreadPos(); @@ -350,13 +350,13 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) avail_packet = &packetAt(m_iStartPos); avail_range = offPos(m_iStartPos, m_iEndPos); } - else if (!m_tsbpd.isEnabled() && m_iFirstRandomMsgPos != -1) + else if (!m_tsbpd.isEnabled() && m_iFirstNonOrderMsgPos != -1) { // In case when TSBPD is off, we take into account the message mode // where messages may potentially span for multiple packets, therefore // the only "next deliverable" is the first complete message that satisfies // the order requirement. - avail_packet = &packetAt(m_iFirstRandomMsgPos); + avail_packet = &packetAt(m_iFirstNonOrderMsgPos); avail_range = 1; } else if (m_iDropPos != m_iEndPos) @@ -379,18 +379,6 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) return InsertInfo(InsertInfo::INSERTED); // No packet candidate (NOTE: impossible in live mode) } -// This function should be called after having m_iEndPos -// has somehow be set to position of a non-empty cell. -// This can happen by two reasons: -// - the cell has been filled by incoming packet -// - the value has been reset due to shifted m_iStartPos -// This means that you have to search for a new gap and -// update the m_iEndPos and m_iDropPos fields, or set them -// both to the end of range. -// -// prev_max_pos should be the position represented by m_iMaxPosOff. -// Passed because it is already calculated in insert(), otherwise -// it would have to be calculated here again. void CRcvBuffer::updateGapInfo(int prev_max_pos) { int pos = m_iEndPos; @@ -474,7 +462,7 @@ int CRcvBuffer::dropUpTo(int32_t seqno) updateNonreadPos(); } if (!m_tsbpd.isEnabled() && m_bMessageAPI) - updateFirstReadableRandom(); + updateFirstReadableNonOrder(); IF_HEAVY_LOGGING(debugShowState(("drop %" + Sprint(seqno)).c_str())); return iDropCnt; @@ -613,9 +601,9 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro } if (!m_tsbpd.isEnabled() && m_bMessageAPI) { - if (!checkFirstReadableRandom()) - m_iFirstRandomMsgPos = -1; - updateFirstReadableRandom(); + if (!checkFirstReadableNonOrder()) + m_iFirstNonOrderMsgPos = -1; + updateFirstReadableNonOrder(); } IF_HEAVY_LOGGING(debugShowState(("dropmsg off %" + Sprint(seqnolo)).c_str())); @@ -645,14 +633,14 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair* pw_seqrange) { const bool canReadInOrder = hasReadableInorderPkts(); - if (!canReadInOrder && m_iFirstRandomMsgPos < 0) + if (!canReadInOrder && m_iFirstNonOrderMsgPos < 0) { LOGC(rbuflog.Warn, log << "CRcvBuffer.readMessage(): nothing to read. Ignored isRcvDataReady() result?"); return 0; } //const bool canReadInOrder = m_iFirstNonreadPos != m_iStartPos; - const int readPos = canReadInOrder ? m_iStartPos : m_iFirstRandomMsgPos; + const int readPos = canReadInOrder ? m_iStartPos : m_iFirstNonOrderMsgPos; const bool isReadingFromStart = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed IF_RCVBUF_DEBUG(ScopedLog scoped_log); @@ -696,8 +684,8 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair 0 && m_iFirstRandomMsgPos != -1); + return hasReadableInorderPkts() || (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != -1); } int CRcvBuffer::getRcvDataSize() const @@ -1028,8 +1016,8 @@ bool CRcvBuffer::isRcvDataReady(time_point time_now) const if (haveInorderPackets) return true; - SRT_ASSERT((!m_bMessageAPI && m_numRandomPackets == 0) || m_bMessageAPI); - return (m_numRandomPackets > 0 && m_iFirstRandomMsgPos != -1); + SRT_ASSERT((!m_bMessageAPI && m_numNonOrderPackets == 0) || m_bMessageAPI); + return (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != -1); } if (!haveInorderPackets) @@ -1053,11 +1041,11 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no const PacketInfo info = {packet.getSeqNo(), false, time_point()}; return info; } - SRT_ASSERT((!m_bMessageAPI && m_numRandomPackets == 0) || m_bMessageAPI); - if (m_iFirstRandomMsgPos >= 0) + SRT_ASSERT((!m_bMessageAPI && m_numNonOrderPackets == 0) || m_bMessageAPI); + if (m_iFirstNonOrderMsgPos >= 0) { - SRT_ASSERT(m_numRandomPackets > 0); - const CPacket& packet = packetAt(m_iFirstRandomMsgPos); + SRT_ASSERT(m_numNonOrderPackets > 0); + const CPacket& packet = packetAt(m_iFirstNonOrderMsgPos); const PacketInfo info = {packet.getSeqNo(), true, time_point()}; return info; } @@ -1107,9 +1095,9 @@ bool CRcvBuffer::dropUnitInPos(int pos) } else if (m_bMessageAPI && !packetAt(pos).getMsgOrderFlag()) { - --m_numRandomPackets; - if (pos == m_iFirstRandomMsgPos) - m_iFirstRandomMsgPos = -1; + --m_numNonOrderPackets; + if (pos == m_iFirstNonOrderMsgPos) + m_iFirstNonOrderMsgPos = -1; } releaseUnitInPos(pos); return true; @@ -1185,9 +1173,9 @@ int CRcvBuffer::findLastMessagePkt() return -1; } -void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) +void CRcvBuffer::onInsertNonOrderPacket(int insertPos) { - if (m_numRandomPackets == 0) + if (m_numNonOrderPackets == 0) return; // If the following condition is true, there is already a packet, @@ -1196,7 +1184,7 @@ void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) // // There might happen that the packet being added precedes the previously found one. // However, it is allowed to re bead out of order, so no need to update the position. - if (m_iFirstRandomMsgPos >= 0) + if (m_iFirstNonOrderMsgPos >= 0) return; // Just a sanity check. This function is called when a new packet is added. @@ -1209,34 +1197,34 @@ void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) //if ((boundary & PB_FIRST) && (boundary & PB_LAST)) //{ // // This packet can be read out of order - // m_iFirstRandomMsgPos = insertPos; + // m_iFirstNonOrderMsgPos = insertPos; // return; //} const int msgNo = pkt.getMsgSeq(m_bPeerRexmitFlag); // First check last packet, because it is expected to be received last. - const bool hasLast = (boundary & PB_LAST) || (-1 < scanNotInOrderMessageRight(insertPos, msgNo)); + const bool hasLast = (boundary & PB_LAST) || (-1 < scanNonOrderMessageRight(insertPos, msgNo)); if (!hasLast) return; const int firstPktPos = (boundary & PB_FIRST) ? insertPos - : scanNotInOrderMessageLeft(insertPos, msgNo); + : scanNonOrderMessageLeft(insertPos, msgNo); if (firstPktPos < 0) return; - m_iFirstRandomMsgPos = firstPktPos; + m_iFirstNonOrderMsgPos = firstPktPos; return; } -bool CRcvBuffer::checkFirstReadableRandom() +bool CRcvBuffer::checkFirstReadableNonOrder() { - if (m_numRandomPackets <= 0 || m_iFirstRandomMsgPos < 0 || m_iMaxPosOff == 0) + if (m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos < 0 || m_iMaxPosOff == 0) return false; const int endPos = incPos(m_iStartPos, m_iMaxPosOff); int msgno = -1; - for (int pos = m_iFirstRandomMsgPos; pos != endPos; pos = incPos(pos)) + for (int pos = m_iFirstNonOrderMsgPos; pos != endPos; pos = incPos(pos)) { if (!m_entries[pos].pUnit) return false; @@ -1257,16 +1245,16 @@ bool CRcvBuffer::checkFirstReadableRandom() return false; } -void CRcvBuffer::updateFirstReadableRandom() +void CRcvBuffer::updateFirstReadableNonOrder() { - if (hasReadableInorderPkts() || m_numRandomPackets <= 0 || m_iFirstRandomMsgPos >= 0) + if (hasReadableInorderPkts() || m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos >= 0) return; if (m_iMaxPosOff == 0) return; // TODO: unused variable outOfOrderPktsRemain? - int outOfOrderPktsRemain = (int) m_numRandomPackets; + int outOfOrderPktsRemain = (int) m_numNonOrderPackets; // Search further packets to the right. // First check if there are packets to the right. @@ -1309,7 +1297,7 @@ void CRcvBuffer::updateFirstReadableRandom() if (boundary & PB_LAST) { - m_iFirstRandomMsgPos = posFirst; + m_iFirstNonOrderMsgPos = posFirst; return; } @@ -1320,7 +1308,7 @@ void CRcvBuffer::updateFirstReadableRandom() return; } -int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const +int CRcvBuffer::scanNonOrderMessageRight(const int startPos, int msgNo) const { // Search further packets to the right. // First check if there are packets to the right. @@ -1351,7 +1339,7 @@ int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const return -1; } -int CRcvBuffer::scanNotInOrderMessageLeft(const int startPos, int msgNo) const +int CRcvBuffer::scanNonOrderMessageLeft(const int startPos, int msgNo) const { // Search preceding packets to the left. // First check if there are packets to the left. diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 22393dbcd..70099d355 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -244,16 +244,45 @@ class CRcvBuffer }; - /// Insert a unit into the buffer. - /// Similar to CRcvBuffer::addData(CUnit* unit, int offset) + /// Inserts the unit with the data packet into the receiver buffer. + /// The result inform about the situation with the packet attempted + /// to be inserted and the readability of the buffer. /// - /// @param [in] unit pointer to a data unit containing new packet - /// @param [in] offset offset from last ACK point. + /// @param [PASS] unit The unit that should be placed in the buffer + /// + /// @return The InsertInfo structure where: + /// * result: the result of insertion, which is: + /// * INSERTED: successfully placed in the buffer + /// * REDUNDANT: not placed, the packet is already there + /// * BELATED: not placed, its sequence is in the past + /// * DISCREPANCY: not placed, the sequence is far future or OOTB + /// * first_seq: the earliest sequence number now avail for reading + /// * avail_range: how many packets are available for reading (1 if unknown) + /// * first_time: the play time of the earliest read-available packet + /// If there is no available packet for reading, first_seq == SRT_SEQNO_NONE. /// - /// @return 0 on success, -1 if packet is already in buffer, -2 if packet is before m_iStartSeqNo. - /// -3 if a packet is offset is ahead the buffer capacity. - // TODO: Previously '-2' also meant 'already acknowledged'. Check usage of this value. InsertInfo insert(CUnit* unit); + + /// Update the values of `m_iEndPos` and `m_iDropPos` in + /// case when `m_iEndPos` was updated to a position of a + /// nonempty cell. + /// + /// This function should be called after having m_iEndPos + /// has somehow be set to position of a non-empty cell. + /// This can happen by two reasons: + /// + /// - the cell has been filled by incoming packet + /// - the value has been reset due to shifted m_iStartPos + /// + /// This means that you have to search for a new gap and + /// update the m_iEndPos and m_iDropPos fields, or set them + /// both to the end of range if there are no loss gaps. + /// + /// The @a prev_max_pos parameter is passed here because it is already + /// calculated in insert(), otherwise it would have to be calculated here again. + /// + /// @param prev_max_pos buffer position represented by `m_iMaxPosOff` + /// void updateGapInfo(int prev_max_pos); /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). @@ -495,12 +524,12 @@ class CRcvBuffer int findLastMessagePkt(); /// Scan for availability of out of order packets. - void onInsertNotInOrderPacket(int insertpos); - // Check if m_iFirstRandomMsgPos is still readable. - bool checkFirstReadableRandom(); - void updateFirstReadableRandom(); - int scanNotInOrderMessageRight(int startPos, int msgNo) const; - int scanNotInOrderMessageLeft(int startPos, int msgNo) const; + void onInsertNonOrderPacket(int insertpos); + // Check if m_iFirstNonOrderMsgPos is still readable. + bool checkFirstReadableNonOrder(); + void updateFirstReadableNonOrder(); + int scanNonOrderMessageRight(int startPos, int msgNo) const; + int scanNonOrderMessageLeft(int startPos, int msgNo) const; typedef bool copy_to_dst_f(char* data, int len, int dst_offset, void* arg); @@ -566,12 +595,12 @@ class CRcvBuffer int m_iMaxPosOff; // the furthest data position int m_iNotch; // index of the first byte to read in the first ready-to-read packet (used in file/stream mode) - size_t m_numRandomPackets; // The number of stored packets with "inorder" flag set to false + size_t m_numNonOrderPackets; // The number of stored packets with "inorder" flag set to false /// Points to the first packet of a message that has out-of-order flag /// and is complete (all packets from first to last are in the buffer). /// If there is no such message in the buffer, it contains -1. - int m_iFirstRandomMsgPos; + int m_iFirstNonOrderMsgPos; bool m_bPeerRexmitFlag; // Needed to read message number correctly const bool m_bMessageAPI; // Operation mode flag: message or stream. diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 258a2fdc8..3d9cf1c09 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -682,7 +682,7 @@ class UniquePtr: public std::auto_ptr bool operator==(const element_type* two) const { return get() == two; } bool operator!=(const element_type* two) const { return get() != two; } - operator bool () { return 0!= get(); } + operator bool () const { return 0!= get(); } }; // A primitive one-argument versions of Sprint and Printable From f6e0271c72a16dd12942651eae4cec28a39f586c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Feb 2024 11:28:24 +0100 Subject: [PATCH 16/28] Removed wrong fix. Fixed C++11 style initialization of PacketInfo --- srtcore/buffer_rcv.cpp | 26 ++++++++++++++++++++------ srtcore/utilities.h | 2 +- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 831840967..566854984 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -986,20 +986,34 @@ int CRcvBuffer::getRcvDataSize(int& bytes, int& timespan) const CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const { - // Check the state of the very first packet first + // Default: no packet available. + PacketInfo pi = { SRT_SEQNO_NONE, false, time_point() }; + + const CPacket* pkt = NULL; + + // Very first packet available with no gap. if (m_entries[m_iStartPos].status == EntryState_Avail) { SRT_ASSERT(m_entries[m_iStartPos].pUnit); - return { m_iStartSeqNo, false /*no gap*/, getPktTsbPdTime(packetAt(m_iStartPos).getMsgTimeStamp()) }; + pkt = &packetAt(m_iStartPos); } // If not, get the information from the drop - if (m_iDropPos != m_iEndPos) + else if (m_iDropPos != m_iEndPos) + { + SRT_ASSERT(m_entries[m_iDropPos].pUnit); + pkt = &packetAt(m_iDropPos); + pi.seq_gap = true; // Available, but after a drop. + } + else { - const CPacket& pkt = packetAt(m_iDropPos); - return { pkt.getSeqNo(), true, getPktTsbPdTime(pkt.getMsgTimeStamp()) }; + // If none of them point to a valid packet, + // there is no packet available; + return pi; } - return { SRT_SEQNO_NONE, false, time_point() }; + pi.seqno = pkt->getSeqNo(); + pi.tsbpd_time = getPktTsbPdTime(pkt->getMsgTimeStamp()); + return pi; } std::pair CRcvBuffer::getAvailablePacketsRange() const diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 3d9cf1c09..258a2fdc8 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -682,7 +682,7 @@ class UniquePtr: public std::auto_ptr bool operator==(const element_type* two) const { return get() == two; } bool operator!=(const element_type* two) const { return get() != two; } - operator bool () const { return 0!= get(); } + operator bool () { return 0!= get(); } }; // A primitive one-argument versions of Sprint and Printable From 072a8c482ec9a8963a9ca0efa0a4ac2ff0df9688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Feb 2024 16:29:55 +0100 Subject: [PATCH 17/28] Refax: CRcvBuffer extracted some parts of insert() to separate functions --- srtcore/buffer_rcv.cpp | 305 ++++++++++++++++++++++------------------- srtcore/buffer_rcv.h | 6 + 2 files changed, 167 insertions(+), 144 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 566854984..97b76d755 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -144,7 +144,7 @@ CRcvBuffer::~CRcvBuffer() { if (!it->pUnit) continue; - + m_pUnitQueue->makeUnitFree(it->pUnit); it->pUnit = NULL; } @@ -167,9 +167,6 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << unit->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); IF_RCVBUF_DEBUG(scoped_log.ss << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); - int32_t avail_seq; - int avail_range; - if (offset < 0) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -2"); @@ -181,31 +178,12 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -3"); - // Calculation done for the sake of possible discrepancy - // in order to inform the caller what to do. - if (m_entries[m_iStartPos].status == EntryState_Avail) - { - avail_seq = packetAt(m_iStartPos).getSeqNo(); - avail_range = m_iEndPos - m_iStartPos; - } - else if (m_iDropPos == m_iEndPos) - { - avail_seq = SRT_SEQNO_NONE; - avail_range = 0; - } - else - { - avail_seq = packetAt(m_iDropPos).getSeqNo(); - - // We don't know how many packets follow it exactly, - // but in this case it doesn't matter. We know that - // at least one is there. - avail_range = 1; - } + InsertInfo ireport (InsertInfo::DISCREPANCY); + getAvailInfo((ireport)); IF_HEAVY_LOGGING(debugShowState((debug_source + " overflow").c_str())); - return InsertInfo(InsertInfo::DISCREPANCY, avail_seq, avail_range); + return ireport; } // TODO: Don't do assert here. Process this situation somehow. @@ -243,95 +221,10 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // Set to a value, if due to insertion there was added // a packet that is earlier to be retrieved than the earliest // currently available packet. - time_point earlier_time; + time_point earlier_time = updatePosInfo(unit, prev_max_off, newpktpos, extended_end); - int prev_max_pos = incPos(m_iStartPos, prev_max_off); - - // Update flags - // Case [A] - if (extended_end) - { - // THIS means that the buffer WAS CONTIGUOUS BEFORE. - if (m_iEndPos == prev_max_pos) - { - // THIS means that the new packet didn't CAUSE a gap - if (m_iMaxPosOff == prev_max_off + 1) - { - // This means that m_iEndPos now shifts by 1, - // and m_iDropPos must be shifted together with it, - // as there's no drop to point. - m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); - m_iDropPos = m_iEndPos; - } - else - { - // Otherwise we have a drop-after-gap candidate - // which is the currently inserted packet. - // Therefore m_iEndPos STAYS WHERE IT IS. - m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); - } - } - } - // - // Since this place, every newpktpos is in the range - // between m_iEndPos (inclusive) and a position for m_iMaxPosOff. - - // Here you can use prev_max_pos as the position represented - // by m_iMaxPosOff, as if !extended_end, it was unchanged. - else if (newpktpos == m_iEndPos) - { - // Case [D]: inserted a packet at the first gap following the - // contiguous region. This makes a potential to extend the - // contiguous region and we need to find its end. - - // If insertion happened at the very first packet, it is the - // new earliest packet now. In any other situation under this - // condition there's some contiguous packet range preceding - // this position. - if (m_iEndPos == m_iStartPos) - { - earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); - } - - updateGapInfo(prev_max_pos); - } - // XXX Not sure if that's the best performant comparison - // What is meant here is that newpktpos is between - // m_iEndPos and m_iDropPos, though we know it's after m_iEndPos. - // CONSIDER: make m_iDropPos rather m_iDropOff, this will make - // this comparison a simple subtraction. Note that offset will - // have to be updated on every shift of m_iStartPos. - else if (cmpPos(newpktpos, m_iDropPos) < 0) - { - // Case [C]: the newly inserted packet precedes the - // previous earliest delivery position after drop, - // that is, there is now a "better" after-drop delivery - // candidate. - - // New position updated a valid packet on an earlier - // position than the drop position was before, although still - // following a gap. - // - // We know it because if the position has filled a gap following - // a valid packet, this preceding valid packet would be pointed - // by m_iDropPos, or it would point to some earlier packet in a - // contiguous series of valid packets following a gap, hence - // the above condition wouldn't be satisfied. - m_iDropPos = newpktpos; - - // If there's an inserted packet BEFORE drop-pos (which makes it - // a new drop-pos), while the very first packet is absent (the - // below condition), it means we have a new earliest-available - // packet. Otherwise we would have only a newly updated drop - // position, but still following some earlier contiguous range - // of valid packets - so it's earlier than previous drop, but - // not earlier than the earliest packet. - if (m_iStartPos == m_iEndPos) - { - earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); - } - } - // OTHERWISE: case [D] in which nothing is to be updated. + InsertInfo ireport (InsertInfo::INSERTED); + ireport.first_time = earlier_time; // If packet "in order" flag is zero, it can be read out of order. // With TSBPD enabled packets are always assumed in order (the flag is ignored). @@ -343,40 +236,164 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) updateNonreadPos(); - CPacket* avail_packet = NULL; - - if (m_entries[m_iStartPos].pUnit && m_entries[m_iStartPos].status == EntryState_Avail) - { - avail_packet = &packetAt(m_iStartPos); - avail_range = offPos(m_iStartPos, m_iEndPos); - } - else if (!m_tsbpd.isEnabled() && m_iFirstNonOrderMsgPos != -1) - { - // In case when TSBPD is off, we take into account the message mode - // where messages may potentially span for multiple packets, therefore - // the only "next deliverable" is the first complete message that satisfies - // the order requirement. - avail_packet = &packetAt(m_iFirstNonOrderMsgPos); - avail_range = 1; - } - else if (m_iDropPos != m_iEndPos) - { - avail_packet = &packetAt(m_iDropPos); - avail_range = 1; - } - else - { - avail_packet = NULL; - avail_range = 0; - } + // This updates only the first_seq and avail_range fields. + getAvailInfo((ireport)); IF_RCVBUF_DEBUG(scoped_log.ss << " returns 0 (OK)"); IF_HEAVY_LOGGING(debugShowState((debug_source + " ok").c_str())); - if (avail_packet) - return InsertInfo(InsertInfo::INSERTED, avail_packet->getSeqNo(), avail_range, earlier_time); - else - return InsertInfo(InsertInfo::INSERTED); // No packet candidate (NOTE: impossible in live mode) + return ireport; +} + +void CRcvBuffer::getAvailInfo(CRcvBuffer::InsertInfo& w_if) +{ + int fallback_pos = -1; + if (!m_tsbpd.isEnabled()) + { + // In case when TSBPD is off, we take into account the message mode + // where messages may potentially span for multiple packets, therefore + // the only "next deliverable" is the first complete message that satisfies + // the order requirement. + // NOTE THAT this field can as well be -1 already. + fallback_pos = m_iFirstNonOrderMsgPos; + } + else if (m_iDropPos != m_iEndPos) + { + // With TSBPD regard the drop position (regardless if + // TLPKTDROP is currently on or off), if "exists", that + // is, m_iDropPos != m_iEndPos. + fallback_pos = m_iDropPos; + } + + // This finds the first possible available packet, which is + // preferably at cell 0, but if not available, try also with + // given fallback position (unless it's -1). + const CPacket* pkt = tryAvailPacketAt(fallback_pos, (w_if.avail_range)); + if (pkt) + { + w_if.first_seq = pkt->getSeqNo(); + } +} + + +const CPacket* CRcvBuffer::tryAvailPacketAt(int pos, int& w_span) +{ + if (m_entries[m_iStartPos].status == EntryState_Avail) + { + pos = m_iStartPos; + w_span = offPos(m_iStartPos, m_iEndPos); + } + + if (pos == -1) + { + w_span = 0; + return NULL; + } + + SRT_ASSERT(m_entries[pos].pUnit != NULL); + + // TODO: we know that at least 1 packet is available, but only + // with m_iEndPos we know where the true range is. This could also + // be implemented for message mode, but still this would employ + // a separate begin-end range declared for a complete out-of-order + // message. + w_span = 1; + return &packetAt(pos); +} + +CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int prev_max_off, const int newpktpos, const bool extended_end) +{ + time_point earlier_time; + + int prev_max_pos = incPos(m_iStartPos, prev_max_off); + + // Update flags + // Case [A] + if (extended_end) + { + // THIS means that the buffer WAS CONTIGUOUS BEFORE. + if (m_iEndPos == prev_max_pos) + { + // THIS means that the new packet didn't CAUSE a gap + if (m_iMaxPosOff == prev_max_off + 1) + { + // This means that m_iEndPos now shifts by 1, + // and m_iDropPos must be shifted together with it, + // as there's no drop to point. + m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); + m_iDropPos = m_iEndPos; + } + else + { + // Otherwise we have a drop-after-gap candidate + // which is the currently inserted packet. + // Therefore m_iEndPos STAYS WHERE IT IS. + m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + } + } + } + // + // Since this place, every newpktpos is in the range + // between m_iEndPos (inclusive) and a position for m_iMaxPosOff. + + // Here you can use prev_max_pos as the position represented + // by m_iMaxPosOff, as if !extended_end, it was unchanged. + else if (newpktpos == m_iEndPos) + { + // Case [D]: inserted a packet at the first gap following the + // contiguous region. This makes a potential to extend the + // contiguous region and we need to find its end. + + // If insertion happened at the very first packet, it is the + // new earliest packet now. In any other situation under this + // condition there's some contiguous packet range preceding + // this position. + if (m_iEndPos == m_iStartPos) + { + earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); + } + + updateGapInfo(prev_max_pos); + } + // XXX Not sure if that's the best performant comparison + // What is meant here is that newpktpos is between + // m_iEndPos and m_iDropPos, though we know it's after m_iEndPos. + // CONSIDER: make m_iDropPos rather m_iDropOff, this will make + // this comparison a simple subtraction. Note that offset will + // have to be updated on every shift of m_iStartPos. + else if (cmpPos(newpktpos, m_iDropPos) < 0) + { + // Case [C]: the newly inserted packet precedes the + // previous earliest delivery position after drop, + // that is, there is now a "better" after-drop delivery + // candidate. + + // New position updated a valid packet on an earlier + // position than the drop position was before, although still + // following a gap. + // + // We know it because if the position has filled a gap following + // a valid packet, this preceding valid packet would be pointed + // by m_iDropPos, or it would point to some earlier packet in a + // contiguous series of valid packets following a gap, hence + // the above condition wouldn't be satisfied. + m_iDropPos = newpktpos; + + // If there's an inserted packet BEFORE drop-pos (which makes it + // a new drop-pos), while the very first packet is absent (the + // below condition), it means we have a new earliest-available + // packet. Otherwise we would have only a newly updated drop + // position, but still following some earlier contiguous range + // of valid packets - so it's earlier than previous drop, but + // not earlier than the earliest packet. + if (m_iStartPos == m_iEndPos) + { + earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); + } + } + // OTHERWISE: case [D] in which nothing is to be updated. + + return earlier_time; } void CRcvBuffer::updateGapInfo(int prev_max_pos) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 70099d355..f25528832 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -106,6 +106,8 @@ namespace srt // [D] [C] [B] [A] (insertion cases) // | (start) --- (end) ===[gap]=== (after-loss) ... (max-pos) | // +// See the CRcvBuffer::updatePosInfo method for detailed implementation. +// // WHEN INSERTING A NEW PACKET: // // If the incoming sequence maps to newpktpos that is: @@ -263,6 +265,10 @@ class CRcvBuffer /// InsertInfo insert(CUnit* unit); + time_point updatePosInfo(const CUnit* unit, const int prev_max_off, const int newpktpos, const bool extended_end); + const CPacket* tryAvailPacketAt(int pos, int& w_span); + void getAvailInfo(InsertInfo& w_if); + /// Update the values of `m_iEndPos` and `m_iDropPos` in /// case when `m_iEndPos` was updated to a position of a /// nonempty cell. From 283021efbaca001e3b0f0de8fdea51b8de173ff9 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 28 Feb 2024 11:03:46 +0100 Subject: [PATCH 18/28] Apply suggestions from code review (further impossible fixes pending) Co-authored-by: Maxim Sharabayko --- srtcore/buffer_rcv.cpp | 1 - srtcore/buffer_rcv.h | 8 ++++---- srtcore/core.cpp | 4 ++-- srtcore/core.h | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 97b76d755..6d6ebd841 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -656,7 +656,6 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pairstatus_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?) // // thread safety: -// start_pos_: CUDT::m_RecvLock +// m_iStartPos: CUDT::m_RecvLock // first_unack_pos_: CUDT::m_AckLock -// max_pos_inc_: none? (modified on add and ack -// first_nonread_pos_: +// m_iMaxPosOff: none? (modified on add and ack +// m_iFirstNonreadPos: // // // m_iStartPos: the first packet that should be read (might be empty) @@ -455,7 +455,7 @@ class CRcvBuffer return m_iMaxPosOff; } - // Unfortunately it still needs locking. + // Returns true if the buffer is full. Requires locking. bool full() const { return size() == capacity(); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index eaea48102..9afa74c9b 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5544,7 +5544,7 @@ void * srt::CUDT::tsbpd(void* param) tsNextDelivery = steady_clock::time_point(); // Ready to read, nothing to wait for. } - SRT_ATR_UNUSED bool wakeup_on_signal = true; + SRT_ATR_UNUSED bool bWakeupOnSignal = true; // We may just briefly unlocked the m_RecvLock, so we need to check m_bClosing again to avoid deadlock. if (self->m_bClosing) @@ -8183,7 +8183,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) } } #endif - // Signalling m_RecvDataCond is not dane when TSBPD is on. + // Signalling m_RecvDataCond is not done when TSBPD is on. // This signalling is done in file mode in order to keep the // API reader thread sleeping until there is a "bigger portion" // of data to read. In TSBPD mode this isn't done because every diff --git a/srtcore/core.h b/srtcore/core.h index 8cff2fbce..dee7bd48d 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -987,7 +987,7 @@ class CUDT sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock - sync::atomic m_bWakeOnRecv; // Expected to be woken up when received a packet + sync::atomic m_bWakeOnRecv; // Expected to wake up TSBPD when a read-ready data packet is received. sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining CallbackHolder m_cbAcceptHook; From b969a8329e9275d6df2f8979a4b803de7499eaaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 28 Feb 2024 12:06:29 +0100 Subject: [PATCH 19/28] Remaining post-review fixes --- srtcore/buffer_rcv.h | 5 +-- srtcore/core.cpp | 83 ++++++++++++++++---------------------------- 2 files changed, 33 insertions(+), 55 deletions(-) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 93b51c2c8..24f5066f6 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -338,9 +338,10 @@ class CRcvBuffer /// Read the whole message from one or several packets. /// - /// @param [in,out] data buffer to write the message into. + /// @param [out] data buffer to write the message into. /// @param [in] len size of the buffer. - /// @param [in,out] message control data + /// @param [out,opt] message control data to be filled + /// @param [out,opt] pw_seqrange range of sequence numbers for packets belonging to the message /// /// @return actual number of bytes extracted from the buffer. /// 0 if nothing to read. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 9afa74c9b..45aeb2b4c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5562,7 +5562,7 @@ void * srt::CUDT::tsbpd(void* param) log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno << " T=" << FormatTime(tsNextDelivery) << " - waiting " << FormatDuration(timediff)); THREAD_PAUSED(); - wakeup_on_signal = tsbpd_cc.wait_until(tsNextDelivery); + bWakeupOnSignal = tsbpd_cc.wait_until(tsNextDelivery); THREAD_RESUMED(); } else @@ -5585,7 +5585,7 @@ void * srt::CUDT::tsbpd(void* param) THREAD_RESUMED(); } - HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP [" << (wakeup_on_signal ? "signal" : "timeout") << "]!!! - " + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP [" << (bWakeupOnSignal? "signal" : "timeout") << "]!!! - " << "NOW=" << FormatTime(steady_clock::now())); } THREAD_EXIT(); @@ -8048,14 +8048,13 @@ bool srt::CUDT::getFirstNoncontSequence(int32_t& w_seq, string& w_log_reason) LOGP(cnlog.Error, "IPE: ack can't be sent, buffer doesn't exist and no group membership"); return false; } - { - ScopedLock buflock (m_RcvBufferLock); - bool has_followers = m_pRcvBuffer->getContiguousEnd((w_seq)); - if (has_followers) - w_log_reason = "first lost"; - else - w_log_reason = "expected next"; - } + + ScopedLock buflock (m_RcvBufferLock); + bool has_followers = m_pRcvBuffer->getContiguousEnd((w_seq)); + if (has_followers) + w_log_reason = "first lost"; + else + w_log_reason = "expected next"; return true; } @@ -9090,34 +9089,27 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); } } - // When the drop request was received, it means that there are - // packets for which there will never be ACK sent; if the TSBPD thread - // is currently in the ACK-waiting state, it may never exit. - if (m_bTsbPd) - { - // XXX Likely this is not necessary because: - // 1. In the recv-waiting state, that is, when TSBPD thread - // sleeps forever, it will be woken up anyway on packet - // reception. - // 2. If there are any packets in the buffer and the initial - // packet cell is empty (in which situation any drop could - // occur), TSBPD thread is sleeping timely, until the playtime - // of the first "drop up to" packet. Dropping changes nothing here. - // 3. If the buffer is empty, there's nothing "to drop up to", so - // this function will not change anything in the buffer and so - // in the reception state as well. - // 4. If the TSBPD thread is waiting until a play-ready packet is - // retrieved by the API call (in which case it is also sleeping - // forever until it's woken up by the API call), it may remain - // stalled forever, if the application isn't reading the play-ready - // packet. But this means that the application got stalled anyway. - // TSBPD when woken up could at best state that there's still a - // play-ready packet that is still not retrieved and fall back - // to sleep (forever). - - //HLOGP(inlog.Debug, "DROPREQ: signal TSBPD"); - //rcvtscc.notify_one(); - } + + // NOTE: + // PREVIOUSLY done: notify on rcvtscc if m_bTsbPd + // OLD COMMENT: + // // When the drop request was received, it means that there are + // // packets for which there will never be ACK sent; if the TSBPD thread + // // is currently in the ACK-waiting state, it may never exit. + // + // Likely this is no longer necessary because: + // + // 1. If there's a play-ready packet, either in cell 0 or + // after a drop, TSBPD is sleeping timely, up to the play-time + // of the next ready packet (and the drop region concerned here + // is still a gap to be skipped for this). + // 2. TSBPD sleeps forever when the buffer is empty, in which case + // it will be always woken up on packet reception (see m_bWakeOnRecv). + // And dropping won't happen in that case anyway. Note that the drop + // request will not drop packets that are already received. + // 3. TSBPD sleeps forever when the API call didn't extract the + // data that are ready to play. This isn't a problem if nothing + // except the API call would wake it up. } dropFromLossLists(dropdata[0], dropdata[1]); @@ -10679,21 +10671,6 @@ int srt::CUDT::processData(CUnit* in_unit) HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (SRT): " << Printable(srt_loss_seqs)); sendLossReport(srt_loss_seqs); } - - // This should not be required with the new receiver buffer because - // signalling happens on every packet reception, if it has changed the - // earliest packet position. - /* - if (m_bTsbPd) - { - HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); - CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); - } - else - { - HLOGC(qrlog.Debug, log << CONID() << "loss: socket is not TSBPD, not signaling"); - } - */ } // Separately report loss records of those reported by a filter. From d9f079a3648acd219608784135201bf5e4d4494b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 3 Jun 2024 10:32:53 +0200 Subject: [PATCH 20/28] Turned integer-based values to strong types. CHECKPOINT: tests passed --- srtcore/buffer_rcv.cpp | 394 ++++++++++++++++++++++++----------------- srtcore/buffer_rcv.h | 215 ++++++++++++++++++---- srtcore/common.h | 2 + srtcore/core.cpp | 32 +--- srtcore/utilities.h | 20 ++- 5 files changed, 435 insertions(+), 228 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 6d6ebd841..ab72670df 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -78,18 +78,19 @@ namespace { // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize]. // The right edge is included because we expect iFirstNonreadPos to be // right after the last valid packet position if all packets are available. - bool isInRange(int iStartPos, int iMaxPosOff, size_t iSize, int iFirstNonreadPos) + bool isInRange(CPos iStartPos, COff iMaxPosOff, size_t iSize, CPos iFirstNonreadPos) { if (iFirstNonreadPos == iStartPos) return true; - const int iLastPos = (iStartPos + iMaxPosOff) % iSize; - const bool isOverrun = iLastPos < iStartPos; + //const int iLastPos = (iStartPos VALUE + iMaxPosOff VALUE) % iSize; + const CPos iLastPos = iStartPos + iMaxPosOff; + const bool isOverrun = iLastPos VALUE < iStartPos VALUE; if (isOverrun) - return iFirstNonreadPos > iStartPos || iFirstNonreadPos <= iLastPos; + return iFirstNonreadPos VALUE > iStartPos VALUE || iFirstNonreadPos VALUE <= iLastPos VALUE; - return iFirstNonreadPos > iStartPos && iFirstNonreadPos <= iLastPos; + return iFirstNonreadPos VALUE > iStartPos VALUE && iFirstNonreadPos VALUE <= iLastPos VALUE; } } @@ -120,14 +121,14 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b , m_szSize(size) // TODO: maybe just use m_entries.size() , m_pUnitQueue(unitqueue) , m_iStartSeqNo(initSeqNo) // NOTE: SRT_SEQNO_NONE is allowed here. - , m_iStartPos(0) - , m_iEndPos(0) - , m_iDropPos(0) - , m_iFirstNonreadPos(0) + , m_iStartPos(&m_szSize, 0) + , m_iEndPos(&m_szSize, 0) + , m_iDropPos(&m_szSize, 0) + , m_iFirstNonreadPos(&m_szSize, 0) , m_iMaxPosOff(0) , m_iNotch(0) , m_numNonOrderPackets(0) - , m_iFirstNonOrderMsgPos(-1) + , m_iFirstNonOrderMsgPos(&m_szSize, CPos_TRAP.val()) , m_bPeerRexmitFlag(true) , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) @@ -152,29 +153,34 @@ CRcvBuffer::~CRcvBuffer() void CRcvBuffer::debugShowState(const char* source SRT_ATR_UNUSED) { - HLOGC(brlog.Debug, log << "RCV-BUF-STATE(" << source << ") start=" << m_iStartPos << " end=" << m_iEndPos - << " drop=" << m_iDropPos << " max-off=+" << m_iMaxPosOff << " seq[start]=%" << m_iStartSeqNo); + HLOGC(brlog.Debug, log << "RCV-BUF-STATE(" << source + << ") start=" << m_iStartPos VALUE + << " end=" << m_iEndPos VALUE + << " drop=" << m_iDropPos VALUE + << " max-off=+" << m_iMaxPosOff VALUE + << " seq[start]=%" << m_iStartSeqNo VALUE); } CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) { SRT_ASSERT(unit != NULL); const int32_t seqno = unit->m_Packet.getSeqNo(); - const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno); + //const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno); + const COff offset = COff(CSeqNo(seqno) - m_iStartSeqNo); IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::insert: seqno " << seqno); IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << unit->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); IF_RCVBUF_DEBUG(scoped_log.ss << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); - if (offset < 0) + if (offset < COff(0)) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -2"); return InsertInfo(InsertInfo::BELATED); } IF_HEAVY_LOGGING(string debug_source = "insert %" + Sprint(seqno)); - if (offset >= (int)capacity()) + if (offset >= COff(capacity())) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -3"); @@ -188,14 +194,14 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // TODO: Don't do assert here. Process this situation somehow. // If >= 2, then probably there is a long gap, and buffer needs to be reset. - SRT_ASSERT((m_iStartPos + offset) / m_szSize < 2); + SRT_ASSERT((m_iStartPos + offset) VALUE / m_szSize < 2); - const int newpktpos = incPos(m_iStartPos, offset); - const int prev_max_off = m_iMaxPosOff; + const CPos newpktpos = m_iStartPos + offset; + const COff prev_max_off = m_iMaxPosOff; bool extended_end = false; if (offset >= m_iMaxPosOff) { - m_iMaxPosOff = offset + 1; + m_iMaxPosOff = offset + COff(1); extended_end = true; } @@ -204,7 +210,7 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // possible even before checking that the packet // exists because existence of a packet beyond // the current max position is not possible). - SRT_ASSERT(newpktpos >= 0 && newpktpos < int(m_szSize)); + SRT_ASSERT(newpktpos VALUE >= 0 && newpktpos VALUE < int(m_szSize)); if (m_entries[newpktpos].status != EntryState_Empty) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -1"); @@ -247,7 +253,7 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) void CRcvBuffer::getAvailInfo(CRcvBuffer::InsertInfo& w_if) { - int fallback_pos = -1; + CPos fallback_pos = CPos_TRAP; if (!m_tsbpd.isEnabled()) { // In case when TSBPD is off, we take into account the message mode @@ -271,22 +277,23 @@ void CRcvBuffer::getAvailInfo(CRcvBuffer::InsertInfo& w_if) const CPacket* pkt = tryAvailPacketAt(fallback_pos, (w_if.avail_range)); if (pkt) { - w_if.first_seq = pkt->getSeqNo(); + w_if.first_seq = CSeqNo(pkt->getSeqNo()); } } -const CPacket* CRcvBuffer::tryAvailPacketAt(int pos, int& w_span) +const CPacket* CRcvBuffer::tryAvailPacketAt(CPos pos, COff& w_span) { if (m_entries[m_iStartPos].status == EntryState_Avail) { pos = m_iStartPos; - w_span = offPos(m_iStartPos, m_iEndPos); + //w_span = offPos(m_iStartPos, m_iEndPos); + w_span = m_iEndPos - m_iStartPos; } - if (pos == -1) + if (pos == CPos_TRAP) { - w_span = 0; + w_span = COff(0); return NULL; } @@ -297,15 +304,16 @@ const CPacket* CRcvBuffer::tryAvailPacketAt(int pos, int& w_span) // be implemented for message mode, but still this would employ // a separate begin-end range declared for a complete out-of-order // message. - w_span = 1; + w_span = COff(1); return &packetAt(pos); } -CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int prev_max_off, const int newpktpos, const bool extended_end) +CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff prev_max_off, const CPos newpktpos, const bool extended_end) { time_point earlier_time; - int prev_max_pos = incPos(m_iStartPos, prev_max_off); + //int prev_max_pos = incPos(m_iStartPos, prev_max_off); + CPos prev_max_pos = m_iStartPos + prev_max_off; // Update flags // Case [A] @@ -320,7 +328,8 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int pr // This means that m_iEndPos now shifts by 1, // and m_iDropPos must be shifted together with it, // as there's no drop to point. - m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); + //m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); + m_iEndPos = m_iStartPos + m_iMaxPosOff; m_iDropPos = m_iEndPos; } else @@ -328,7 +337,8 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int pr // Otherwise we have a drop-after-gap candidate // which is the currently inserted packet. // Therefore m_iEndPos STAYS WHERE IT IS. - m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + //m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + m_iDropPos = m_iStartPos + (m_iMaxPosOff - 1); } } } @@ -361,7 +371,9 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int pr // CONSIDER: make m_iDropPos rather m_iDropOff, this will make // this comparison a simple subtraction. Note that offset will // have to be updated on every shift of m_iStartPos. - else if (cmpPos(newpktpos, m_iDropPos) < 0) + + //else if (cmpPos(newpktpos, m_iDropPos) < 0) + else if (newpktpos.cmp(m_iDropPos, m_iStartPos) < 0) { // Case [C]: the newly inserted packet precedes the // previous earliest delivery position after drop, @@ -396,12 +408,12 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int pr return earlier_time; } -void CRcvBuffer::updateGapInfo(int prev_max_pos) +void CRcvBuffer::updateGapInfo(CPos prev_max_pos) { - int pos = m_iEndPos; + CPos pos = m_iEndPos; // First, search for the next gap, max until m_iMaxPosOff. - for ( ; pos != prev_max_pos; pos = incPos(pos)) + for ( ; pos != prev_max_pos; ++pos /*pos = incPos(pos)*/) { if (m_entries[pos].status == EntryState_Empty) { @@ -420,7 +432,7 @@ void CRcvBuffer::updateGapInfo(int prev_max_pos) m_iEndPos = pos; m_iDropPos = pos; // fallback, although SHOULD be impossible // So, search for the first position to drop up to. - for ( ; pos != prev_max_pos; pos = incPos(pos)) + for ( ; pos != prev_max_pos; ++pos /*pos = incPos(pos)*/) { if (m_entries[pos].status != EntryState_Empty) { @@ -440,7 +452,8 @@ int CRcvBuffer::dropUpTo(int32_t seqno) IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo); - int len = CSeqNo::seqoff(m_iStartSeqNo, seqno); + COff len = COff(CSeqNo(seqno) - m_iStartSeqNo); + //int len = CSeqNo::seqoff(m_iStartSeqNo, seqno); if (len <= 0) { IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); @@ -451,25 +464,27 @@ int CRcvBuffer::dropUpTo(int32_t seqno) if (m_iMaxPosOff < 0) m_iMaxPosOff = 0; - const int iDropCnt = len; - while (len > 0) + const int iDropCnt = len VALUE; + while (len VALUE > 0) { dropUnitInPos(m_iStartPos); m_entries[m_iStartPos].status = EntryState_Empty; SRT_ASSERT(m_entries[m_iStartPos].pUnit == NULL && m_entries[m_iStartPos].status == EntryState_Empty); - m_iStartPos = incPos(m_iStartPos); + //m_iStartPos = incPos(m_iStartPos); + ++m_iStartPos; --len; } // Update positions - m_iStartSeqNo = seqno; + m_iStartSeqNo = CSeqNo(seqno); // Move forward if there are "read/drop" entries. // (This call MAY shift m_iStartSeqNo further.) releaseNextFillerEntries(); // Start from here and search fort the next gap m_iEndPos = m_iDropPos = m_iStartPos; - updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); + //updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); + updateGapInfo(m_iStartPos + m_iMaxPosOff); // If the nonread position is now behind the starting position, set it to the starting position and update. // Preceding packets were likely missing, and the non read position can probably be moved further now. @@ -490,7 +505,8 @@ int CRcvBuffer::dropAll() if (empty()) return 0; - const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); + //const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); + const int end_seqno = (m_iStartSeqNo + m_iMaxPosOff VALUE) VALUE; return dropUpTo(end_seqno); } @@ -502,24 +518,28 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro << m_iStartSeqNo); // Drop by packet seqno range to also wipe those packets that do not exist in the buffer. - const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); - const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi); + //const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); + //const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi); + const int offset_a = CSeqNo(seqnolo) - m_iStartSeqNo; + const int offset_b = CSeqNo(seqnohi) - m_iStartSeqNo; if (offset_b < 0) { LOGC(rbuflog.Debug, log << "CRcvBuffer.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " - << seqnohi << "]. Buffer start " << m_iStartSeqNo << "."); + << seqnohi << "]. Buffer start " << m_iStartSeqNo VALUE << "."); return 0; } const bool bKeepExisting = (actionOnExisting == KEEP_EXISTING); - int minDroppedOffset = -1; + COff minDroppedOffset = COff(-1); int iDropCnt = 0; - const int start_off = max(0, offset_a); - const int start_pos = incPos(m_iStartPos, start_off); - const int end_off = min((int) m_szSize - 1, offset_b + 1); - const int end_pos = incPos(m_iStartPos, end_off); + const COff start_off = COff(max(0, offset_a)); + //const int start_pos = incPos(m_iStartPos, start_off); + const CPos start_pos = m_iStartPos + start_off; + const COff end_off = COff(min((int) m_szSize - 1, offset_b + 1)); + //const int end_pos = incPos(m_iStartPos, end_off); + const CPos end_pos = m_iStartPos + end_off; bool bDropByMsgNo = msgno > SRT_MSGNO_CONTROL; // Excluding both SRT_MSGNO_NONE (-1) and SRT_MSGNO_CONTROL (0). - for (int i = start_pos; i != end_pos; i = incPos(i)) + for (CPos i = start_pos; i != end_pos; ++i) { // Check if the unit was already dropped earlier. if (m_entries[i].status == EntryState_Drop) @@ -557,7 +577,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro ++iDropCnt; m_entries[i].status = EntryState_Drop; if (minDroppedOffset == -1) - minDroppedOffset = offPos(m_iStartPos, i); + //minDroppedOffset = offPos(m_iStartPos, i); + minDroppedOffset = i - m_iStartPos; } if (bDropByMsgNo) @@ -567,8 +588,9 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro // The sender should have the last packet of the message it is requesting to be dropped. // Therefore we don't search forward, but need to check earlier packets in the RCV buffer. // Try to drop by the message number in case the message starts earlier than @a seqnolo. - const int stop_pos = decPos(m_iStartPos); - for (int i = start_pos; i != stop_pos; i = decPos(i)) + //const int stop_pos = decPos(m_iStartPos); + const CPos stop_pos = m_iStartPos - COff(1); + for (CPos i = start_pos; i != stop_pos; --i) { // Can't drop if message number is not known. if (!m_entries[i].pUnit) // also dropped earlier. @@ -591,7 +613,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro dropUnitInPos(i); m_entries[i].status = EntryState_Drop; // As the search goes backward, i is always earlier than minDroppedOffset. - minDroppedOffset = offPos(m_iStartPos, i); + //minDroppedOffset = offPos(m_iStartPos, i); + minDroppedOffset = i - m_iStartPos; // Break the loop if the start of the message has been found. No need to search further. if (bnd == PB_FIRST) @@ -619,7 +642,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro if (!m_tsbpd.isEnabled() && m_bMessageAPI) { if (!checkFirstReadableNonOrder()) - m_iFirstNonOrderMsgPos = -1; + m_iFirstNonOrderMsgPos = CPos_TRAP; updateFirstReadableNonOrder(); } @@ -632,16 +655,20 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const if (m_iStartPos == m_iEndPos) { // Initial contiguous region empty (including empty buffer). - HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo); - w_seq = m_iStartSeqNo; + HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo VALUE); + w_seq = m_iStartSeqNo VALUE; return m_iMaxPosOff > 0; } - int end_off = offPos(m_iStartPos, m_iEndPos); + //int end_off = offPos(m_iStartPos, m_iEndPos); + COff end_off = m_iEndPos - m_iStartPos; - w_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + //w_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + w_seq = (m_iStartSeqNo + end_off VALUE) VALUE; - HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << end_off << " maxD=" << m_iMaxPosOff << " base=%" << m_iStartSeqNo + HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << end_off VALUE + << " maxD=" << m_iMaxPosOff VALUE + << " base=%" << m_iStartSeqNo VALUE << " end=%" << w_seq); return (end_off < m_iMaxPosOff); @@ -650,13 +677,13 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair* pw_seqrange) { const bool canReadInOrder = hasReadableInorderPkts(); - if (!canReadInOrder && m_iFirstNonOrderMsgPos < 0) + if (!canReadInOrder && m_iFirstNonOrderMsgPos == CPos_TRAP) { LOGC(rbuflog.Warn, log << "CRcvBuffer.readMessage(): nothing to read. Ignored isRcvDataReady() result?"); return 0; } - const int readPos = canReadInOrder ? m_iStartPos : m_iFirstNonOrderMsgPos; + const CPos readPos = canReadInOrder ? m_iStartPos : m_iFirstNonOrderMsgPos; const bool isReadingFromStart = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed IF_RCVBUF_DEBUG(ScopedLog scoped_log); @@ -670,7 +697,7 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= 0); - m_iStartSeqNo = CSeqNo::incseq(pktseqno); + m_iStartSeqNo = CSeqNo(pktseqno) + 1; } else { @@ -747,7 +774,7 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= remain_pktlen) { releaseUnitInPos(p); - p = incPos(p); + //p = incPos(p); + ++p; m_iNotch = 0; m_iStartPos = p; --m_iMaxPosOff; - SRT_ASSERT(m_iMaxPosOff >= 0); - m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + SRT_ASSERT(m_iMaxPosOff VALUE >= 0); + //m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + ++m_iStartSeqNo; } else m_iNotch += rs; @@ -923,7 +961,8 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) if (iBytesRead == 0) { - LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos); + LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos VALUE + << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos VALUE); } IF_HEAVY_LOGGING(debugShowState("readbuf")); @@ -942,12 +981,13 @@ int CRcvBuffer::readBufferToFile(fstream& ofs, int len) bool CRcvBuffer::hasAvailablePackets() const { - return hasReadableInorderPkts() || (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != -1); + return hasReadableInorderPkts() || (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != CPos_TRAP); } int CRcvBuffer::getRcvDataSize() const { - return offPos(m_iStartPos, m_iFirstNonreadPos); + //return offPos(m_iStartPos, m_iFirstNonreadPos); + return (m_iFirstNonreadPos - m_iStartPos) VALUE; } int CRcvBuffer::getTimespan_ms() const @@ -958,7 +998,8 @@ int CRcvBuffer::getTimespan_ms() const if (m_iMaxPosOff == 0) return 0; - int lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); + //int lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); + CPos lastpos = m_iStartPos + (m_iMaxPosOff - COff(1)); // Normally the last position should always be non empty // if TSBPD is enabled (reading out of order is not allowed). // However if decryption of the last packet fails, it may be dropped @@ -966,16 +1007,18 @@ int CRcvBuffer::getTimespan_ms() const SRT_ASSERT(m_entries[lastpos].pUnit != NULL || m_entries[lastpos].status == EntryState_Drop); while (m_entries[lastpos].pUnit == NULL && lastpos != m_iStartPos) { - lastpos = decPos(lastpos); + //lastpos = decPos(lastpos); + --lastpos; } - + if (m_entries[lastpos].pUnit == NULL) return 0; - int startpos = m_iStartPos; + CPos startpos = m_iStartPos; while (m_entries[startpos].pUnit == NULL && startpos != lastpos) { - startpos = incPos(startpos); + //startpos = incPos(startpos); + ++startpos; } if (m_entries[startpos].pUnit == NULL) @@ -1034,8 +1077,10 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const std::pair CRcvBuffer::getAvailablePacketsRange() const { - const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, offPos(m_iStartPos, m_iFirstNonreadPos)); - return std::pair(m_iStartSeqNo, seqno_last); + //const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, offPos(m_iStartPos, m_iFirstNonreadPos)); + const int nonread_off = (m_iFirstNonreadPos - m_iStartPos) VALUE; + const int seqno_last = (m_iStartSeqNo + nonread_off) VALUE; + return std::pair(m_iStartSeqNo VALUE, seqno_last); } bool CRcvBuffer::isRcvDataReady(time_point time_now) const @@ -1047,7 +1092,7 @@ bool CRcvBuffer::isRcvDataReady(time_point time_now) const return true; SRT_ASSERT((!m_bMessageAPI && m_numNonOrderPackets == 0) || m_bMessageAPI); - return (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != -1); + return (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != CPos_TRAP); } if (!haveInorderPackets) @@ -1072,7 +1117,7 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no return info; } SRT_ASSERT((!m_bMessageAPI && m_numNonOrderPackets == 0) || m_bMessageAPI); - if (m_iFirstNonOrderMsgPos >= 0) + if (m_iFirstNonOrderMsgPos != CPos_TRAP) { SRT_ASSERT(m_numNonOrderPackets > 0); const CPacket& packet = packetAt(m_iFirstNonOrderMsgPos); @@ -1107,7 +1152,7 @@ void CRcvBuffer::countBytes(int pkts, int bytes) } } -void CRcvBuffer::releaseUnitInPos(int pos) +void CRcvBuffer::releaseUnitInPos(CPos pos) { CUnit* tmp = m_entries[pos].pUnit; m_entries[pos] = Entry(); // pUnit = NULL; status = Empty @@ -1115,7 +1160,7 @@ void CRcvBuffer::releaseUnitInPos(int pos) m_pUnitQueue->makeUnitFree(tmp); } -bool CRcvBuffer::dropUnitInPos(int pos) +bool CRcvBuffer::dropUnitInPos(CPos pos) { if (!m_entries[pos].pUnit) return false; @@ -1127,7 +1172,7 @@ bool CRcvBuffer::dropUnitInPos(int pos) { --m_numNonOrderPackets; if (pos == m_iFirstNonOrderMsgPos) - m_iFirstNonOrderMsgPos = -1; + m_iFirstNonOrderMsgPos = CPos_TRAP; } releaseUnitInPos(pos); return true; @@ -1135,12 +1180,14 @@ bool CRcvBuffer::dropUnitInPos(int pos) void CRcvBuffer::releaseNextFillerEntries() { - int pos = m_iStartPos; + CPos pos = m_iStartPos; while (m_entries[pos].status == EntryState_Read || m_entries[pos].status == EntryState_Drop) { - m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + //m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + ++m_iStartSeqNo; releaseUnitInPos(pos); - pos = incPos(pos); + //pos = incPos(pos); + ++pos; m_iStartPos = pos; --m_iMaxPosOff; if (m_iMaxPosOff < 0) @@ -1154,15 +1201,16 @@ void CRcvBuffer::updateNonreadPos() if (m_iMaxPosOff == 0) return; - const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. + //const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. + const CPos end_pos = m_iStartPos + m_iMaxPosOff; // The empty position right after the last valid entry. - int pos = m_iFirstNonreadPos; + CPos pos = m_iFirstNonreadPos; while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail) { if (m_bMessageAPI && (packetAt(pos).getMsgBoundary() & PB_FIRST) == 0) break; - for (int i = pos; i != end_pos; i = incPos(i)) + for (CPos i = pos; i != end_pos; ++i) // i = incPos(i)) { if (!m_entries[i].pUnit || m_entries[pos].status != EntryState_Avail) { @@ -1176,7 +1224,8 @@ void CRcvBuffer::updateNonreadPos() // Check PB_LAST only in message mode. if (!m_bMessageAPI || packetAt(i).getMsgBoundary() & PB_LAST) { - m_iFirstNonreadPos = incPos(i); + //m_iFirstNonreadPos = incPos(i); + m_iFirstNonreadPos = i + COff(1); break; } } @@ -1188,9 +1237,9 @@ void CRcvBuffer::updateNonreadPos() } } -int CRcvBuffer::findLastMessagePkt() +CPos CRcvBuffer::findLastMessagePkt() { - for (int i = m_iStartPos; i != m_iFirstNonreadPos; i = incPos(i)) + for (CPos i = m_iStartPos; i != m_iFirstNonreadPos; ++i) //i = incPos(i)) { SRT_ASSERT(m_entries[i].pUnit); @@ -1200,10 +1249,10 @@ int CRcvBuffer::findLastMessagePkt() } } - return -1; + return CPos_TRAP; } -void CRcvBuffer::onInsertNonOrderPacket(int insertPos) +void CRcvBuffer::onInsertNonOrderPacket(CPos insertPos) { if (m_numNonOrderPackets == 0) return; @@ -1214,7 +1263,7 @@ void CRcvBuffer::onInsertNonOrderPacket(int insertPos) // // There might happen that the packet being added precedes the previously found one. // However, it is allowed to re bead out of order, so no need to update the position. - if (m_iFirstNonOrderMsgPos >= 0) + if (m_iFirstNonOrderMsgPos != CPos_TRAP) return; // Just a sanity check. This function is called when a new packet is added. @@ -1233,14 +1282,14 @@ void CRcvBuffer::onInsertNonOrderPacket(int insertPos) const int msgNo = pkt.getMsgSeq(m_bPeerRexmitFlag); // First check last packet, because it is expected to be received last. - const bool hasLast = (boundary & PB_LAST) || (-1 < scanNonOrderMessageRight(insertPos, msgNo)); + const bool hasLast = (boundary & PB_LAST) || (scanNonOrderMessageRight(insertPos, msgNo) != CPos_TRAP); if (!hasLast) return; - const int firstPktPos = (boundary & PB_FIRST) + const CPos firstPktPos = (boundary & PB_FIRST) ? insertPos : scanNonOrderMessageLeft(insertPos, msgNo); - if (firstPktPos < 0) + if (firstPktPos == CPos_TRAP) return; m_iFirstNonOrderMsgPos = firstPktPos; @@ -1249,12 +1298,13 @@ void CRcvBuffer::onInsertNonOrderPacket(int insertPos) bool CRcvBuffer::checkFirstReadableNonOrder() { - if (m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos < 0 || m_iMaxPosOff == 0) + if (m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos == CPos_TRAP || m_iMaxPosOff == COff(0)) return false; - const int endPos = incPos(m_iStartPos, m_iMaxPosOff); + //const int endPos = incPos(m_iStartPos, m_iMaxPosOff); + const CPos endPos = m_iStartPos + m_iMaxPosOff; int msgno = -1; - for (int pos = m_iFirstNonOrderMsgPos; pos != endPos; pos = incPos(pos)) + for (CPos pos = m_iFirstNonOrderMsgPos; pos != endPos; ++pos) // pos = incPos(pos)) { if (!m_entries[pos].pUnit) return false; @@ -1277,7 +1327,7 @@ bool CRcvBuffer::checkFirstReadableNonOrder() void CRcvBuffer::updateFirstReadableNonOrder() { - if (hasReadableInorderPkts() || m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos >= 0) + if (hasReadableInorderPkts() || m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos != CPos_TRAP) return; if (m_iMaxPosOff == 0) @@ -1288,17 +1338,19 @@ void CRcvBuffer::updateFirstReadableNonOrder() // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; + //const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; + const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); - int posFirst = -1; - int posLast = -1; + CPos posFirst = CPos_TRAP; + CPos posLast = CPos_TRAP; int msgNo = -1; - for (int pos = m_iStartPos; outOfOrderPktsRemain; pos = incPos(pos)) + for (CPos pos = m_iStartPos; outOfOrderPktsRemain; ++pos) //pos = incPos(pos)) { if (!m_entries[pos].pUnit) { - posFirst = posLast = msgNo = -1; + posFirst = posLast = CPos_TRAP; + msgNo = -1; continue; } @@ -1306,7 +1358,8 @@ void CRcvBuffer::updateFirstReadableNonOrder() if (pkt.getMsgOrderFlag()) // Skip in order packet { - posFirst = posLast = msgNo = -1; + posFirst = posLast = CPos_TRAP; + msgNo = -1; continue; } @@ -1321,7 +1374,8 @@ void CRcvBuffer::updateFirstReadableNonOrder() if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { - posFirst = posLast = msgNo = -1; + posFirst = posLast = CPos_TRAP; + msgNo = -1; continue; } @@ -1338,18 +1392,20 @@ void CRcvBuffer::updateFirstReadableNonOrder() return; } -int CRcvBuffer::scanNonOrderMessageRight(const int startPos, int msgNo) const +CPos CRcvBuffer::scanNonOrderMessageRight(const CPos startPos, int msgNo) const { // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; + //const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; + const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); if (startPos == lastPos) - return -1; + return CPos_TRAP; - int pos = startPos; + CPos pos = startPos; do { - pos = incPos(pos); + //pos = incPos(pos); + ++pos; if (!m_entries[pos].pUnit) break; @@ -1358,7 +1414,7 @@ int CRcvBuffer::scanNonOrderMessageRight(const int startPos, int msgNo) const if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { LOGC(rbuflog.Error, log << "Missing PB_LAST packet for msgNo " << msgNo); - return -1; + return CPos_TRAP; } const PacketBoundary boundary = pkt.getMsgBoundary(); @@ -1366,30 +1422,31 @@ int CRcvBuffer::scanNonOrderMessageRight(const int startPos, int msgNo) const return pos; } while (pos != lastPos); - return -1; + return CPos_TRAP; } -int CRcvBuffer::scanNonOrderMessageLeft(const int startPos, int msgNo) const +CPos CRcvBuffer::scanNonOrderMessageLeft(const CPos startPos, int msgNo) const { // Search preceding packets to the left. // First check if there are packets to the left. if (startPos == m_iStartPos) - return -1; + return CPos_TRAP; - int pos = startPos; + CPos pos = startPos; do { - pos = decPos(pos); + //pos = decPos(pos); + --pos; if (!m_entries[pos].pUnit) - return -1; + return CPos_TRAP; const CPacket& pkt = packetAt(pos); if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { LOGC(rbuflog.Error, log << "Missing PB_FIRST packet for msgNo " << msgNo); - return -1; + return CPos_TRAP; } const PacketBoundary boundary = pkt.getMsgBoundary(); @@ -1397,7 +1454,7 @@ int CRcvBuffer::scanNonOrderMessageLeft(const int startPos, int msgNo) const return pos; } while (pos != m_iStartPos); - return -1; + return CPos_TRAP; } bool CRcvBuffer::addRcvTsbPdDriftSample(uint32_t usTimestamp, const time_point& tsPktArrival, int usRTTSample) @@ -1435,12 +1492,12 @@ void CRcvBuffer::updateTsbPdTimeBase(uint32_t usPktTimestamp) m_tsbpd.updateTsbPdTimeBase(usPktTimestamp); } -string CRcvBuffer::strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const +string CRcvBuffer::strFullnessState(int32_t iFirstUnackSeqNo, const time_point& tsNow) const { stringstream ss; - ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo - << " m_iStartPos=" << m_iStartPos << " m_iMaxPosOff=" << m_iMaxPosOff << ". "; + ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo VALUE + << " m_iStartPos=" << m_iStartPos VALUE << " m_iMaxPosOff=" << m_iMaxPosOff VALUE << ". "; ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; @@ -1451,7 +1508,7 @@ string CRcvBuffer::strFullnessState(int iFirstUnackSeqNo, const time_point& tsNo if (!is_zero(nextValidPkt.tsbpd_time)) { ss << count_milliseconds(nextValidPkt.tsbpd_time - tsNow) << "ms"; - const int iLastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + const CPos iLastPos = m_iStartPos + m_iMaxPosOff - COff(1); //incPos(m_iStartPos, m_iMaxPosOff - 1); if (m_entries[iLastPos].pUnit) { ss << ", timespan "; @@ -1502,35 +1559,39 @@ void CRcvBuffer::updRcvAvgDataSize(const steady_clock::time_point& now) int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) { - int offset = CSeqNo::seqoff(m_iStartSeqNo, fromseq); + //int offset = CSeqNo::seqoff(m_iStartSeqNo, fromseq); + int offset_val = CSeqNo(fromseq) - m_iStartSeqNo; + COff offset (offset_val); // Check if it's still inside the buffer - if (offset < 0 || offset >= m_iMaxPosOff) + if (offset_val < 0 || offset >= m_iMaxPosOff) { - HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset << " for %" << fromseq - << " (with max=" << m_iMaxPosOff << ") - NO LOSS FOUND"); + HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset VALUE << " for %" << fromseq + << " (with max=" << m_iMaxPosOff VALUE << ") - NO LOSS FOUND"); return SRT_SEQNO_NONE; } // Start position - int frompos = incPos(m_iStartPos, offset); + //int frompos = incPos(m_iStartPos, offset); + CPos frompos = m_iStartPos + offset; // Ok; likely we should stand at the m_iEndPos position. // If this given position is earlier than this, then // m_iEnd stands on the first loss, unless it's equal // to the position pointed by m_iMaxPosOff. - int32_t ret_seq = SRT_SEQNO_NONE; - int ret_off = m_iMaxPosOff; + CSeqNo ret_seq = CSeqNo(SRT_SEQNO_NONE); + COff ret_off = m_iMaxPosOff; - int end_off = offPos(m_iStartPos, m_iEndPos); - if (frompos < end_off) + COff end_off = m_iEndPos - m_iStartPos; //offPos(m_iStartPos, m_iEndPos); + if (offset < end_off) { // If m_iEndPos has such a value, then there are // no loss packets at all. if (end_off != m_iMaxPosOff) { - ret_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + //ret_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + ret_seq = m_iStartSeqNo + end_off VALUE; ret_off = end_off; } } @@ -1544,11 +1605,13 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // REUSE offset as a control variable for (; offset < m_iMaxPosOff; ++offset) { - int pos = incPos(m_iStartPos, offset); + //int pos = incPos(m_iStartPos, offset); + const CPos pos = m_iStartPos + offset; if (m_entries[pos].status == EntryState_Empty) { ret_off = offset; - ret_seq = CSeqNo::incseq(m_iStartSeqNo, offset); + //ret_seq = CSeqNo::incseq(m_iStartSeqNo, offset); + ret_seq = m_iStartSeqNo + offset VALUE; break; } } @@ -1559,26 +1622,31 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // Also no need to search anything if only the beginning was // being looked for. - if (ret_seq == SRT_SEQNO_NONE || !pw_end) - return ret_seq; + if (ret_seq == CSeqNo(SRT_SEQNO_NONE) || !pw_end) + return ret_seq VALUE; // We want also the end range, so continue from where you // stopped. // Start from ret_off + 1 because we know already that ret_off // points to an empty cell. - for (int off = ret_off + 1; off < m_iMaxPosOff; ++off) + for (COff off = ret_off + COff(1); off < m_iMaxPosOff; ++off) { - int pos = incPos(m_iStartPos, off); + //int pos = incPos(m_iStartPos, off); + const CPos pos = m_iStartPos + off; if (m_entries[pos].status != EntryState_Empty) { - *pw_end = CSeqNo::incseq(m_iStartSeqNo, off - 1); - return ret_seq; + //*pw_end = CSeqNo::incseq(m_iStartSeqNo, off - 1); + *pw_end = (m_iStartSeqNo + (off - COff(1)) VALUE) VALUE; + return ret_seq VALUE; } } // Fallback - this should be impossible, so issue a log. - LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << frompos << " %" << CSeqNo::incseq(m_iStartSeqNo, ret_off) << " not followed by any valid cell"); + LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << frompos VALUE << " %" + //<< CSeqNo::incseq(m_iStartSeqNo, ret_off) + << (m_iStartSeqNo + ret_off) VALUE + << " not followed by any valid cell"); // Return this in the last resort - this could only be a situation when // a packet has somehow disappeared, but it contains empty cells up to the diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 24f5066f6..18450947d 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -15,10 +15,162 @@ #include "common.h" #include "queue.h" #include "tsbpd_time.h" +#include "utilities.h" + +#define USE_WRAPPERS 1 +#define USE_OPERATORS 1 namespace srt { +// DEVELOPMENT TOOL - TO BE MOVED ELSEWHERE (like common.h) + +#if USE_WRAPPERS +struct CPos +{ + int value; + const size_t* psize; + + int isize() const {return *psize;} + + explicit CPos(explicit_t ps, explicit_t val): value(val), psize(ps) {} + int val() const { return value; } + explicit operator int() const {return value;} + + CPos(const CPos& src): value(src.value), psize(src.psize) {} + CPos& operator=(const CPos& src) + { + psize = src.psize; + value = src.value; + return *this; + } + + int cmp(CPos other, CPos start) const + { + int pos2 = value; + int pos1 = other.value; + + const int off1 = pos1 >= start.value ? pos1 - start.value : pos1 + start.isize() - start.value; + const int off2 = pos2 >= start.value ? pos2 - start.value : pos2 + start.isize() - start.value; + + return off2 - off1; + } + + CPos& operator--() + { + if (value == 0) + value = isize() - 1; + else + --value; + return *this; + } + + CPos& operator++() + { + ++value; + if (value == isize()) + value = 0; + return *this; + } + + bool operator == (CPos other) const { return value == other.value; } + bool operator != (CPos other) const { return value != other.value; } +}; + +struct COff +{ + int value; + explicit COff(int v): value(v) {} + COff& operator=(int v) { value = v; return *this; } + + int val() const { return value; } + explicit operator int() const {return value;} + + COff& operator--() { --value; return *this; } + COff& operator++() { ++value; return *this; } + + COff operator--(int) { int v = value; --value; return COff(v); } + COff operator++(int) { int v = value; ++value; return COff(v); } + + COff operator+(COff other) const { return COff(value + other.value); } + COff operator-(COff other) const { return COff(value - other.value); } + COff& operator+=(COff other) { value += other.value; return *this;} + COff& operator-=(COff other) { value -= other.value; return *this;} + + bool operator == (COff other) const { return value == other.value; } + bool operator != (COff other) const { return value != other.value; } + bool operator < (COff other) const { return value < other.value; } + bool operator > (COff other) const { return value > other.value; } + bool operator <= (COff other) const { return value <= other.value; } + bool operator >= (COff other) const { return value >= other.value; } + + // Exceptionally allow modifications of COff by a bare integer + COff operator+(int other) const { return COff(value + other); } + COff operator-(int other) const { return COff(value - other); } + COff& operator+=(int other) { value += other; return *this;} + COff& operator-=(int other) { value -= other; return *this;} + + bool operator == (int other) const { return value == other; } + bool operator != (int other) const { return value != other; } + bool operator < (int other) const { return value < other; } + bool operator > (int other) const { return value > other; } + bool operator <= (int other) const { return value <= other; } + bool operator >= (int other) const { return value >= other; } +}; + +#define VALUE .val() + +#if USE_OPERATORS + +inline CPos operator+(const CPos& pos, COff off) +{ + int val = pos.value + off.value; + while (val >= pos.isize()) + val -= pos.isize(); + return CPos(pos.psize, val); +} + +inline CPos operator-(const CPos& pos, COff off) +{ + int val = pos.value - off.value; + while (val < 0) + val += pos.isize(); + return CPos(pos.psize, val); +} + +// Should verify that CPos use the same size! +inline COff operator-(CPos later, CPos earlier) +{ + if (later.value < earlier.value) + return COff(later.value + later.isize() - earlier.value); + + return COff(later.value - earlier.value); +} + +inline CSeqNo operator+(CSeqNo seq, COff off) +{ + int32_t val = CSeqNo::incseq(seq.val(), off.val()); + return CSeqNo(val); +} + +inline CSeqNo operator-(CSeqNo seq, COff off) +{ + int32_t val = CSeqNo::decseq(seq.val(), off.val()); + return CSeqNo(val); +} + + +#endif +const size_t fix_rollover = 16; +const CPos CPos_TRAP (&fix_rollover, -1); + +#else +#define VALUE +typedef int CPos; +typedef int COff; +const int CPos_TRAP = -1; +#endif + // // Circular receiver buffer. // @@ -229,9 +381,9 @@ class CRcvBuffer // Below fields are valid only if result == INSERTED. Otherwise they have trap repro. - int first_seq; // sequence of the first available readable packet + CSeqNo first_seq; // sequence of the first available readable packet time_point first_time; // Time of the new, earlier packet that appeared ready, or null-time if this didn't change. - int avail_range; + COff avail_range; InsertInfo(Result r, int fp_seq = SRT_SEQNO_NONE, int range = 0, time_point fp_time = time_point()) @@ -265,8 +417,8 @@ class CRcvBuffer /// InsertInfo insert(CUnit* unit); - time_point updatePosInfo(const CUnit* unit, const int prev_max_off, const int newpktpos, const bool extended_end); - const CPacket* tryAvailPacketAt(int pos, int& w_span); + time_point updatePosInfo(const CUnit* unit, const COff prev_max_off, const CPos newpktpos, const bool extended_end); + const CPacket* tryAvailPacketAt(CPos pos, COff& w_span); void getAvailInfo(InsertInfo& w_if); /// Update the values of `m_iEndPos` and `m_iDropPos` in @@ -289,7 +441,7 @@ class CRcvBuffer /// /// @param prev_max_pos buffer position represented by `m_iMaxPosOff` /// - void updateGapInfo(int prev_max_pos); + void updateGapInfo(CPos prev_max_pos); /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). /// @param [in] seqno drop units up to this sequence number @@ -362,31 +514,32 @@ class CRcvBuffer public: /// Get the starting position of the buffer as a packet sequence number. - int getStartSeqNo() const { return m_iStartSeqNo; } + int getStartSeqNo() const { return m_iStartSeqNo VALUE; } /// Sets the start seqno of the buffer. /// Must be used with caution and only when the buffer is empty. - void setStartSeqNo(int seqno) { m_iStartSeqNo = seqno; } + void setStartSeqNo(int seqno) { m_iStartSeqNo = CSeqNo(seqno); } /// Given the sequence number of the first unacknowledged packet /// tells the size of the buffer available for packets. /// Effective returns capacity of the buffer minus acknowledged packet still kept in it. // TODO: Maybe does not need to return minus one slot now to distinguish full and empty buffer. - size_t getAvailSize(int iFirstUnackSeqNo) const + size_t getAvailSize(int32_t iFirstUnackSeqNo) const { // Receiver buffer allows reading unacknowledged packets. // Therefore if the first packet in the buffer is ahead of the iFirstUnackSeqNo // then it does not have acknowledged packets and its full capacity is available. // Otherwise subtract the number of acknowledged but not yet read packets from its capacity. - const int iRBufSeqNo = getStartSeqNo(); - if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo + const CSeqNo iRBufSeqNo = m_iStartSeqNo; + //if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo + if (iRBufSeqNo >= CSeqNo(iFirstUnackSeqNo)) { // Full capacity is available. return capacity(); } // Note: CSeqNo::seqlen(n, n) returns 1. - return capacity() - CSeqNo::seqlen(iRBufSeqNo, iFirstUnackSeqNo) + 1; + return capacity() - CSeqNo::seqlen(iRBufSeqNo VALUE, iFirstUnackSeqNo) + 1; } /// @brief Checks if the buffer has packets available for reading regardless of the TSBPD. @@ -436,7 +589,7 @@ class CRcvBuffer bool empty() const { - return (m_iMaxPosOff == 0); + return (m_iMaxPosOff == COff(0)); } /// Return buffer capacity. @@ -453,7 +606,7 @@ class CRcvBuffer /// between the initial position and the youngest received packet. size_t size() const { - return m_iMaxPosOff; + return size_t(m_iMaxPosOff VALUE); } // Returns true if the buffer is full. Requires locking. @@ -489,6 +642,7 @@ class CRcvBuffer const CUnit* peek(int32_t seqno); private: + /* inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : int(m_szSize + pos2 - pos1); } @@ -506,20 +660,21 @@ class CRcvBuffer return off2 - off1; } + */ // NOTE: Assumes that pUnit != NULL - CPacket& packetAt(int pos) { return m_entries[pos].pUnit->m_Packet; } - const CPacket& packetAt(int pos) const { return m_entries[pos].pUnit->m_Packet; } + CPacket& packetAt(CPos pos) { return m_entries[pos].pUnit->m_Packet; } + const CPacket& packetAt(CPos pos) const { return m_entries[pos].pUnit->m_Packet; } private: void countBytes(int pkts, int bytes); void updateNonreadPos(); - void releaseUnitInPos(int pos); + void releaseUnitInPos(CPos pos); /// @brief Drop a unit from the buffer. /// @param pos position in the m_entries of the unit to drop. /// @return false if nothing to drop, true if the unit was dropped successfully. - bool dropUnitInPos(int pos); + bool dropUnitInPos(CPos pos); /// Release entries following the current buffer position if they were already /// read out of order (EntryState_Read) or dropped (EntryState_Drop). @@ -528,15 +683,15 @@ class CRcvBuffer bool hasReadableInorderPkts() const { return (m_iFirstNonreadPos != m_iStartPos); } /// Find position of the last packet of the message. - int findLastMessagePkt(); + CPos findLastMessagePkt(); /// Scan for availability of out of order packets. - void onInsertNonOrderPacket(int insertpos); + void onInsertNonOrderPacket(CPos insertpos); // Check if m_iFirstNonOrderMsgPos is still readable. bool checkFirstReadableNonOrder(); void updateFirstReadableNonOrder(); - int scanNonOrderMessageRight(int startPos, int msgNo) const; - int scanNonOrderMessageLeft(int startPos, int msgNo) const; + CPos scanNonOrderMessageRight(CPos startPos, int msgNo) const; + CPos scanNonOrderMessageLeft(CPos startPos, int msgNo) const; typedef bool copy_to_dst_f(char* data, int len, int dst_offset, void* arg); @@ -588,18 +743,18 @@ class CRcvBuffer //static Entry emptyEntry() { return Entry { NULL, EntryState_Empty }; } - typedef FixedArray entries_t; + typedef FixedArray entries_t; entries_t m_entries; const size_t m_szSize; // size of the array of units (buffer) CUnitQueue* m_pUnitQueue; // the shared unit queue - int m_iStartSeqNo; - int m_iStartPos; // the head position for I/O (inclusive) - int m_iEndPos; // past-the-end of the contiguous region since m_iStartPos - int m_iDropPos; // points past m_iEndPos to the first deliverable after a gap, or == m_iEndPos if no such packet - int m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) - int m_iMaxPosOff; // the furthest data position + CSeqNo m_iStartSeqNo; + CPos m_iStartPos; // the head position for I/O (inclusive) + CPos m_iEndPos; // past-the-end of the contiguous region since m_iStartPos + CPos m_iDropPos; // points past m_iEndPos to the first deliverable after a gap, or == m_iEndPos if no such packet + CPos m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) + COff m_iMaxPosOff; // the furthest data position int m_iNotch; // index of the first byte to read in the first ready-to-read packet (used in file/stream mode) size_t m_numNonOrderPackets; // The number of stored packets with "inorder" flag set to false @@ -607,7 +762,7 @@ class CRcvBuffer /// Points to the first packet of a message that has out-of-order flag /// and is complete (all packets from first to last are in the buffer). /// If there is no such message in the buffer, it contains -1. - int m_iFirstNonOrderMsgPos; + CPos m_iFirstNonOrderMsgPos; bool m_bPeerRexmitFlag; // Needed to read message number correctly const bool m_bMessageAPI; // Operation mode flag: message or stream. @@ -637,7 +792,7 @@ class CRcvBuffer /// Form a string of the current buffer fullness state. /// number of packets acknowledged, TSBPD readiness, etc. - std::string strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const; + std::string strFullnessState(int32_t iFirstUnackSeqNo, const time_point& tsNow) const; private: CTsbpdTime m_tsbpd; diff --git a/srtcore/common.h b/srtcore/common.h index 6a8912118..1c15dd01a 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -580,6 +580,8 @@ class CSeqNo explicit CSeqNo(int32_t v): value(v) {} + int32_t val() const { return value; } + // Comparison bool operator == (const CSeqNo& other) const { return other.value == value; } bool operator < (const CSeqNo& other) const diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 45aeb2b4c..ad42ceb92 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8192,24 +8192,12 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) log << CONID() << "ACK: clip %" << m_iRcvLastAck << "-%" << ack << ", REVOKED " << CSeqNo::seqoff(ack, m_iRcvLastAck) << " from RCV buffer"); - if (m_bTsbPd) - { - /* - There's no need to update TSBPD in the wake-on-recv state - from ACK because it is being done already in the receiver thread - when a newly inserted packet caused provision of a new candidate - that could be delivered soon. Also, this flag is only used in TSBPD - mode and can be only set to true in the TSBPD thread. - - // Newly acknowledged data, signal TsbPD thread // - CUniqueSync tslcc (m_RecvLock, m_RcvTsbPdCond); - // m_bWakeOnRecv is protected by m_RecvLock in the tsbpd() thread - if (m_bWakeOnRecv) - tslcc.notify_one(); - - */ - } - else + // There's no need to update TSBPD in the wake-on-recv state + // from ACK because it is being done already in the receiver thread + // when a newly inserted packet caused provision of a new candidate + // that could be delivered soon. Also, this flag is only used in TSBPD + // mode and can be only set to true in the TSBPD thread. + if (!m_bTsbPd) { { CUniqueSync rdcc (m_RecvLock, m_RecvDataCond); @@ -10682,14 +10670,6 @@ int srt::CUDT::processData(CUnit* in_unit) { HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); sendLossReport(filter_loss_seqs); - - // XXX unsure as to whether this should change anything in the TSBPD conditions. - // Might be that this trigger is not necessary. - if (m_bTsbPd) - { - HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); - CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); - } } // Now review the list of FreshLoss to see if there's any "old enough" to send UMSG_LOSSREPORT to it. diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 8a1374eb7..9dbebe762 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -414,7 +414,7 @@ struct DynamicStruct /// Fixed-size array template class. namespace srt { -template +template class FixedArray { public: @@ -430,22 +430,23 @@ class FixedArray } public: - const T& operator[](size_t index) const + const T& operator[](Indexer index) const { - if (index >= m_size) - throw_invalid_index(index); + if (int(index) >= int(m_size)) + throw_invalid_index(int(index)); - return m_entries[index]; + return m_entries[int(index)]; } - T& operator[](size_t index) + T& operator[](Indexer index) { - if (index >= m_size) - throw_invalid_index(index); + if (int(index) >= int(m_size)) + throw_invalid_index(int(index)); - return m_entries[index]; + return m_entries[int(index)]; } + /* const T& operator[](int index) const { if (index < 0 || static_cast(index) >= m_size) @@ -461,6 +462,7 @@ class FixedArray return m_entries[index]; } + */ size_t size() const { return m_size; } From ab469d65d58a9e4ec825d4557bc405f70e8abb36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 10 Jun 2024 11:09:06 +0200 Subject: [PATCH 21/28] Fixed previous problems with receiver buffer --- srtcore/buffer_rcv.cpp | 168 +++++++++++++++++++---------------------- srtcore/buffer_rcv.h | 61 ++++++++++++--- 2 files changed, 129 insertions(+), 100 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index ab72670df..539c51f9e 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -75,23 +75,6 @@ namespace { #define IF_RCVBUF_DEBUG(instr) (void)0 - // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize]. - // The right edge is included because we expect iFirstNonreadPos to be - // right after the last valid packet position if all packets are available. - bool isInRange(CPos iStartPos, COff iMaxPosOff, size_t iSize, CPos iFirstNonreadPos) - { - if (iFirstNonreadPos == iStartPos) - return true; - - //const int iLastPos = (iStartPos VALUE + iMaxPosOff VALUE) % iSize; - const CPos iLastPos = iStartPos + iMaxPosOff; - const bool isOverrun = iLastPos VALUE < iStartPos VALUE; - - if (isOverrun) - return iFirstNonreadPos VALUE > iStartPos VALUE || iFirstNonreadPos VALUE <= iLastPos VALUE; - - return iFirstNonreadPos VALUE > iStartPos VALUE && iFirstNonreadPos VALUE <= iLastPos VALUE; - } } @@ -194,9 +177,10 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // TODO: Don't do assert here. Process this situation somehow. // If >= 2, then probably there is a long gap, and buffer needs to be reset. - SRT_ASSERT((m_iStartPos + offset) VALUE / m_szSize < 2); + SRT_ASSERT((m_iStartPos VALUE + offset VALUE) / m_szSize < 2); - const CPos newpktpos = m_iStartPos + offset; + //const CPos newpktpos = m_iStartPos + offset; + const CPos newpktpos = incPos(m_iStartPos, offset); const COff prev_max_off = m_iMaxPosOff; bool extended_end = false; if (offset >= m_iMaxPosOff) @@ -287,8 +271,8 @@ const CPacket* CRcvBuffer::tryAvailPacketAt(CPos pos, COff& w_span) if (m_entries[m_iStartPos].status == EntryState_Avail) { pos = m_iStartPos; - //w_span = offPos(m_iStartPos, m_iEndPos); - w_span = m_iEndPos - m_iStartPos; + w_span = offPos(m_iStartPos, m_iEndPos); + //w_span = m_iEndPos - m_iStartPos; } if (pos == CPos_TRAP) @@ -312,8 +296,8 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p { time_point earlier_time; - //int prev_max_pos = incPos(m_iStartPos, prev_max_off); - CPos prev_max_pos = m_iStartPos + prev_max_off; + CPos prev_max_pos = incPos(m_iStartPos, prev_max_off); + //CPos prev_max_pos = m_iStartPos + prev_max_off; // Update flags // Case [A] @@ -328,8 +312,8 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // This means that m_iEndPos now shifts by 1, // and m_iDropPos must be shifted together with it, // as there's no drop to point. - //m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); - m_iEndPos = m_iStartPos + m_iMaxPosOff; + m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); + //m_iEndPos = m_iStartPos + m_iMaxPosOff; m_iDropPos = m_iEndPos; } else @@ -337,8 +321,8 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // Otherwise we have a drop-after-gap candidate // which is the currently inserted packet. // Therefore m_iEndPos STAYS WHERE IT IS. - //m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); - m_iDropPos = m_iStartPos + (m_iMaxPosOff - 1); + m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + //m_iDropPos = m_iStartPos + (m_iMaxPosOff - 1); } } } @@ -483,12 +467,12 @@ int CRcvBuffer::dropUpTo(int32_t seqno) // Start from here and search fort the next gap m_iEndPos = m_iDropPos = m_iStartPos; - //updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); - updateGapInfo(m_iStartPos + m_iMaxPosOff); + updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); + //updateGapInfo(m_iStartPos + m_iMaxPosOff); // If the nonread position is now behind the starting position, set it to the starting position and update. // Preceding packets were likely missing, and the non read position can probably be moved further now. - if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) + if (!isInUsedRange( m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; updateNonreadPos(); @@ -533,13 +517,13 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro COff minDroppedOffset = COff(-1); int iDropCnt = 0; const COff start_off = COff(max(0, offset_a)); - //const int start_pos = incPos(m_iStartPos, start_off); - const CPos start_pos = m_iStartPos + start_off; + const CPos start_pos = incPos(m_iStartPos, start_off); + //const CPos start_pos = m_iStartPos + start_off; const COff end_off = COff(min((int) m_szSize - 1, offset_b + 1)); - //const int end_pos = incPos(m_iStartPos, end_off); - const CPos end_pos = m_iStartPos + end_off; + const CPos end_pos = incPos(m_iStartPos, end_off); + //const CPos end_pos = m_iStartPos + end_off; bool bDropByMsgNo = msgno > SRT_MSGNO_CONTROL; // Excluding both SRT_MSGNO_NONE (-1) and SRT_MSGNO_CONTROL (0). - for (CPos i = start_pos; i != end_pos; ++i) + for (CPos i = start_pos; i != end_pos; i = incPos(i)) // ++i) { // Check if the unit was already dropped earlier. if (m_entries[i].status == EntryState_Drop) @@ -577,8 +561,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro ++iDropCnt; m_entries[i].status = EntryState_Drop; if (minDroppedOffset == -1) - //minDroppedOffset = offPos(m_iStartPos, i); - minDroppedOffset = i - m_iStartPos; + minDroppedOffset = offPos(m_iStartPos, i); + //minDroppedOffset = i - m_iStartPos; } if (bDropByMsgNo) @@ -588,8 +572,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro // The sender should have the last packet of the message it is requesting to be dropped. // Therefore we don't search forward, but need to check earlier packets in the RCV buffer. // Try to drop by the message number in case the message starts earlier than @a seqnolo. - //const int stop_pos = decPos(m_iStartPos); - const CPos stop_pos = m_iStartPos - COff(1); + const CPos stop_pos = decPos(m_iStartPos); + //const CPos stop_pos = m_iStartPos - COff(1); for (CPos i = start_pos; i != stop_pos; --i) { // Can't drop if message number is not known. @@ -613,8 +597,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro dropUnitInPos(i); m_entries[i].status = EntryState_Drop; // As the search goes backward, i is always earlier than minDroppedOffset. - //minDroppedOffset = offPos(m_iStartPos, i); - minDroppedOffset = i - m_iStartPos; + minDroppedOffset = offPos(m_iStartPos, i); + //minDroppedOffset = i - m_iStartPos; // Break the loop if the start of the message has been found. No need to search further. if (bnd == PB_FIRST) @@ -660,8 +644,8 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const return m_iMaxPosOff > 0; } - //int end_off = offPos(m_iStartPos, m_iEndPos); - COff end_off = m_iEndPos - m_iStartPos; + COff end_off = offPos(m_iStartPos, m_iEndPos); + //COff end_off = m_iEndPos - m_iStartPos; //w_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); w_seq = (m_iStartSeqNo + end_off VALUE) VALUE; @@ -745,7 +729,8 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= remain_pktlen) { releaseUnitInPos(p); - //p = incPos(p); - ++p; + p = incPos(p); + //++p; m_iNotch = 0; m_iStartPos = p; @@ -954,7 +939,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) // Update positions // Set nonread position to the starting position before updating, // because start position was increased, and preceding packets are invalid. - if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) + if (!isInUsedRange( m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; } @@ -986,8 +971,8 @@ bool CRcvBuffer::hasAvailablePackets() const int CRcvBuffer::getRcvDataSize() const { - //return offPos(m_iStartPos, m_iFirstNonreadPos); - return (m_iFirstNonreadPos - m_iStartPos) VALUE; + return offPos(m_iStartPos, m_iFirstNonreadPos) VALUE; + //return (m_iFirstNonreadPos - m_iStartPos) VALUE; } int CRcvBuffer::getTimespan_ms() const @@ -998,8 +983,9 @@ int CRcvBuffer::getTimespan_ms() const if (m_iMaxPosOff == 0) return 0; - //int lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); - CPos lastpos = m_iStartPos + (m_iMaxPosOff - COff(1)); + CPos lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); + //CPos lastpos = m_iStartPos + (m_iMaxPosOff - COff(1)); + // Normally the last position should always be non empty // if TSBPD is enabled (reading out of order is not allowed). // However if decryption of the last packet fails, it may be dropped @@ -1007,8 +993,8 @@ int CRcvBuffer::getTimespan_ms() const SRT_ASSERT(m_entries[lastpos].pUnit != NULL || m_entries[lastpos].status == EntryState_Drop); while (m_entries[lastpos].pUnit == NULL && lastpos != m_iStartPos) { - //lastpos = decPos(lastpos); - --lastpos; + lastpos = decPos(lastpos); + //--lastpos; } if (m_entries[lastpos].pUnit == NULL) @@ -1017,8 +1003,8 @@ int CRcvBuffer::getTimespan_ms() const CPos startpos = m_iStartPos; while (m_entries[startpos].pUnit == NULL && startpos != lastpos) { - //startpos = incPos(startpos); - ++startpos; + startpos = incPos(startpos); + //++startpos; } if (m_entries[startpos].pUnit == NULL) @@ -1077,9 +1063,10 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const std::pair CRcvBuffer::getAvailablePacketsRange() const { - //const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, offPos(m_iStartPos, m_iFirstNonreadPos)); - const int nonread_off = (m_iFirstNonreadPos - m_iStartPos) VALUE; - const int seqno_last = (m_iStartSeqNo + nonread_off) VALUE; + const COff nonread_off = offPos(m_iStartPos, m_iFirstNonreadPos); + const int seqno_last = CSeqNo::incseq(m_iStartSeqNo VALUE, nonread_off VALUE); + //const int nonread_off = (m_iFirstNonreadPos - m_iStartPos) VALUE; + //const int seqno_last = (m_iStartSeqNo + nonread_off) VALUE; return std::pair(m_iStartSeqNo VALUE, seqno_last); } @@ -1201,8 +1188,8 @@ void CRcvBuffer::updateNonreadPos() if (m_iMaxPosOff == 0) return; - //const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. - const CPos end_pos = m_iStartPos + m_iMaxPosOff; // The empty position right after the last valid entry. + const CPos end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. + //const CPos end_pos = m_iStartPos + m_iMaxPosOff; // The empty position right after the last valid entry. CPos pos = m_iFirstNonreadPos; while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail) @@ -1224,8 +1211,8 @@ void CRcvBuffer::updateNonreadPos() // Check PB_LAST only in message mode. if (!m_bMessageAPI || packetAt(i).getMsgBoundary() & PB_LAST) { - //m_iFirstNonreadPos = incPos(i); - m_iFirstNonreadPos = i + COff(1); + m_iFirstNonreadPos = incPos(i); + //m_iFirstNonreadPos = i + COff(1); break; } } @@ -1301,10 +1288,10 @@ bool CRcvBuffer::checkFirstReadableNonOrder() if (m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos == CPos_TRAP || m_iMaxPosOff == COff(0)) return false; - //const int endPos = incPos(m_iStartPos, m_iMaxPosOff); - const CPos endPos = m_iStartPos + m_iMaxPosOff; + const CPos endPos = incPos(m_iStartPos, m_iMaxPosOff); + //const CPos endPos = m_iStartPos + m_iMaxPosOff; int msgno = -1; - for (CPos pos = m_iFirstNonOrderMsgPos; pos != endPos; ++pos) // pos = incPos(pos)) + for (CPos pos = m_iFirstNonOrderMsgPos; pos != endPos; pos = incPos(pos)) // ++pos) { if (!m_entries[pos].pUnit) return false; @@ -1339,7 +1326,8 @@ void CRcvBuffer::updateFirstReadableNonOrder() // Search further packets to the right. // First check if there are packets to the right. //const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; - const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); + //const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); + const CPos lastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); CPos posFirst = CPos_TRAP; CPos posLast = CPos_TRAP; @@ -1397,7 +1385,8 @@ CPos CRcvBuffer::scanNonOrderMessageRight(const CPos startPos, int msgNo) const // Search further packets to the right. // First check if there are packets to the right. //const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; - const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); + //const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); + const CPos lastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); if (startPos == lastPos) return CPos_TRAP; @@ -1508,7 +1497,8 @@ string CRcvBuffer::strFullnessState(int32_t iFirstUnackSeqNo, const time_point& if (!is_zero(nextValidPkt.tsbpd_time)) { ss << count_milliseconds(nextValidPkt.tsbpd_time - tsNow) << "ms"; - const CPos iLastPos = m_iStartPos + m_iMaxPosOff - COff(1); //incPos(m_iStartPos, m_iMaxPosOff - 1); + const CPos iLastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + //const CPos iLastPos = m_iStartPos + m_iMaxPosOff - COff(1); if (m_entries[iLastPos].pUnit) { ss << ", timespan "; @@ -1572,8 +1562,8 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) } // Start position - //int frompos = incPos(m_iStartPos, offset); - CPos frompos = m_iStartPos + offset; + CPos frompos = incPos(m_iStartPos, offset); + //CPos frompos = m_iStartPos + offset; // Ok; likely we should stand at the m_iEndPos position. // If this given position is earlier than this, then @@ -1582,15 +1572,15 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) CSeqNo ret_seq = CSeqNo(SRT_SEQNO_NONE); COff ret_off = m_iMaxPosOff; - - COff end_off = m_iEndPos - m_iStartPos; //offPos(m_iStartPos, m_iEndPos); + COff end_off = offPos(m_iStartPos, m_iEndPos); + //COff end_off = m_iEndPos - m_iStartPos; if (offset < end_off) { // If m_iEndPos has such a value, then there are // no loss packets at all. if (end_off != m_iMaxPosOff) { - //ret_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + //ret_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, end_off VALUE); ret_seq = m_iStartSeqNo + end_off VALUE; ret_off = end_off; } @@ -1605,8 +1595,8 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // REUSE offset as a control variable for (; offset < m_iMaxPosOff; ++offset) { - //int pos = incPos(m_iStartPos, offset); - const CPos pos = m_iStartPos + offset; + const CPos pos = incPos(m_iStartPos, offset); + //const CPos pos = m_iStartPos + offset; if (m_entries[pos].status == EntryState_Empty) { ret_off = offset; @@ -1632,11 +1622,11 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // points to an empty cell. for (COff off = ret_off + COff(1); off < m_iMaxPosOff; ++off) { - //int pos = incPos(m_iStartPos, off); - const CPos pos = m_iStartPos + off; + const CPos pos = incPos(m_iStartPos, off); + //const CPos pos = m_iStartPos + off; if (m_entries[pos].status != EntryState_Empty) { - //*pw_end = CSeqNo::incseq(m_iStartSeqNo, off - 1); + //*pw_end = CSeqNo::incseq(m_iStartSeqNo, (off - 1) VALUE); *pw_end = (m_iStartSeqNo + (off - COff(1)) VALUE) VALUE; return ret_seq VALUE; } @@ -1645,7 +1635,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // Fallback - this should be impossible, so issue a log. LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << frompos VALUE << " %" //<< CSeqNo::incseq(m_iStartSeqNo, ret_off) - << (m_iStartSeqNo + ret_off) VALUE + << (m_iStartSeqNo + ret_off VALUE) VALUE << " not followed by any valid cell"); // Return this in the last resort - this could only be a situation when diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 18450947d..ff659a45c 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -18,7 +18,7 @@ #include "utilities.h" #define USE_WRAPPERS 1 -#define USE_OPERATORS 1 +#define USE_OPERATORS 0 namespace srt { @@ -33,7 +33,7 @@ struct CPos int isize() const {return *psize;} - explicit CPos(explicit_t ps, explicit_t val): value(val), psize(ps) {} + explicit CPos(const size_t* ps, int val): value(val), psize(ps) {} int val() const { return value; } explicit operator int() const {return value;} @@ -642,25 +642,64 @@ class CRcvBuffer const CUnit* peek(int32_t seqno); private: - /* - inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } - inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } - inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : int(m_szSize + pos2 - pos1); } + //* + inline CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos(&m_szSize, (pos VALUE + inc VALUE) % m_szSize); } + inline CPos decPos(CPos pos) const { return (pos VALUE - 1) >= 0 ? CPos(&m_szSize, pos VALUE - 1) : CPos(&m_szSize, m_szSize - 1); } + inline COff offPos(CPos pos1, CPos pos2) const + { + int diff = pos2 VALUE - pos1 VALUE; + if (diff >= 0) + { + return COff(diff); + } + return COff(m_szSize + diff); + } + + inline COff posToOff(CPos pos) const { return offPos(m_iStartPos, pos); } /// @brief Compares the two positions in the receiver buffer relative to the starting position. /// @param pos2 a position in the receiver buffer. /// @param pos1 a position in the receiver buffer. /// @return a positive value if pos2 is ahead of pos1; a negative value, if pos2 is behind pos1; otherwise returns 0. - inline int cmpPos(int pos2, int pos1) const + inline COff cmpPos(CPos pos2, CPos pos1) const { // XXX maybe not the best implementation, but this keeps up to the rule. // Maybe use m_iMaxPosOff to ensure a position is not behind the m_iStartPos. - const int off1 = pos1 >= m_iStartPos ? pos1 - m_iStartPos : pos1 + (int)m_szSize - m_iStartPos; - const int off2 = pos2 >= m_iStartPos ? pos2 - m_iStartPos : pos2 + (int)m_szSize - m_iStartPos; - return off2 - off1; + return posToOff(pos2) - posToOff(pos1); + } + // */ + + // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize]. + // The right edge is included because we expect iFirstNonreadPos to be + // right after the last valid packet position if all packets are available. + static bool isInRange(CPos iStartPos, COff iMaxPosOff, size_t iSize, CPos iFirstNonreadPos) + { + if (iFirstNonreadPos == iStartPos) + return true; + + const CPos iLastPos = CPos(iStartPos.psize, (iStartPos VALUE + iMaxPosOff VALUE) % int(iSize)); + //const CPos iLastPos = iStartPos + iMaxPosOff; + const bool isOverrun = iLastPos VALUE < iStartPos VALUE; + + if (isOverrun) + return iFirstNonreadPos VALUE > iStartPos VALUE || iFirstNonreadPos VALUE <= iLastPos VALUE; + + return iFirstNonreadPos VALUE > iStartPos VALUE && iFirstNonreadPos VALUE <= iLastPos VALUE; + } + + bool isInUsedRange(CPos iFirstNonreadPos) + { + if (iFirstNonreadPos == m_iStartPos) + return true; + + // DECODE the iFirstNonreadPos + int diff = iFirstNonreadPos VALUE - m_iStartPos VALUE; + if (diff < 0) + diff += m_szSize; + + return diff <= m_iMaxPosOff VALUE; } - */ // NOTE: Assumes that pUnit != NULL CPacket& packetAt(CPos pos) { return m_entries[pos].pUnit->m_Packet; } From a2b1b44b35c883ac176b5273856d1224bc5af089 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 17 Jun 2024 11:40:52 +0200 Subject: [PATCH 22/28] First working version with end/drop as offset --- CMakeLists.txt | 2 +- srtcore/buffer_rcv.cpp | 611 +++++++++++++++++++++------------------ srtcore/buffer_rcv.h | 123 +++++--- test/test_buffer_rcv.cpp | 71 +++++ testing/testmedia.cpp | 24 +- 5 files changed, 493 insertions(+), 338 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5994d3b7..70e1f50a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1528,7 +1528,7 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) #set_tests_properties(test-srt PROPERTIES RUN_SERIAL TRUE) else() set_tests_properties(${tests_srt} PROPERTIES RUN_SERIAL TRUE) - gtest_discover_tests(test-srt) + #gtest_discover_tests(test-srt) endif() enable_testing() diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 539c51f9e..1562d0264 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -104,14 +104,14 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b , m_szSize(size) // TODO: maybe just use m_entries.size() , m_pUnitQueue(unitqueue) , m_iStartSeqNo(initSeqNo) // NOTE: SRT_SEQNO_NONE is allowed here. - , m_iStartPos(&m_szSize, 0) - , m_iEndPos(&m_szSize, 0) - , m_iDropPos(&m_szSize, 0) - , m_iFirstNonreadPos(&m_szSize, 0) + , m_iStartPos(0) + , m_iEndOff(0) + , m_iDropOff(0) + , m_iFirstNonreadPos(0) , m_iMaxPosOff(0) , m_iNotch(0) , m_numNonOrderPackets(0) - , m_iFirstNonOrderMsgPos(&m_szSize, CPos_TRAP.val()) + , m_iFirstNonOrderMsgPos(CPos_TRAP) , m_bPeerRexmitFlag(true) , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) @@ -138,8 +138,8 @@ void CRcvBuffer::debugShowState(const char* source SRT_ATR_UNUSED) { HLOGC(brlog.Debug, log << "RCV-BUF-STATE(" << source << ") start=" << m_iStartPos VALUE - << " end=" << m_iEndPos VALUE - << " drop=" << m_iDropPos VALUE + << " end=+" << m_iEndOff VALUE + << " drop=+" << m_iDropOff VALUE << " max-off=+" << m_iMaxPosOff VALUE << " seq[start]=%" << m_iStartSeqNo VALUE); } @@ -211,7 +211,7 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // Set to a value, if due to insertion there was added // a packet that is earlier to be retrieved than the earliest // currently available packet. - time_point earlier_time = updatePosInfo(unit, prev_max_off, newpktpos, extended_end); + time_point earlier_time = updatePosInfo(unit, prev_max_off, offset, extended_end); InsertInfo ireport (InsertInfo::INSERTED); ireport.first_time = earlier_time; @@ -237,102 +237,103 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) void CRcvBuffer::getAvailInfo(CRcvBuffer::InsertInfo& w_if) { - CPos fallback_pos = CPos_TRAP; - if (!m_tsbpd.isEnabled()) - { - // In case when TSBPD is off, we take into account the message mode - // where messages may potentially span for multiple packets, therefore - // the only "next deliverable" is the first complete message that satisfies - // the order requirement. - // NOTE THAT this field can as well be -1 already. - fallback_pos = m_iFirstNonOrderMsgPos; - } - else if (m_iDropPos != m_iEndPos) - { - // With TSBPD regard the drop position (regardless if - // TLPKTDROP is currently on or off), if "exists", that - // is, m_iDropPos != m_iEndPos. - fallback_pos = m_iDropPos; - } - - // This finds the first possible available packet, which is - // preferably at cell 0, but if not available, try also with - // given fallback position (unless it's -1). - const CPacket* pkt = tryAvailPacketAt(fallback_pos, (w_if.avail_range)); - if (pkt) - { - w_if.first_seq = CSeqNo(pkt->getSeqNo()); - } -} - + // This finds the first possible available packet, which is + // preferably at cell 0, but if not available, try also with + // given fallback position, if it's set + if (m_entries[m_iStartPos].status == EntryState_Avail) + { + const CPacket* pkt = &packetAt(m_iStartPos); + SRT_ASSERT(pkt); + w_if.avail_range = m_iEndOff; + w_if.first_seq = CSeqNo(pkt->getSeqNo()); + return; + } -const CPacket* CRcvBuffer::tryAvailPacketAt(CPos pos, COff& w_span) -{ - if (m_entries[m_iStartPos].status == EntryState_Avail) - { - pos = m_iStartPos; - w_span = offPos(m_iStartPos, m_iEndPos); - //w_span = m_iEndPos - m_iStartPos; - } + // If not the first position, probe the skipped positions: + // - for live mode, check the DROP position + // (for potential after-drop reading) + // - for message mode, check the non-order message position + // (for potential out-of-oder message delivery) - if (pos == CPos_TRAP) - { - w_span = COff(0); - return NULL; - } + const CPacket* pkt = NULL; + if (m_tsbpd.isEnabled()) + { + // With TSBPD you can rely on drop position, if set + if (m_iDropOff) + { + pkt = &packetAt(incPos(m_iStartPos, m_iDropOff)); + SRT_ASSERT(pkt); + } + } + else + { + // Message-mode: try non-order read position. + if (m_iFirstNonOrderMsgPos != CPos_TRAP) + { + pkt = &packetAt(m_iFirstNonOrderMsgPos); + SRT_ASSERT(pkt); + } + } - SRT_ASSERT(m_entries[pos].pUnit != NULL); + if (!pkt) + { + // This is default, but set just in case + // The default seq is SRT_SEQNO_NONE. + w_if.avail_range = COff(0); + return; + } - // TODO: we know that at least 1 packet is available, but only - // with m_iEndPos we know where the true range is. This could also - // be implemented for message mode, but still this would employ - // a separate begin-end range declared for a complete out-of-order - // message. - w_span = COff(1); - return &packetAt(pos); + // TODO: we know that at least 1 packet is available, but only + // with m_iEndOff we know where the true range is. This could also + // be implemented for message mode, but still this would employ + // a separate begin-end range declared for a complete out-of-order + // message. + w_if.avail_range = COff(1); + w_if.first_seq = CSeqNo(pkt->getSeqNo()); } -CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff prev_max_off, const CPos newpktpos, const bool extended_end) + +// This function is called exclusively after packet insertion. +// This will update also m_iEndOff and m_iDropOff fields (the latter +// regardless of the TSBPD mode). +CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff prev_max_off, + const COff offset, + const bool extended_end) { time_point earlier_time; - CPos prev_max_pos = incPos(m_iStartPos, prev_max_off); - //CPos prev_max_pos = m_iStartPos + prev_max_off; - // Update flags // Case [A] if (extended_end) { // THIS means that the buffer WAS CONTIGUOUS BEFORE. - if (m_iEndPos == prev_max_pos) + if (m_iEndOff == prev_max_off) { // THIS means that the new packet didn't CAUSE a gap if (m_iMaxPosOff == prev_max_off + 1) { - // This means that m_iEndPos now shifts by 1, - // and m_iDropPos must be shifted together with it, + // This means that m_iEndOff now shifts by 1, + // and m_iDropOff must be shifted together with it, // as there's no drop to point. - m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); - //m_iEndPos = m_iStartPos + m_iMaxPosOff; - m_iDropPos = m_iEndPos; + m_iEndOff = m_iMaxPosOff; + m_iDropOff = 0; } else { // Otherwise we have a drop-after-gap candidate // which is the currently inserted packet. - // Therefore m_iEndPos STAYS WHERE IT IS. - m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); - //m_iDropPos = m_iStartPos + (m_iMaxPosOff - 1); + // Therefore m_iEndOff STAYS WHERE IT IS. + m_iDropOff = m_iMaxPosOff - 1; } } } // - // Since this place, every newpktpos is in the range - // between m_iEndPos (inclusive) and a position for m_iMaxPosOff. + // Since this place, every 'offset' is in the range + // between m_iEndOff (inclusive) and m_iMaxPosOff. // Here you can use prev_max_pos as the position represented // by m_iMaxPosOff, as if !extended_end, it was unchanged. - else if (newpktpos == m_iEndPos) + else if (offset == m_iEndOff) { // Case [D]: inserted a packet at the first gap following the // contiguous region. This makes a potential to extend the @@ -342,22 +343,14 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // new earliest packet now. In any other situation under this // condition there's some contiguous packet range preceding // this position. - if (m_iEndPos == m_iStartPos) + if (m_iEndOff == 0) { earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); } - updateGapInfo(prev_max_pos); + updateGapInfo(); } - // XXX Not sure if that's the best performant comparison - // What is meant here is that newpktpos is between - // m_iEndPos and m_iDropPos, though we know it's after m_iEndPos. - // CONSIDER: make m_iDropPos rather m_iDropOff, this will make - // this comparison a simple subtraction. Note that offset will - // have to be updated on every shift of m_iStartPos. - - //else if (cmpPos(newpktpos, m_iDropPos) < 0) - else if (newpktpos.cmp(m_iDropPos, m_iStartPos) < 0) + else if (offset < m_iDropOff) { // Case [C]: the newly inserted packet precedes the // previous earliest delivery position after drop, @@ -370,10 +363,10 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // // We know it because if the position has filled a gap following // a valid packet, this preceding valid packet would be pointed - // by m_iDropPos, or it would point to some earlier packet in a + // by m_iDropOff, or it would point to some earlier packet in a // contiguous series of valid packets following a gap, hence // the above condition wouldn't be satisfied. - m_iDropPos = newpktpos; + m_iDropOff = offset; // If there's an inserted packet BEFORE drop-pos (which makes it // a new drop-pos), while the very first packet is absent (the @@ -382,7 +375,7 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // position, but still following some earlier contiguous range // of valid packets - so it's earlier than previous drop, but // not earlier than the earliest packet. - if (m_iStartPos == m_iEndPos) + if (m_iEndOff == 0) { earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); } @@ -392,37 +385,94 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p return earlier_time; } -void CRcvBuffer::updateGapInfo(CPos prev_max_pos) +// This function is called when the m_iEndOff has been set to a new +// position and the m_iDropOff should be calculated since that position again. +void CRcvBuffer::updateGapInfo() { - CPos pos = m_iEndPos; + COff from = m_iEndOff, to = m_iMaxPosOff; + SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iMaxPosOff)].status == EntryState_Empty); - // First, search for the next gap, max until m_iMaxPosOff. - for ( ; pos != prev_max_pos; ++pos /*pos = incPos(pos)*/) + CPos pos = incPos(m_iStartPos, from); + + if (m_entries[pos].status == EntryState_Avail) { - if (m_entries[pos].status == EntryState_Empty) + + CPos end_pos = incPos(m_iStartPos, m_iMaxPosOff); + + for (; pos != end_pos; pos = incPos(pos)) { - break; + if (m_entries[pos].status != EntryState_Avail) + break; } + + m_iEndOff = offPos(m_iStartPos, pos); } - if (pos == prev_max_pos) + + // XXX This should be this way, but there are still inconsistencies + // in the message code. + //SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status == EntryState_Empty); + SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status != EntryState_Avail); + + // XXX Controversy: m_iDropOff is only used in case when SRTO_TLPKTDROP + // is set. This option is not handled in message mode, only in live mode. + // Dropping by packet makes sense only in case of packetwise reading, + // which isn't the case of neither stream nor message mode. + if (!m_tsbpd.isEnabled()) { - // Reached the end and found no gaps. - m_iEndPos = prev_max_pos; - m_iDropPos = prev_max_pos; + m_iDropOff = 0; + return; } - else + + // Do not touch m_iDropOff if it's still beside the contiguous + // region. DO NOT SEARCH for m_iDropOff if m_iEndOff is max + // because this means that the whole buffer is contiguous. + // That would simply find nothing and only uselessly burden the + // performance by searching for a not present empty cell. + + // Also check if the current drop position is a readable packet. + // If not, start over. + CPos drop_pos = incPos(m_iStartPos, m_iDropOff); + + if (m_iDropOff < m_iEndOff || m_entries[drop_pos].status != EntryState_Avail) { - // Found a gap at pos - m_iEndPos = pos; - m_iDropPos = pos; // fallback, although SHOULD be impossible - // So, search for the first position to drop up to. - for ( ; pos != prev_max_pos; ++pos /*pos = incPos(pos)*/) + m_iDropOff = 0; + if (m_iEndOff < m_iMaxPosOff) { - if (m_entries[pos].status != EntryState_Empty) + int maxend = m_szSize - m_iStartPos VALUE; + int ifrom = m_iEndOff + 1; + int ito = m_iMaxPosOff VALUE; + + bool found = false; + for (int i = ifrom; i < std::min(maxend, ito); ++i) { - m_iDropPos = pos; - break; + if (m_entries[CPos(i)].status == EntryState_Avail) + { + m_iDropOff = i; + found = true; + break; + } } + + if (!found && ito > maxend) + { + int upto = ito - maxend; + for (int i = 0; i < upto; ++i) + { + if (m_entries[CPos(i)].status == EntryState_Avail) + { + m_iDropOff = i; + found = true; + break; + } + } + } + + // Must be found somewhere, worst case at the position + // of m_iMaxPosOff-1. If no finding loop caught it somehow, + // it will remain at 0. The case when you have empty packets + // in the busy range is only with message mode after reading + // packets out-of-order, but this doesn't use tsbpd mode. + SRT_ASSERT(m_iDropOff != 0); } } } @@ -444,9 +494,9 @@ int CRcvBuffer::dropUpTo(int32_t seqno) return 0; } - m_iMaxPosOff -= len; - if (m_iMaxPosOff < 0) - m_iMaxPosOff = 0; + m_iMaxPosOff = decOff(m_iMaxPosOff, len); + m_iEndOff = decOff(m_iEndOff, len); + m_iDropOff = decOff(m_iDropOff, len); const int iDropCnt = len VALUE; while (len VALUE > 0) @@ -454,8 +504,8 @@ int CRcvBuffer::dropUpTo(int32_t seqno) dropUnitInPos(m_iStartPos); m_entries[m_iStartPos].status = EntryState_Empty; SRT_ASSERT(m_entries[m_iStartPos].pUnit == NULL && m_entries[m_iStartPos].status == EntryState_Empty); - //m_iStartPos = incPos(m_iStartPos); - ++m_iStartPos; + m_iStartPos = incPos(m_iStartPos); + //++m_iStartPos; --len; } @@ -465,10 +515,7 @@ int CRcvBuffer::dropUpTo(int32_t seqno) // (This call MAY shift m_iStartSeqNo further.) releaseNextFillerEntries(); - // Start from here and search fort the next gap - m_iEndPos = m_iDropPos = m_iStartPos; - updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); - //updateGapInfo(m_iStartPos + m_iMaxPosOff); + updateGapInfo(); // If the nonread position is now behind the starting position, set it to the starting position and update. // Preceding packets were likely missing, and the non read position can probably be moved further now. @@ -538,7 +585,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro if (bKeepExisting && bnd == PB_SOLO) { bDropByMsgNo = false; // Solo packet, don't search for the rest of the message. - LOGC(rbuflog.Debug, + HLOGC(rbuflog.Debug, log << "CRcvBuffer::dropMessage(): Skipped dropping an existing SOLO packet %" << packetAt(i).getSeqNo() << "."); continue; @@ -565,6 +612,14 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro //minDroppedOffset = i - m_iStartPos; } + if (end_off > m_iMaxPosOff) + { + HLOGC(rbuflog.Debug, log << "CRcvBuffer::dropMessage: requested to drop up to %" << seqnohi + << " with highest in the buffer %" << CSeqNo::incseq(m_iStartSeqNo VALUE, end_off) + << " - updating the busy region"); + m_iMaxPosOff = end_off; + } + if (bDropByMsgNo) { // If msgno is specified, potentially not the whole message was dropped using seqno range. @@ -574,7 +629,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro // Try to drop by the message number in case the message starts earlier than @a seqnolo. const CPos stop_pos = decPos(m_iStartPos); //const CPos stop_pos = m_iStartPos - COff(1); - for (CPos i = start_pos; i != stop_pos; --i) + for (CPos i = start_pos; i != stop_pos; i = decPos(i)) { // Can't drop if message number is not known. if (!m_entries[i].pUnit) // also dropped earlier. @@ -607,14 +662,23 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro IF_RCVBUF_DEBUG(scoped_log.ss << " iDropCnt " << iDropCnt); } + if (iDropCnt) + { + // We don't need the drop position, if we allow to drop messages by number + // and with that value we risk that drop was pointing to a dropped packet. + // Theoretically to make it consistent we need to shift the value to the + // next found packet, but we don't need this information if we use the message + // mode (because drop-by-packet is not supported in this mode) and this + // will burden the performance for nothing. + m_iDropOff = 0; + } + // Check if units before m_iFirstNonreadPos are dropped. const bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); releaseNextFillerEntries(); - // XXX TEST AND FIX - // Start from the last updated start pos and search fort the next gap - m_iEndPos = m_iDropPos = m_iStartPos; - updateGapInfo(end_pos); + updateGapInfo(); + IF_HEAVY_LOGGING(debugShowState( ("dropmsg off %" + Sprint(seqnolo) + " #" + Sprint(msgno)).c_str())); @@ -636,7 +700,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const { - if (m_iStartPos == m_iEndPos) + if (m_iEndOff == 0) { // Initial contiguous region empty (including empty buffer). HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo VALUE); @@ -644,18 +708,15 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const return m_iMaxPosOff > 0; } - COff end_off = offPos(m_iStartPos, m_iEndPos); - //COff end_off = m_iEndPos - m_iStartPos; - - //w_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); - w_seq = (m_iStartSeqNo + end_off VALUE) VALUE; + w_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, m_iEndOff VALUE); + //w_seq = (m_iStartSeqNo + m_iEndOff VALUE) VALUE; - HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << end_off VALUE + HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << m_iEndOff VALUE << " maxD=" << m_iMaxPosOff VALUE << " base=%" << m_iStartSeqNo VALUE << " end=%" << w_seq); - return (end_off < m_iMaxPosOff); + return (m_iEndOff < m_iMaxPosOff); } int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair* pw_seqrange) @@ -681,7 +742,17 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= 0); m_iStartSeqNo = CSeqNo(pktseqno) + 1; + ++nskipped; } else { @@ -759,72 +814,39 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= 0); + + m_iEndOff = decOff(m_iEndOff, len); + } countBytes(-pkts_read, -bytes_extracted); releaseNextFillerEntries(); + // This will update the end position + updateGapInfo(); + if (!isInUsedRange( m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; //updateNonreadPos(); } - // Now that we have m_iStartPos potentially shifted, reinitialize - // m_iEndPos and m_iDropPos. - - CPos pend_pos = incPos(m_iStartPos, m_iMaxPosOff); - //CPos pend_pos = m_iStartPos + m_iMaxPosOff; - - // First check: is anything in the beginning - if (m_entries[m_iStartPos].status == EntryState_Avail) - { - // If so, shift m_iEndPos up to the first nonexistent unit - // XXX Try to optimize search by splitting into two loops if necessary. - - m_iEndPos = incPos(m_iStartPos); - //m_iEndPos = m_iStartPos + COff(1); - while (m_entries[m_iEndPos].status == EntryState_Avail) - { - m_iEndPos = incPos(m_iEndPos); - //m_iEndPos = m_iEndPos + COff(1); - if (m_iEndPos == pend_pos) - break; - } - - // If we had first packet available, then there's also no drop pos. - m_iDropPos = m_iEndPos; - } - else - { - // If not, reset m_iEndPos and search for the first after-drop candidate. - m_iEndPos = m_iStartPos; - m_iDropPos = m_iEndPos; - - // The container could have become empty. - // Stay here if so. - if (m_iStartPos != pend_pos) - { - while (m_entries[m_iDropPos].status != EntryState_Avail) - { - m_iDropPos = incPos(m_iDropPos); - //m_iDropPos = m_iDropPos + COff(1); - if (m_iDropPos == pend_pos) - { - // Nothing found - set drop pos equal to end pos, - // which means there's no drop - m_iDropPos = m_iEndPos; - break; - } - } - } - } - - if (!m_tsbpd.isEnabled()) // We need updateFirstReadableNonOrder() here even if we are reading inorder, // incase readable inorder packets are all read out. @@ -923,6 +945,15 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) m_iStartPos = p; --m_iMaxPosOff; SRT_ASSERT(m_iMaxPosOff VALUE >= 0); + + --m_iEndOff; + if (m_iEndOff < 0) + m_iEndOff = 0; + + --m_iDropOff; + if (m_iDropOff < 0) + m_iDropOff = 0; + //m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); ++m_iStartSeqNo; } @@ -1043,10 +1074,11 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const pkt = &packetAt(m_iStartPos); } // If not, get the information from the drop - else if (m_iDropPos != m_iEndPos) + else if (m_iDropOff) { - SRT_ASSERT(m_entries[m_iDropPos].pUnit); - pkt = &packetAt(m_iDropPos); + CPos drop_pos = incPos(m_iStartPos, m_iDropOff); + SRT_ASSERT(m_entries[drop_pos].pUnit); + pkt = &packetAt(drop_pos); pi.seq_gap = true; // Available, but after a drop. } else @@ -1165,21 +1197,43 @@ bool CRcvBuffer::dropUnitInPos(CPos pos) return true; } -void CRcvBuffer::releaseNextFillerEntries() +int CRcvBuffer::releaseNextFillerEntries() { CPos pos = m_iStartPos; + int nskipped = 0; + while (m_entries[pos].status == EntryState_Read || m_entries[pos].status == EntryState_Drop) { + if (nskipped == m_iMaxPosOff) + { + // This should never happen. All the previously read- or drop-marked + // packets should be contained in the range up to m_iMaxPosOff. Do not + // let the buffer ride any further and report the problem. Still stay there. + LOGC(rbuflog.Error, log << "releaseNextFillerEntries: IPE: Read/Drop status outside the busy range!"); + break; + } + //m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); ++m_iStartSeqNo; releaseUnitInPos(pos); - //pos = incPos(pos); - ++pos; + pos = incPos(pos); + //++pos; m_iStartPos = pos; - --m_iMaxPosOff; - if (m_iMaxPosOff < 0) - m_iMaxPosOff = 0; + ++nskipped; } + + if (!nskipped) + { + return nskipped; + } + + m_iMaxPosOff -= nskipped; + m_iEndOff = decOff(m_iEndOff, nskipped); + + // Drop off will be updated after that call, if needed. + m_iDropOff = 0; + + return nskipped; } // TODO: Is this function complete? There are some comments left inside. @@ -1197,7 +1251,7 @@ void CRcvBuffer::updateNonreadPos() if (m_bMessageAPI && (packetAt(pos).getMsgBoundary() & PB_FIRST) == 0) break; - for (CPos i = pos; i != end_pos; ++i) // i = incPos(i)) + for (CPos i = pos; i != end_pos; i = incPos(i)) { if (!m_entries[i].pUnit || m_entries[pos].status != EntryState_Avail) { @@ -1226,7 +1280,7 @@ void CRcvBuffer::updateNonreadPos() CPos CRcvBuffer::findLastMessagePkt() { - for (CPos i = m_iStartPos; i != m_iFirstNonreadPos; ++i) //i = incPos(i)) + for (CPos i = m_iStartPos; i != m_iFirstNonreadPos; i = incPos(i)) { SRT_ASSERT(m_entries[i].pUnit); @@ -1333,7 +1387,7 @@ void CRcvBuffer::updateFirstReadableNonOrder() CPos posLast = CPos_TRAP; int msgNo = -1; - for (CPos pos = m_iStartPos; outOfOrderPktsRemain; ++pos) //pos = incPos(pos)) + for (CPos pos = m_iStartPos; outOfOrderPktsRemain; pos = incPos(pos)) { if (!m_entries[pos].pUnit) { @@ -1393,8 +1447,8 @@ CPos CRcvBuffer::scanNonOrderMessageRight(const CPos startPos, int msgNo) const CPos pos = startPos; do { - //pos = incPos(pos); - ++pos; + pos = incPos(pos); + //++pos; if (!m_entries[pos].pUnit) break; @@ -1424,8 +1478,8 @@ CPos CRcvBuffer::scanNonOrderMessageLeft(const CPos startPos, int msgNo) const CPos pos = startPos; do { - //pos = decPos(pos); - --pos; + pos = decPos(pos); + //--pos; if (!m_entries[pos].pUnit) return CPos_TRAP; @@ -1549,100 +1603,89 @@ void CRcvBuffer::updRcvAvgDataSize(const steady_clock::time_point& now) int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) { + // This means that there are no lost seqs at all, no matter + // from which position they would have to be checked. + if (m_iEndOff == m_iMaxPosOff) + return SRT_SEQNO_NONE; + //int offset = CSeqNo::seqoff(m_iStartSeqNo, fromseq); int offset_val = CSeqNo(fromseq) - m_iStartSeqNo; COff offset (offset_val); - // Check if it's still inside the buffer - if (offset_val < 0 || offset >= m_iMaxPosOff) + // Check if it's still inside the buffer. + // Skip the region from 0 to m_iEndOff because this + // region is by definition contiguous and contains no loss. + if (offset_val < m_iEndOff || offset >= m_iMaxPosOff) { HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset VALUE << " for %" << fromseq << " (with max=" << m_iMaxPosOff VALUE << ") - NO LOSS FOUND"); return SRT_SEQNO_NONE; } - // Start position - CPos frompos = incPos(m_iStartPos, offset); - //CPos frompos = m_iStartPos + offset; - - // Ok; likely we should stand at the m_iEndPos position. - // If this given position is earlier than this, then - // m_iEnd stands on the first loss, unless it's equal - // to the position pointed by m_iMaxPosOff. - - CSeqNo ret_seq = CSeqNo(SRT_SEQNO_NONE); - COff ret_off = m_iMaxPosOff; - COff end_off = offPos(m_iStartPos, m_iEndPos); - //COff end_off = m_iEndPos - m_iStartPos; - if (offset < end_off) + // Check if this offset is equal to m_iEndOff. If it is, + // then you have the loss sequence exactly the one that + // was passed. Skip now, pw_end was not requested. + if (offset == m_iEndOff) { - // If m_iEndPos has such a value, then there are - // no loss packets at all. - if (end_off != m_iMaxPosOff) + if (pw_end) { - //ret_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, end_off VALUE); - ret_seq = m_iStartSeqNo + end_off VALUE; - ret_off = end_off; + // If the offset is exactly at m_iEndOff, then + // m_iDropOff will mark the end of gap. + if (m_iDropOff) + *pw_end = CSeqNo::incseq(m_iStartSeqNo VALUE, m_iDropOff); + else + { + LOGC(rbuflog.Error, log << "getFirstLossSeq: IPE: drop-off=0 while seq-off == end-off != max-off"); + *pw_end = fromseq; + } } + return fromseq; } - else - { - // Could be strange, but just as the caller wishes: - // find the first loss since this point on - // You can't rely on m_iEndPos, you are beyond that now. - // So simply find the next hole. - // REUSE offset as a control variable - for (; offset < m_iMaxPosOff; ++offset) + int ret_seq = SRT_SEQNO_NONE; + int loss_off = 0; + // Now find the first empty position since here, + // up to m_iMaxPosOff. Checking against m_iDropOff + // makes no sense because if it is not 0, you'll + // find it earlier by checking packet presence. + for (int off = offset; off < m_iMaxPosOff; ++off) + { + CPos ipos ((m_iStartPos VALUE + off) % m_szSize); + if (m_entries[ipos].status == EntryState_Empty) { - const CPos pos = incPos(m_iStartPos, offset); - //const CPos pos = m_iStartPos + offset; - if (m_entries[pos].status == EntryState_Empty) - { - ret_off = offset; - //ret_seq = CSeqNo::incseq(m_iStartSeqNo, offset); - ret_seq = m_iStartSeqNo + offset VALUE; - break; - } + ret_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, off); + loss_off = off; + break; } } - // If found no loss, just return this value and do not - // rewrite nor look for anything. - - // Also no need to search anything if only the beginning was - // being looked for. - if (ret_seq == CSeqNo(SRT_SEQNO_NONE) || !pw_end) - return ret_seq VALUE; - - // We want also the end range, so continue from where you - // stopped. + if (ret_seq == SRT_SEQNO_NONE) + { + // This is theoretically possible if we search from behind m_iEndOff, + // after m_iDropOff. This simply means that we are trying to search + // behind the last gap in the buffer. + return ret_seq; + } - // Start from ret_off + 1 because we know already that ret_off - // points to an empty cell. - for (COff off = ret_off + COff(1); off < m_iMaxPosOff; ++off) + // We get this position, so search for the end of gap + if (pw_end) { - const CPos pos = incPos(m_iStartPos, off); - //const CPos pos = m_iStartPos + off; - if (m_entries[pos].status != EntryState_Empty) + for (int off = loss_off+1; off < m_iMaxPosOff; ++off) { - //*pw_end = CSeqNo::incseq(m_iStartSeqNo, (off - 1) VALUE); - *pw_end = (m_iStartSeqNo + (off - COff(1)) VALUE) VALUE; - return ret_seq VALUE; + CPos ipos ((m_iStartPos VALUE + off) % m_szSize); + if (m_entries[ipos].status != EntryState_Empty) + { + *pw_end = CSeqNo::incseq(m_iStartSeqNo VALUE, off); + return ret_seq; + } } - } - // Fallback - this should be impossible, so issue a log. - LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << frompos VALUE << " %" - //<< CSeqNo::incseq(m_iStartSeqNo, ret_off) - << (m_iStartSeqNo + ret_off VALUE) VALUE - << " not followed by any valid cell"); - - // Return this in the last resort - this could only be a situation when - // a packet has somehow disappeared, but it contains empty cells up to the - // end of buffer occupied range. This shouldn't be possible at all because - // there must be a valid packet at least at the last occupied cell. - return SRT_SEQNO_NONE; + // Should not be possible to not find an existing packet + // following the gap, otherwise there would be no gap. + LOGC(rbuflog.Error, log << "getFirstLossSeq: IPE: gap since %" << ret_seq << " not covered by existing packet"); + *pw_end = ret_seq; + } + return ret_seq; } diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index ff659a45c..230efa481 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -29,22 +29,39 @@ namespace srt struct CPos { int value; +#if USE_OPERATORS const size_t* psize; - int isize() const {return *psize;} +#endif + +#if USE_OPERATORS + explicit CPos(const size_t* ps SRT_ATR_UNUSED, int val) + : value(val) + , psize(ps) + {} + +#else + explicit CPos(int val): value(val) {} +#endif - explicit CPos(const size_t* ps, int val): value(val), psize(ps) {} int val() const { return value; } explicit operator int() const {return value;} - CPos(const CPos& src): value(src.value), psize(src.psize) {} + CPos(const CPos& src): value(src.value) +#if USE_OPERATORS + , psize(src.psize) +#endif + {} CPos& operator=(const CPos& src) { +#if USE_OPERATORS psize = src.psize; +#endif value = src.value; return *this; } +#if USE_OPERATORS int cmp(CPos other, CPos start) const { int pos2 = value; @@ -72,6 +89,7 @@ struct CPos value = 0; return *this; } +#endif bool operator == (CPos other) const { return value == other.value; } bool operator != (CPos other) const { return value != other.value; } @@ -116,6 +134,15 @@ struct COff bool operator > (int other) const { return value > other; } bool operator <= (int other) const { return value <= other; } bool operator >= (int other) const { return value >= other; } + + friend bool operator == (int value, COff that) { return value == that.value; } + friend bool operator != (int value, COff that) { return value != that.value; } + friend bool operator < (int value, COff that) { return value < that.value; } + friend bool operator > (int value, COff that) { return value > that.value; } + friend bool operator <= (int value, COff that) { return value <= that.value; } + friend bool operator >= (int value, COff that) { return value >= that.value; } + + operator bool() const { return value != 0; } }; #define VALUE .val() @@ -161,8 +188,7 @@ inline CSeqNo operator-(CSeqNo seq, COff off) #endif -const size_t fix_rollover = 16; -const CPos CPos_TRAP (&fix_rollover, -1); +const CPos CPos_TRAP (-1); #else #define VALUE @@ -174,22 +200,33 @@ const int CPos_TRAP = -1; // // Circular receiver buffer. // +// ICR = Initial Contiguous Region: all cells here contain valid packets +// SCRAP REGION: Region with possibly filled or empty cells +// NOTE: in scrap region, the first cell is empty and the last one filled. +// SPARE REGION: Region without packets +// | | | | +// | ICR | SCRAP REGION | SPARE REGION...-> +// ......->| | | | +// | FIRST-GAP | | // |<------------------- m_szSize ---------------------------->| // | |<------------ m_iMaxPosOff ----------->| | -// | | | | +// | | | | | | // +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ // | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] // +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ -// | | | | -// | | | \__last pkt received -// | | | -// | | \___ m_iDropPos -// | | -// | \___ m_iEndPos -// | -// \___ m_iStartPos: first packet position in the buffer -// -// m_pUnit[i]->status_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?) +// | | | | +// | | | \__last pkt received +// |<------------->| m_iDropOff | +// | | | +// |<--------->| m_iEndOff | +// | +// \___ m_iStartPos: first packet position in the buffer +// +// m_pUnit[i]->status: +// EntryState_Empty: No packet was ever received here +// EntryState_Avail: The packet is ready for reading +// EntryState_Read: The packet is non-order-read +// EntryState_Drop: The packet was requested to drop // // thread safety: // m_iStartPos: CUDT::m_RecvLock @@ -199,30 +236,30 @@ const int CPos_TRAP = -1; // // // m_iStartPos: the first packet that should be read (might be empty) -// m_iEndPos: the end of contiguous range. Empty if m_iEndPos == m_iStartPos -// m_iDropPos: a packet available for retrieval after a drop. If == m_iEndPos, no such packet. +// m_iEndOff: shift to the end of contiguous range. This points always to an empty cell. +// m_iDropPos: shift a packet available for retrieval after a drop. If 0, no such packet. // // Operational rules: // // Initially: // m_iStartPos = 0 -// m_iEndPos = 0 -// m_iDropPos = 0 +// m_iEndOff = 0 +// m_iDropOff = 0 // // When a packet has arrived, then depending on where it landed: // // 1. Position: next to the last read one and newest // // m_iStartPos unchanged. -// m_iEndPos shifted by 1 -// m_iDropPos = m_iEndPos +// m_iEndOff shifted by 1 +// m_iDropOff = 0 // // 2. Position: after a loss, newest. // // m_iStartPos unchanged. -// m_iEndPos unchanged. -// m_iDropPos: -// - if it was == m_iEndPos, set to this +// m_iEndOff unchanged. +// m_iDropOff: +// - set to this packet, if m_iDropOff == 0 or m_iDropOff is past this packet // - otherwise unchanged // // 3. Position: after a loss, but belated (retransmitted) -- not equal to m_iEndPos @@ -417,8 +454,7 @@ class CRcvBuffer /// InsertInfo insert(CUnit* unit); - time_point updatePosInfo(const CUnit* unit, const COff prev_max_off, const CPos newpktpos, const bool extended_end); - const CPacket* tryAvailPacketAt(CPos pos, COff& w_span); + time_point updatePosInfo(const CUnit* unit, const COff prev_max_off, const COff offset, const bool extended_end); void getAvailInfo(InsertInfo& w_if); /// Update the values of `m_iEndPos` and `m_iDropPos` in @@ -436,12 +472,7 @@ class CRcvBuffer /// update the m_iEndPos and m_iDropPos fields, or set them /// both to the end of range if there are no loss gaps. /// - /// The @a prev_max_pos parameter is passed here because it is already - /// calculated in insert(), otherwise it would have to be calculated here again. - /// - /// @param prev_max_pos buffer position represented by `m_iMaxPosOff` - /// - void updateGapInfo(CPos prev_max_pos); + void updateGapInfo(); /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). /// @param [in] seqno drop units up to this sequence number @@ -643,9 +674,9 @@ class CRcvBuffer private: //* - inline CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos(&m_szSize, (pos VALUE + inc VALUE) % m_szSize); } - inline CPos decPos(CPos pos) const { return (pos VALUE - 1) >= 0 ? CPos(&m_szSize, pos VALUE - 1) : CPos(&m_szSize, m_szSize - 1); } - inline COff offPos(CPos pos1, CPos pos2) const + CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos((pos VALUE + inc VALUE) % m_szSize); } + CPos decPos(CPos pos) const { return (pos VALUE - 1) >= 0 ? CPos(pos VALUE - 1) : CPos(m_szSize - 1); } + COff offPos(CPos pos1, CPos pos2) const { int diff = pos2 VALUE - pos1 VALUE; if (diff >= 0) @@ -655,7 +686,15 @@ class CRcvBuffer return COff(m_szSize + diff); } - inline COff posToOff(CPos pos) const { return offPos(m_iStartPos, pos); } + COff posToOff(CPos pos) const { return offPos(m_iStartPos, pos); } + + static COff decOff(COff val, int shift) + { + int ival = val VALUE - shift; + if (ival < 0) + return COff(0); + return COff(ival); + } /// @brief Compares the two positions in the receiver buffer relative to the starting position. /// @param pos2 a position in the receiver buffer. @@ -678,7 +717,7 @@ class CRcvBuffer if (iFirstNonreadPos == iStartPos) return true; - const CPos iLastPos = CPos(iStartPos.psize, (iStartPos VALUE + iMaxPosOff VALUE) % int(iSize)); + const CPos iLastPos = CPos((iStartPos VALUE + iMaxPosOff VALUE) % int(iSize)); //const CPos iLastPos = iStartPos + iMaxPosOff; const bool isOverrun = iLastPos VALUE < iStartPos VALUE; @@ -717,7 +756,9 @@ class CRcvBuffer /// Release entries following the current buffer position if they were already /// read out of order (EntryState_Read) or dropped (EntryState_Drop). - void releaseNextFillerEntries(); + /// + /// @return the range for which the start pos has been shifted + int releaseNextFillerEntries(); bool hasReadableInorderPkts() const { return (m_iFirstNonreadPos != m_iStartPos); } @@ -790,8 +831,8 @@ class CRcvBuffer CSeqNo m_iStartSeqNo; CPos m_iStartPos; // the head position for I/O (inclusive) - CPos m_iEndPos; // past-the-end of the contiguous region since m_iStartPos - CPos m_iDropPos; // points past m_iEndPos to the first deliverable after a gap, or == m_iEndPos if no such packet + COff m_iEndOff; // past-the-end of the contiguous region since m_iStartOff + COff m_iDropOff; // points past m_iEndOff to the first deliverable after a gap, or == m_iEndOff if no such packet CPos m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) COff m_iMaxPosOff; // the furthest data position int m_iNotch; // index of the first byte to read in the first ready-to-read packet (used in file/stream mode) diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 10be243a9..9436fa77d 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -646,6 +646,77 @@ TEST_F(CRcvBufferReadMsg, MsgOutOfOrderDrop) EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } +TEST_F(CRcvBufferReadMsg, MsgOrderScraps) +{ + // Ok, in this test we're filling the message this way: + // 1. We have an empty packet in the first cell. + // 2. This is followed by a 5-packet message that is valid. + // 3. This is followed by empty, valid, empty, valid, valid packet, + // where all valid packets belong to the same message. + // 4. After that there should be 3-packet valid messsage. + // 5. We deploy drop request to that second scrapped message. + // 6. We read one message. Should be the first message. + // 7. We read one message. Should be the last message. + + auto& rcv_buffer = *m_rcv_buffer.get(); + + // 1, 2 + addMessage(5,// packets + 2, // msgno + m_init_seqno + 1, + true); + + // LAYOUT: 10 11 12 13 + // [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F] + // * (2 2 2 2 2) * 3 * 3 3) (4 4 4) + + // 3 + addPacket( + m_init_seqno + 7, + 3, + false, false, // subsequent + true); + + addPacket( + m_init_seqno + 9, + 3, + false, false, // subsequent + true); + + addPacket( + m_init_seqno + 10, + 3, + false, true, // last + true); + + // 4 + addMessage(3, // packets + 4, // msgno + m_init_seqno + 11, + true); + + // 5 + EXPECT_GT(rcv_buffer.dropMessage(m_init_seqno+8, m_init_seqno+8, 3, CRcvBuffer::KEEP_EXISTING), 0); + + // 6 + array buff; + SRT_MSGCTRL mc; + pair seqrange; + EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz); + EXPECT_EQ(mc.msgno, 2); + EXPECT_EQ(seqrange, make_pair(m_init_seqno+1, m_init_seqno+5)); + + CRcvBuffer::InsertInfo ii; + rcv_buffer.getAvailInfo((ii)); + EXPECT_EQ(ii.first_seq.val(), m_init_seqno+11); + + // 7 + EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz); + EXPECT_EQ(mc.msgno, 4); + EXPECT_EQ(seqrange, make_pair(m_init_seqno+11, m_init_seqno+13)); + +} + // One message (4 packets) is added to the buffer after a message with "in order" flag. // Read in order TEST_F(CRcvBufferReadMsg, MsgOutOfOrderAfterInOrder) diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 2d6635288..1bf63ed64 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -1059,8 +1059,8 @@ void SrtCommon::OpenGroupClient() if (!extras.empty()) { Verb() << "?" << extras[0] << VerbNoEOL; - for (size_t i = 1; i < extras.size(); ++i) - Verb() << "&" << extras[i] << VerbNoEOL; + for (size_t x = 1; x < extras.size(); ++x) + Verb() << "&" << extras[x] << VerbNoEOL; } Verb(); @@ -1130,15 +1130,15 @@ void SrtCommon::OpenGroupClient() // spread the setting on all sockets. ConfigurePost(m_sock); - for (size_t i = 0; i < targets.size(); ++i) + for (size_t x = 0; x < targets.size(); ++x) { // As m_group_nodes is simply transformed into 'targets', // one index can be used to index them all. You don't // have to check if they have equal addresses because they // are equal by definition. - if (targets[i].id != -1 && targets[i].errorcode == SRT_SUCCESS) + if (targets[x].id != -1 && targets[x].errorcode == SRT_SUCCESS) { - m_group_nodes[i].socket = targets[i].id; + m_group_nodes[x].socket = targets[x].id; } } @@ -1159,12 +1159,12 @@ void SrtCommon::OpenGroupClient() } m_group_data.resize(size); - for (size_t i = 0; i < m_group_nodes.size(); ++i) + for (size_t x = 0; x < m_group_nodes.size(); ++x) { - SRTSOCKET insock = m_group_nodes[i].socket; + SRTSOCKET insock = m_group_nodes[x].socket; if (insock == -1) { - Verb() << "TARGET '" << sockaddr_any(targets[i].peeraddr).str() << "' connection failed."; + Verb() << "TARGET '" << sockaddr_any(targets[x].peeraddr).str() << "' connection failed."; continue; } @@ -1194,11 +1194,11 @@ void SrtCommon::OpenGroupClient() NULL, NULL) != -1) { Verb() << "[C]" << VerbNoEOL; - for (int i = 0; i < len1; ++i) - Verb() << " " << ready_conn[i] << VerbNoEOL; + for (int x = 0; x < len1; ++x) + Verb() << " " << ready_conn[x] << VerbNoEOL; Verb() << "[E]" << VerbNoEOL; - for (int i = 0; i < len2; ++i) - Verb() << " " << ready_err[i] << VerbNoEOL; + for (int x = 0; x < len2; ++x) + Verb() << " " << ready_err[x] << VerbNoEOL; Verb() << ""; From ff16e6871036a25622b47e92fe7867284cd7596c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 17 Jun 2024 13:52:01 +0200 Subject: [PATCH 23/28] Blocked development support types and fixed the test --- srtcore/buffer_rcv.cpp | 32 ++++++++++++++++---------------- srtcore/buffer_rcv.h | 17 ++++++++++------- srtcore/common.h | 8 ++++++++ test/test_buffer_rcv.cpp | 4 ++-- 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 1562d0264..494dc3763 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -141,7 +141,7 @@ void CRcvBuffer::debugShowState(const char* source SRT_ATR_UNUSED) << " end=+" << m_iEndOff VALUE << " drop=+" << m_iDropOff VALUE << " max-off=+" << m_iMaxPosOff VALUE - << " seq[start]=%" << m_iStartSeqNo VALUE); + << " seq[start]=%" << m_iStartSeqNo.val()); } CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) @@ -389,7 +389,7 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // position and the m_iDropOff should be calculated since that position again. void CRcvBuffer::updateGapInfo() { - COff from = m_iEndOff, to = m_iMaxPosOff; + COff from = m_iEndOff; //, to = m_iMaxPosOff; SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iMaxPosOff)].status == EntryState_Empty); CPos pos = incPos(m_iStartPos, from); @@ -536,8 +536,8 @@ int CRcvBuffer::dropAll() if (empty()) return 0; - //const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); - const int end_seqno = (m_iStartSeqNo + m_iMaxPosOff VALUE) VALUE; + const int32_t end_seqno = CSeqNo::incseq(m_iStartSeqNo.val(), m_iMaxPosOff); + //const int end_seqno = (m_iStartSeqNo + m_iMaxPosOff VALUE) VALUE; return dropUpTo(end_seqno); } @@ -556,7 +556,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro if (offset_b < 0) { LOGC(rbuflog.Debug, log << "CRcvBuffer.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " - << seqnohi << "]. Buffer start " << m_iStartSeqNo VALUE << "."); + << seqnohi << "]. Buffer start " << m_iStartSeqNo.val() << "."); return 0; } @@ -615,7 +615,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro if (end_off > m_iMaxPosOff) { HLOGC(rbuflog.Debug, log << "CRcvBuffer::dropMessage: requested to drop up to %" << seqnohi - << " with highest in the buffer %" << CSeqNo::incseq(m_iStartSeqNo VALUE, end_off) + << " with highest in the buffer %" << CSeqNo::incseq(m_iStartSeqNo.val(), end_off) << " - updating the busy region"); m_iMaxPosOff = end_off; } @@ -703,17 +703,17 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const if (m_iEndOff == 0) { // Initial contiguous region empty (including empty buffer). - HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo VALUE); - w_seq = m_iStartSeqNo VALUE; + HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo.val()); + w_seq = m_iStartSeqNo.val(); return m_iMaxPosOff > 0; } - w_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, m_iEndOff VALUE); + w_seq = CSeqNo::incseq(m_iStartSeqNo.val(), m_iEndOff VALUE); //w_seq = (m_iStartSeqNo + m_iEndOff VALUE) VALUE; HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << m_iEndOff VALUE << " maxD=" << m_iMaxPosOff VALUE - << " base=%" << m_iStartSeqNo VALUE + << " base=%" << m_iStartSeqNo.val() << " end=%" << w_seq); return (m_iEndOff < m_iMaxPosOff); @@ -1096,10 +1096,10 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const std::pair CRcvBuffer::getAvailablePacketsRange() const { const COff nonread_off = offPos(m_iStartPos, m_iFirstNonreadPos); - const int seqno_last = CSeqNo::incseq(m_iStartSeqNo VALUE, nonread_off VALUE); + const int seqno_last = CSeqNo::incseq(m_iStartSeqNo.val(), nonread_off VALUE); //const int nonread_off = (m_iFirstNonreadPos - m_iStartPos) VALUE; //const int seqno_last = (m_iStartSeqNo + nonread_off) VALUE; - return std::pair(m_iStartSeqNo VALUE, seqno_last); + return std::pair(m_iStartSeqNo.val(), seqno_last); } bool CRcvBuffer::isRcvDataReady(time_point time_now) const @@ -1539,7 +1539,7 @@ string CRcvBuffer::strFullnessState(int32_t iFirstUnackSeqNo, const time_point& { stringstream ss; - ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo VALUE + ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo.val() << " m_iStartPos=" << m_iStartPos VALUE << " m_iMaxPosOff=" << m_iMaxPosOff VALUE << ". "; ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; @@ -1632,7 +1632,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // If the offset is exactly at m_iEndOff, then // m_iDropOff will mark the end of gap. if (m_iDropOff) - *pw_end = CSeqNo::incseq(m_iStartSeqNo VALUE, m_iDropOff); + *pw_end = CSeqNo::incseq(m_iStartSeqNo.val(), m_iDropOff); else { LOGC(rbuflog.Error, log << "getFirstLossSeq: IPE: drop-off=0 while seq-off == end-off != max-off"); @@ -1653,7 +1653,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) CPos ipos ((m_iStartPos VALUE + off) % m_szSize); if (m_entries[ipos].status == EntryState_Empty) { - ret_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, off); + ret_seq = CSeqNo::incseq(m_iStartSeqNo.val(), off); loss_off = off; break; } @@ -1675,7 +1675,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) CPos ipos ((m_iStartPos VALUE + off) % m_szSize); if (m_entries[ipos].status != EntryState_Empty) { - *pw_end = CSeqNo::incseq(m_iStartSeqNo VALUE, off); + *pw_end = CSeqNo::incseq(m_iStartSeqNo.val(), off); return ret_seq; } } diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 230efa481..c843abcec 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -17,7 +17,7 @@ #include "tsbpd_time.h" #include "utilities.h" -#define USE_WRAPPERS 1 +#define USE_WRAPPERS 0 #define USE_OPERATORS 0 namespace srt @@ -25,6 +25,9 @@ namespace srt // DEVELOPMENT TOOL - TO BE MOVED ELSEWHERE (like common.h) +// NOTE: This below series of definitions for CPos and COff +// are here for development support only, but they are not in +// use in the release code - there CPos and COff are aliases to int. #if USE_WRAPPERS struct CPos { @@ -545,11 +548,11 @@ class CRcvBuffer public: /// Get the starting position of the buffer as a packet sequence number. - int getStartSeqNo() const { return m_iStartSeqNo VALUE; } + int32_t getStartSeqNo() const { return m_iStartSeqNo.val(); } /// Sets the start seqno of the buffer. /// Must be used with caution and only when the buffer is empty. - void setStartSeqNo(int seqno) { m_iStartSeqNo = CSeqNo(seqno); } + void setStartSeqNo(int32_t seqno) { m_iStartSeqNo = CSeqNo(seqno); } /// Given the sequence number of the first unacknowledged packet /// tells the size of the buffer available for packets. @@ -561,16 +564,16 @@ class CRcvBuffer // Therefore if the first packet in the buffer is ahead of the iFirstUnackSeqNo // then it does not have acknowledged packets and its full capacity is available. // Otherwise subtract the number of acknowledged but not yet read packets from its capacity. - const CSeqNo iRBufSeqNo = m_iStartSeqNo; - //if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo - if (iRBufSeqNo >= CSeqNo(iFirstUnackSeqNo)) + const int32_t iRBufSeqNo = m_iStartSeqNo.val(); + if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo + //if (iRBufSeqNo >= CSeqNo(iFirstUnackSeqNo)) { // Full capacity is available. return capacity(); } // Note: CSeqNo::seqlen(n, n) returns 1. - return capacity() - CSeqNo::seqlen(iRBufSeqNo VALUE, iFirstUnackSeqNo) + 1; + return capacity() - CSeqNo::seqlen(iRBufSeqNo, iFirstUnackSeqNo) + 1; } /// @brief Checks if the buffer has packets available for reading regardless of the TSBPD. diff --git a/srtcore/common.h b/srtcore/common.h index 1c15dd01a..e0d7212cc 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -685,14 +685,20 @@ class CSeqNo inline static int32_t incseq(int32_t seq) {return (seq == m_iMaxSeqNo) ? 0 : seq + 1;} + CSeqNo inc() const { return CSeqNo(incseq(value)); } + inline static int32_t decseq(int32_t seq) {return (seq == 0) ? m_iMaxSeqNo : seq - 1;} + CSeqNo dec() const { return CSeqNo(decseq(value)); } + inline static int32_t incseq(int32_t seq, int32_t inc) {return (m_iMaxSeqNo - seq >= inc) ? seq + inc : seq - m_iMaxSeqNo + inc - 1;} // m_iMaxSeqNo >= inc + sec --- inc + sec <= m_iMaxSeqNo // if inc + sec > m_iMaxSeqNo then return seq + inc - (m_iMaxSeqNo+1) + CSeqNo inc(int32_t i) const { return CSeqNo(incseq(value, i)); } + inline static int32_t decseq(int32_t seq, int32_t dec) { // Check if seq - dec < 0, but before it would have happened @@ -705,6 +711,8 @@ class CSeqNo return seq - dec; } + CSeqNo dec(int32_t i) const { return CSeqNo(decseq(value, i)); } + static int32_t maxseq(int32_t seq1, int32_t seq2) { if (seqcmp(seq1, seq2) < 0) diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 9436fa77d..155dd7bdd 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -702,7 +702,7 @@ TEST_F(CRcvBufferReadMsg, MsgOrderScraps) array buff; SRT_MSGCTRL mc; pair seqrange; - EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz); + EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz*5); EXPECT_EQ(mc.msgno, 2); EXPECT_EQ(seqrange, make_pair(m_init_seqno+1, m_init_seqno+5)); @@ -711,7 +711,7 @@ TEST_F(CRcvBufferReadMsg, MsgOrderScraps) EXPECT_EQ(ii.first_seq.val(), m_init_seqno+11); // 7 - EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz); + EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz*3); EXPECT_EQ(mc.msgno, 4); EXPECT_EQ(seqrange, make_pair(m_init_seqno+11, m_init_seqno+13)); From fa70fdaf0ee32ee8ae42a9c48d4c8cbe2c2afc24 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 18 Jun 2024 12:55:09 +0200 Subject: [PATCH 24/28] Merged changes --- srtcore/buffer_rcv.cpp | 15 +++++++++++++++ srtcore/buffer_rcv.h | 12 ++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index a323360bb..d790345a6 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -435,6 +435,20 @@ void CRcvBuffer::updateGapInfo() m_iDropOff = 0; if (m_iEndOff < m_iMaxPosOff) { + CPos start = incPos(m_iStartPos, m_iEndOff + 1), + end = incPos(m_iStartPos, m_iEndOff); + + for (CPos i = start; i != end; i = incPos(i)) + { + if (m_entries[i].status == EntryState_Avail) + { + m_iDropOff = offPos(m_iStartPos, i); + break; + } + } + + /* OPTIMIZED, but buggy. + int maxend = m_szSize - m_iStartPos VALUE; int ifrom = m_iEndOff + 1; int ito = m_iMaxPosOff VALUE; @@ -463,6 +477,7 @@ void CRcvBuffer::updateGapInfo() } } } + */ // Must be found somewhere, worst case at the position // of m_iMaxPosOff-1. If no finding loop caught it somehow, diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 7ad000b69..325245194 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -208,14 +208,14 @@ const int CPos_TRAP = -1; // NOTE: in scrap region, the first cell is empty and the last one filled. // SPARE REGION: Region without packets // -// | BUSY REGION | -// | | | | -// | ICR | SCRAP REGION | SPARE REGION...-> -// ......->| | | | -// | FIRST-GAP | | +// | BUSY REGION | +// | | | | +// | ICR | SCRAP REGION | SPARE REGION...-> +// ......->| | | | +// | /FIRST-GAP | | // |<------------------- m_szSize ---------------------------->| // | |<------------ m_iMaxPosOff ----------->| | -// | | | | | | +// | | | | | | // +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ // | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] // +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ From 1bf93ff4afc1843c8716d159ebe1b4f8a0cbe7a4 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 18 Jun 2024 15:33:09 +0200 Subject: [PATCH 25/28] Cleanup of the commented-out code --- srtcore/buffer_rcv.cpp | 56 +++++------------------------------------- 1 file changed, 6 insertions(+), 50 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index d790345a6..9e19884b7 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -148,7 +148,6 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) { SRT_ASSERT(unit != NULL); const int32_t seqno = unit->m_Packet.getSeqNo(); - //const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno); const COff offset = COff(CSeqNo(seqno) - m_iStartSeqNo); IF_RCVBUF_DEBUG(ScopedLog scoped_log); @@ -387,7 +386,7 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // position and the m_iDropOff should be calculated since that position again. void CRcvBuffer::updateGapInfo() { - COff from = m_iEndOff; //, to = m_iMaxPosOff; + COff from = m_iEndOff; SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iMaxPosOff)].status == EntryState_Empty); CPos pos = incPos(m_iStartPos, from); @@ -407,7 +406,7 @@ void CRcvBuffer::updateGapInfo() // XXX This should be this way, but there are still inconsistencies // in the message code. - //SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status == EntryState_Empty); + //USE: SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status == EntryState_Empty); SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status != EntryState_Avail); // XXX Controversy: m_iDropOff is only used in case when SRTO_TLPKTDROP @@ -447,38 +446,6 @@ void CRcvBuffer::updateGapInfo() } } - /* OPTIMIZED, but buggy. - - int maxend = m_szSize - m_iStartPos VALUE; - int ifrom = m_iEndOff + 1; - int ito = m_iMaxPosOff VALUE; - - bool found = false; - for (int i = ifrom; i < std::min(maxend, ito); ++i) - { - if (m_entries[CPos(i)].status == EntryState_Avail) - { - m_iDropOff = i; - found = true; - break; - } - } - - if (!found && ito > maxend) - { - int upto = ito - maxend; - for (int i = 0; i < upto; ++i) - { - if (m_entries[CPos(i)].status == EntryState_Avail) - { - m_iDropOff = i; - found = true; - break; - } - } - } - */ - // Must be found somewhere, worst case at the position // of m_iMaxPosOff-1. If no finding loop caught it somehow, // it will remain at 0. The case when you have empty packets @@ -566,8 +533,6 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro << m_iStartSeqNo); // Drop by packet seqno range to also wipe those packets that do not exist in the buffer. - //const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); - //const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi); const int offset_a = CSeqNo(seqnolo) - m_iStartSeqNo; const int offset_b = CSeqNo(seqnohi) - m_iStartSeqNo; if (offset_b < 0) @@ -624,7 +589,6 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro m_entries[i].status = EntryState_Drop; if (minDroppedOffset == -1) minDroppedOffset = offPos(m_iStartPos, i); - //minDroppedOffset = i - m_iStartPos; } if (end_off > m_iMaxPosOff) @@ -812,7 +776,6 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= 2, then probably there is a long gap, and buffer needs to be reset. - SRT_ASSERT((m_iStartPos VALUE + offset VALUE) / m_szSize < 2); + SRT_ASSERT((m_iStartPos + offset) / m_szSize < 2); - //const CPos newpktpos = m_iStartPos + offset; const CPos newpktpos = incPos(m_iStartPos, offset); const COff prev_max_off = m_iMaxPosOff; bool extended_end = false; @@ -193,7 +192,7 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // possible even before checking that the packet // exists because existence of a packet beyond // the current max position is not possible). - SRT_ASSERT(newpktpos VALUE >= 0 && newpktpos VALUE < int(m_szSize)); + SRT_ASSERT(newpktpos >= 0 && newpktpos < int(m_szSize)); if (m_entries[newpktpos].status != EntryState_Empty) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -1"); @@ -466,7 +465,6 @@ std::pair CRcvBuffer::dropUpTo(int32_t seqno) IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo); COff len = COff(CSeqNo(seqno) - m_iStartSeqNo); - //int len = CSeqNo::seqoff(m_iStartSeqNo, seqno); if (len <= 0) { IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); @@ -479,7 +477,7 @@ std::pair CRcvBuffer::dropUpTo(int32_t seqno) int iNumDropped = 0; // Number of dropped packets that were missing. int iNumDiscarded = 0; // The number of dropped packets that existed in the buffer. - while (len VALUE > 0) + while (len > 0) { // Note! Dropping a EntryState_Read must not be counted as a drop because it was read. // Note! Dropping a EntryState_Drop must not be counted as a drop because it was already dropped and counted earlier. @@ -685,10 +683,10 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const return m_iMaxPosOff > 0; } - w_seq = CSeqNo::incseq(m_iStartSeqNo.val(), m_iEndOff VALUE); + w_seq = CSeqNo::incseq(m_iStartSeqNo.val(), m_iEndOff); - HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << m_iEndOff VALUE - << " maxD=" << m_iMaxPosOff VALUE + HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << m_iEndOff + << " maxD=" << m_iMaxPosOff << " base=%" << m_iStartSeqNo.val() << " end=%" << w_seq); @@ -917,7 +915,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) m_iStartPos = p; --m_iMaxPosOff; - SRT_ASSERT(m_iMaxPosOff VALUE >= 0); + SRT_ASSERT(m_iMaxPosOff >= 0); m_iEndOff = decOff(m_iEndOff, 1); m_iDropOff = decOff(m_iDropOff, 1); @@ -943,8 +941,8 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) if (iBytesRead == 0) { - LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos VALUE - << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos VALUE); + LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos + << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos); } IF_HEAVY_LOGGING(debugShowState("readbuf")); @@ -968,7 +966,7 @@ bool CRcvBuffer::hasAvailablePackets() const int CRcvBuffer::getRcvDataSize() const { - return offPos(m_iStartPos, m_iFirstNonreadPos) VALUE; + return offPos(m_iStartPos, m_iFirstNonreadPos); } int CRcvBuffer::getTimespan_ms() const @@ -1057,7 +1055,7 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const std::pair CRcvBuffer::getAvailablePacketsRange() const { const COff nonread_off = offPos(m_iStartPos, m_iFirstNonreadPos); - const CSeqNo seqno_last = m_iStartSeqNo + nonread_off VALUE; + const CSeqNo seqno_last = m_iStartSeqNo + nonread_off; return std::pair(m_iStartSeqNo.val(), seqno_last.val()); } @@ -1488,7 +1486,7 @@ string CRcvBuffer::strFullnessState(int32_t iFirstUnackSeqNo, const time_point& stringstream ss; ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo.val() - << " m_iStartPos=" << m_iStartPos VALUE << " m_iMaxPosOff=" << m_iMaxPosOff VALUE << ". "; + << " m_iStartPos=" << m_iStartPos << " m_iMaxPosOff=" << m_iMaxPosOff << ". "; ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; @@ -1555,17 +1553,15 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) if (m_iEndOff == m_iMaxPosOff) return SRT_SEQNO_NONE; - //int offset = CSeqNo::seqoff(m_iStartSeqNo, fromseq); - int offset_val = CSeqNo(fromseq) - m_iStartSeqNo; - COff offset (offset_val); + COff offset = COff(CSeqNo(fromseq) - m_iStartSeqNo); // Check if it's still inside the buffer. // Skip the region from 0 to m_iEndOff because this // region is by definition contiguous and contains no loss. - if (offset_val < m_iEndOff || offset >= m_iMaxPosOff) + if (offset < m_iEndOff || offset >= m_iMaxPosOff) { - HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset VALUE << " for %" << fromseq - << " (with max=" << m_iMaxPosOff VALUE << ") - NO LOSS FOUND"); + HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset << " for %" << fromseq + << " (with max=" << m_iMaxPosOff << ") - NO LOSS FOUND"); return SRT_SEQNO_NONE; } @@ -1597,7 +1593,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // find it earlier by checking packet presence. for (int off = offset; off < m_iMaxPosOff; ++off) { - CPos ipos ((m_iStartPos VALUE + off) % m_szSize); + CPos ipos ((m_iStartPos + off) % m_szSize); if (m_entries[ipos].status == EntryState_Empty) { ret_seq = CSeqNo::incseq(m_iStartSeqNo.val(), off); @@ -1619,7 +1615,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) { for (int off = loss_off+1; off < m_iMaxPosOff; ++off) { - CPos ipos ((m_iStartPos VALUE + off) % m_szSize); + CPos ipos ((m_iStartPos + off) % m_szSize); if (m_entries[ipos].status != EntryState_Empty) { *pw_end = CSeqNo::incseq(m_iStartSeqNo.val(), off); diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 325245194..ec5f2c256 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -148,8 +148,6 @@ struct COff operator bool() const { return value != 0; } }; -#define VALUE .val() - #if USE_OPERATORS inline CPos operator+(const CPos& pos, COff off) @@ -194,7 +192,6 @@ inline CSeqNo operator-(CSeqNo seq, COff off) const CPos CPos_TRAP (-1); #else -#define VALUE typedef int CPos; typedef int COff; const int CPos_TRAP = -1; @@ -679,11 +676,11 @@ class CRcvBuffer private: //* - CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos((pos VALUE + inc VALUE) % m_szSize); } - CPos decPos(CPos pos) const { return (pos VALUE - 1) >= 0 ? CPos(pos VALUE - 1) : CPos(m_szSize - 1); } + CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos((pos + inc) % m_szSize); } + CPos decPos(CPos pos) const { return (pos - 1) >= 0 ? CPos(pos - 1) : CPos(m_szSize - 1); } COff offPos(CPos pos1, CPos pos2) const { - int diff = pos2 VALUE - pos1 VALUE; + int diff = pos2 - pos1; if (diff >= 0) { return COff(diff); @@ -695,7 +692,7 @@ class CRcvBuffer static COff decOff(COff val, int shift) { - int ival = val VALUE - shift; + int ival = val - shift; if (ival < 0) return COff(0); return COff(ival); @@ -722,14 +719,13 @@ class CRcvBuffer if (iFirstNonreadPos == iStartPos) return true; - const CPos iLastPos = CPos((iStartPos VALUE + iMaxPosOff VALUE) % int(iSize)); - //const CPos iLastPos = iStartPos + iMaxPosOff; - const bool isOverrun = iLastPos VALUE < iStartPos VALUE; + const CPos iLastPos = CPos((iStartPos + iMaxPosOff) % int(iSize)); + const bool isOverrun = iLastPos < iStartPos; if (isOverrun) - return iFirstNonreadPos VALUE > iStartPos VALUE || iFirstNonreadPos VALUE <= iLastPos VALUE; + return iFirstNonreadPos > iStartPos || iFirstNonreadPos <= iLastPos; - return iFirstNonreadPos VALUE > iStartPos VALUE && iFirstNonreadPos VALUE <= iLastPos VALUE; + return iFirstNonreadPos > iStartPos && iFirstNonreadPos <= iLastPos; } bool isInUsedRange(CPos iFirstNonreadPos) @@ -738,11 +734,11 @@ class CRcvBuffer return true; // DECODE the iFirstNonreadPos - int diff = iFirstNonreadPos VALUE - m_iStartPos VALUE; + int diff = iFirstNonreadPos - m_iStartPos; if (diff < 0) diff += m_szSize; - return diff <= m_iMaxPosOff VALUE; + return diff <= m_iMaxPosOff; } // NOTE: Assumes that pUnit != NULL From cb00ccead13e7d5f884ff229a6c637cadc0464db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 3 Sep 2024 17:57:10 +0200 Subject: [PATCH 27/28] Added option to disable test discovery (off by default) --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b135fc72..215559326 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -162,6 +162,7 @@ option(ENABLE_PKTINFO "Enable using IP_PKTINFO to allow the listener extracting option(ENABLE_RELATIVE_LIBPATH "Should application contain relative library paths, like ../lib" OFF) option(ENABLE_GETNAMEINFO "In-logs sockaddr-to-string should do rev-dns" OFF) option(ENABLE_UNITTESTS "Enable unit tests" OFF) +option(DISABLE_UNITTESTS_DISCOVERY "Do not discover unit tests when enabled" OFF) option(ENABLE_ENCRYPTION "Enable encryption in SRT" ON) option(ENABLE_AEAD_API_PREVIEW "Enable AEAD API preview in SRT" Off) option(ENABLE_MAXREXMITBW "Enable SRTO_MAXREXMITBW (v1.6.0 API preview)" Off) @@ -1527,7 +1528,9 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) #set_tests_properties(test-srt PROPERTIES RUN_SERIAL TRUE) else() set_tests_properties(${tests_srt} PROPERTIES RUN_SERIAL TRUE) - #gtest_discover_tests(test-srt) + if (NOT DISABLE_UNITTESTS_DISCOVERY) + gtest_discover_tests(test-srt) + endif() endif() enable_testing() From 5bc71e9506423760adb653f0b686eaf1d2452bfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 4 Sep 2024 10:28:04 +0200 Subject: [PATCH 28/28] BUGFIX: the buffer info extraction function wasn't mutex-protected --- srtcore/core.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 802521d8a..b0ab9376c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8031,7 +8031,10 @@ bool srt::CUDT::getFirstNoncontSequence(int32_t& w_seq, string& w_log_reason) } if (m_config.bTSBPD || !m_config.bMessageAPI) { - // The getFirstNonreadSeqNo() function retuens the sequence number of the first packet + // NOTE: it's not only about protecting the buffer itself, it's also protecting + // the section where the m_iRcvCurrSeqNo is updated. + ScopedLock buflock (m_RcvBufferLock); + // The getFirstNonreadSeqNo() function returns the sequence number of the first packet // that cannot be read. In cases when a message can consist of several data packets, // an existing packet of partially available message also cannot be read. // If TSBPD mode is enabled, a message must consist of a single data packet only.