From 1bfab3ded36aa7cceac8d457e2854f350b100cc2 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 23 Jan 2024 16:40:36 +0100 Subject: [PATCH 01/51] [core] Fixing some lock-order-inversion and data race problems (TSAN reports) (#1824). --- srtcore/api.cpp | 42 ++++++++++++++++++++++++++++-------------- srtcore/buffer_snd.cpp | 8 ++++---- srtcore/buffer_snd.h | 6 +++++- srtcore/core.cpp | 33 +++++++++++++++++++++------------ srtcore/core.h | 4 ++-- srtcore/queue.cpp | 1 + 6 files changed, 61 insertions(+), 33 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index a0e59ded9..7be0d1d95 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -148,8 +148,7 @@ void srt::CUDTSocket::setBrokenClosed() bool srt::CUDTSocket::readReady() { - // TODO: Use m_RcvBufferLock here (CUDT::isRcvReadReady())? - if (m_UDT.m_bConnected && m_UDT.m_pRcvBuffer->isRcvDataReady()) + if (m_UDT.m_bConnected && m_UDT.isRcvBufferReady()) return true; if (m_UDT.m_bListening) @@ -2611,20 +2610,23 @@ void srt::CUDTUnited::checkBrokenSockets() if (elapsed < milliseconds_from(CUDT::COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS)) continue; } - else if ((s->core().m_pRcvBuffer != NULL) - // FIXED: calling isRcvDataAvailable() just to get the information - // whether there are any data waiting in the buffer, - // NOT WHETHER THEY ARE ALSO READY TO PLAY at the time when - // this function is called (isRcvDataReady also checks if the - // available data is "ready to play"). - && s->core().m_pRcvBuffer->hasAvailablePackets()) + else { - const int bc = s->core().m_iBrokenCounter.load(); - if (bc > 0) + CUDT& u = s->core(); + + enterCS(u.m_RcvBufferLock); + bool has_avail_packets = u.m_pRcvBuffer && u.m_pRcvBuffer->hasAvailablePackets(); + leaveCS(u.m_RcvBufferLock); + + if (has_avail_packets) { - // if there is still data in the receiver buffer, wait longer - s->core().m_iBrokenCounter.store(bc - 1); - continue; + const int bc = u.m_iBrokenCounter.load(); + if (bc > 0) + { + // if there is still data in the receiver buffer, wait longer + s->core().m_iBrokenCounter.store(bc - 1); + continue; + } } } @@ -2782,8 +2784,20 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) // delete this one m_ClosedSockets.erase(i); + // XXX This below section can unlock m_GlobControlLock + // just for calling CUDT::closeInternal(), which is needed + // to avoid locking m_ConnectionLock after m_GlobControlLock, + // while m_ConnectionLock orders BEFORE m_GlobControlLock. + // This should be perfectly safe thing to do after the socket + // ID has been erased from m_ClosedSockets. No container access + // is done in this case. + // + // Report: P04-1.28, P04-2.27, P04-2.50, P04-2.55 + HLOGC(smlog.Debug, log << "GC/removeSocket: closing associated UDT @" << u); + leaveCS(m_GlobControlLock); s->core().closeInternal(); + enterCS(m_GlobControlLock); HLOGC(smlog.Debug, log << "GC/removeSocket: DELETING SOCKET @" << u); delete s; HLOGC(smlog.Debug, log << "GC/removeSocket: socket @" << u << " DELETED. Checking muxer."); diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index eb16e3341..4edfe80c0 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -219,7 +219,7 @@ void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl) } m_pLastBlock = s; - m_iCount += iNumBlocks; + m_iCount = m_iCount + iNumBlocks; m_iBytesCount += len; m_rateEstimator.updateInputRate(m_tsLastOriginTime, iNumBlocks, len); @@ -293,7 +293,7 @@ int CSndBuffer::addBufferFromFile(fstream& ifs, int len) m_pLastBlock = s; enterCS(m_BufLock); - m_iCount += iNumBlocks; + m_iCount = m_iCount + iNumBlocks; m_iBytesCount += total; leaveCS(m_BufLock); @@ -547,7 +547,7 @@ void CSndBuffer::ackData(int offset) if (move) m_pCurrBlock = m_pFirstBlock; - m_iCount -= offset; + m_iCount = m_iCount - offset; updAvgBufSize(steady_clock::now()); } @@ -653,7 +653,7 @@ int CSndBuffer::dropLateData(int& w_bytes, int32_t& w_first_msgno, const steady_ { m_pCurrBlock = m_pFirstBlock; } - m_iCount -= dpkts; + m_iCount = m_iCount - dpkts; m_iBytesCount -= dbytes; w_bytes = dbytes; diff --git a/srtcore/buffer_snd.h b/srtcore/buffer_snd.h index 5da8c9631..1b1aa6222 100644 --- a/srtcore/buffer_snd.h +++ b/srtcore/buffer_snd.h @@ -241,7 +241,11 @@ class CSndBuffer int m_iSize; // buffer size (number of packets) const int m_iBlockLen; // maximum length of a block holding packet payload and AUTH tag (excluding packet header). const int m_iAuthTagSize; // Authentication tag size (if GCM is enabled). - int m_iCount; // number of used blocks + + // NOTE: This is atomic AND under lock because the function getCurrBufSize() + // is returning it WITHOUT locking. Modification, however, must stay under + // a lock. + sync::atomic m_iCount; // number of used blocks int m_iBytesCount; // number of payload bytes in queue time_point m_tsLastOriginTime; diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 0f3a19f6d..cd22ebece 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5708,6 +5708,14 @@ void srt::CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs) void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& w_hs) { HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: setting up data according to handshake"); +#if ENABLE_BONDING + // Keep the group alive for the lifetime of this function, + // and do it BEFORE acquiring m_ConnectionLock to avoid + // lock inversion. + // This will check if a socket belongs to a group and if so + // it will remember this group and keep it alive here. + CUDTUnited::GroupKeeper group_keeper(uglobal(), m_parent); +#endif ScopedLock cg(m_ConnectionLock); @@ -5814,8 +5822,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& { #if ENABLE_BONDING - ScopedLock cl (uglobal().m_GlobControlLock); - CUDTGroup* g = m_parent->m_GroupOf; + CUDTGroup* g = group_keeper.group; if (g) { // This is the last moment when this can be done. @@ -5981,7 +5988,7 @@ SRT_REJECT_REASON srt::CUDT::setupCC() // Create the CCC object and configure it. // UDT also sets back the congestion window: ??? - // m_dCongestionWindow = m_pCC->m_dCWndSize; + // m_iCongestionWindow = m_pCC->m_dCWndSize; // XXX Not sure about that. May happen that AGENT wants // tsbpd mode, but PEER doesn't, even in bidirectional mode. @@ -6155,8 +6162,9 @@ void srt::CUDT::addressAndSend(CPacket& w_pkt) m_pSndQueue->sendto(m_PeerAddr, w_pkt, m_SourceAddr); } -// [[using maybe_locked(m_GlobControlLock, if called from GC)]] -bool srt::CUDT::closeInternal() +// [[using maybe_locked(m_GlobControlLock, if called from breakSocket_LOCKED, usually from GC)]] +// [[using maybe_locked(m_parent->m_ControlLock, if called from srt_close())]] +bool srt::CUDT::closeInternal() ATR_NOEXCEPT { // NOTE: this function is called from within the garbage collector thread. @@ -7509,7 +7517,7 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->mbpsRecvRate = double(perf->byteRecv) * 8.0 / interval; perf->usPktSndPeriod = (double) count_microseconds(m_tdSendInterval.load()); perf->pktFlowWindow = m_iFlowWindowSize.load(); - perf->pktCongestionWindow = (int)m_dCongestionWindow; + perf->pktCongestionWindow = m_iCongestionWindow; perf->pktFlightSize = flight_span; perf->msRTT = (double)m_iSRTT / 1000.0; perf->msSndTsbPdDelay = m_bPeerTsbPd ? m_iPeerTsbPdDelay_ms : 0; @@ -7698,12 +7706,13 @@ bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) // - m_dPktSndPeriod // - m_dCWndSize m_tdSendInterval = microseconds_from((int64_t)m_CongCtl->pktSndPeriod_us()); - m_dCongestionWindow = m_CongCtl->cgWindowSize(); + const double cgwindow = m_CongCtl->cgWindowSize(); + m_iCongestionWindow = cgwindow; #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=" - << std::setprecision(3) << m_dCongestionWindow); + << std::setprecision(3) << cgwindow); #endif } @@ -8446,7 +8455,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ if (CSeqNo::seqcmp(ackdata_seqno, m_iSndLastAck) >= 0) { - const int cwnd1 = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow)); + const int cwnd1 = std::min(m_iFlowWindowSize, m_iCongestionWindow); const bool bWasStuck = cwnd1<= getFlightSpan(); // Update Flow Window Size, must update before and together with m_iSndLastAck m_iFlowWindowSize = ackdata[ACKD_BUFFERLEFT]; @@ -8454,7 +8463,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ m_tsLastRspAckTime = currtime; m_iReXmitCount = 1; // Reset re-transmit count since last ACK - const int cwnd = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow)); + const int cwnd = std::min(m_iFlowWindowSize, m_iCongestionWindow); if (bWasStuck && cwnd > getFlightSpan()) { m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); @@ -9709,12 +9718,12 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) { ScopedLock lkrack (m_RecvAckLock); // Check the congestion/flow window limit - const int cwnd = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow)); + const int cwnd = std::min(m_iFlowWindowSize, m_iCongestionWindow); const int flightspan = getFlightSpan(); if (cwnd <= flightspan) { HLOGC(qslog.Debug, - log << CONID() << "packUniqueData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_dCongestionWindow + log << CONID() << "packUniqueData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_iCongestionWindow << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo << ")=" << flightspan); return false; } diff --git a/srtcore/core.h b/srtcore/core.h index 5b4b27aaf..9d5fa4c3a 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -596,7 +596,7 @@ class CUDT /// Close the opened UDT entity. - bool closeInternal(); + bool closeInternal() ATR_NOEXCEPT; void updateBrokenConnection(); void completeBrokenConnectionDependencies(int errorcode); @@ -848,7 +848,7 @@ class CUDT SRT_ATTR_GUARDED_BY(m_RecvAckLock) sync::atomic m_iFlowWindowSize; // Flow control window size - double m_dCongestionWindow; // Congestion window size + sync::atomic m_iCongestionWindow; // Congestion window size private: // Timers atomic_time_point m_tsNextACKTime; // Next ACK time, in CPU clock cycles, same below diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 863148b34..cf0016901 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1747,6 +1747,7 @@ void srt::CRcvQueue::setNewEntry(CUDT* u) bool srt::CRcvQueue::ifNewEntry() { + ScopedLock listguard(m_IDLock); return !(m_vNewEntry.empty()); } From 6e0ab4e520a43518a8c3032217483bf8c78f2c72 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 24 Jan 2024 12:46:03 +0100 Subject: [PATCH 02/51] [core] Refax of the CSndBuffer::readData making the call form clearer (#2593). --- srtcore/buffer_snd.cpp | 50 +++++++++++++++++++++++++++--------------- srtcore/buffer_snd.h | 29 +++++++++++++++++++----- srtcore/core.cpp | 27 +++++++++++------------ 3 files changed, 69 insertions(+), 37 deletions(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index 4edfe80c0..3aa81042e 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -422,9 +422,10 @@ int32_t CSndBuffer::getMsgNoAt(const int offset) return p->getMsgSeq(); } -int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen) +int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, DropRange& w_drop) { - int32_t& msgno_bitset = w_packet.m_iMsgNo; + // NOTE: w_packet.m_iSeqNo is expected to be set to the value + // of the sequence number with which this packet should be sent. ScopedLock bufferguard(m_BufLock); @@ -439,19 +440,23 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time if (p == m_pLastBlock) { LOGC(qslog.Error, log << "CSndBuffer::readData: offset " << offset << " too large!"); - return 0; + return READ_NONE; } #if ENABLE_HEAVY_LOGGING const int32_t first_seq = p->m_iSeqNo; int32_t last_seq = p->m_iSeqNo; #endif + // This is rexmit request, so the packet should have the sequence number + // already set when it was once sent uniquely. + SRT_ASSERT(p->m_iSeqNo == w_packet.m_iSeqNo); + // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale. // If so, then inform the caller that it should first take care of the whole // message (all blocks with that message id). Shift the m_pCurrBlock pointer // to the position past the last of them. Then return -1 and set the - // msgno_bitset return reference to the message id that should be dropped as + // msgno bitset packet field to the message id that should be dropped as // a whole. // After taking care of that, the caller should immediately call this function again, @@ -463,11 +468,11 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - p->m_tsOriginTime) > p->m_iTTL)) { - int32_t msgno = p->getMsgSeq(); - w_msglen = 1; - p = p->m_pNext; - bool move = false; - while (p != m_pLastBlock && msgno == p->getMsgSeq()) + w_drop.msgno = p->getMsgSeq(); + int msglen = 1; + p = p->m_pNext; + bool move = false; + while (p != m_pLastBlock && w_drop.msgno == p->getMsgSeq()) { #if ENABLE_HEAVY_LOGGING last_seq = p->m_iSeqNo; @@ -477,18 +482,27 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time p = p->m_pNext; if (move) m_pCurrBlock = p; - w_msglen++; + msglen++; } HLOGC(qslog.Debug, - log << "CSndBuffer::readData: due to TTL exceeded, SEQ " << first_seq << " - " << last_seq << ", " - << w_msglen << " packets to drop, msgno=" << msgno); - - // If readData returns -1, then msgno_bitset is understood as a Message ID to drop. - // This means that in this case it should be written by the message sequence value only - // (not the whole 4-byte bitset written at PH_MSGNO). - msgno_bitset = msgno; - return -1; + log << "CSndBuffer::readData: due to TTL exceeded, %(" << first_seq << " - " << last_seq << "), " + << msglen << " packets to drop with #" << w_drop.msgno); + + // Theoretically as the seq numbers are being tracked, you should be able + // to simply take the sequence number from the block. But this is a new + // feature and should be only used after refax for the sender buffer to + // make it manage the sequence numbers inside, instead of by CUDT::m_iSndLastDataAck. + w_drop.seqno[DropRange::BEGIN] = w_packet.m_iSeqNo; + w_drop.seqno[DropRange::END] = CSeqNo::incseq(w_packet.m_iSeqNo, msglen - 1); + + // Note the rules: here `p` is pointing to the first block AFTER the + // message to be dropped, so the end sequence should be one behind + // the one for p. Note that the loop rolls until hitting the first + // packet that doesn't belong to the message or m_pLastBlock, which + // is past-the-end for the occupied range in the sender buffer. + SRT_ASSERT(w_drop.seqno[DropRange::END] == CSeqNo::decseq(p->m_iSeqNo)); + return READ_DROP; } w_packet.m_pcData = p->m_pcData; diff --git a/srtcore/buffer_snd.h b/srtcore/buffer_snd.h index 1b1aa6222..afd52110b 100644 --- a/srtcore/buffer_snd.h +++ b/srtcore/buffer_snd.h @@ -116,6 +116,10 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) int addBufferFromFile(std::fstream& ifs, int len); + // Special values that can be returned by readData. + static const int READ_NONE = 0; + static const int READ_DROP = -1; + /// Find data position to pack a DATA packet from the furthest reading point. /// @param [out] packet the packet to read. /// @param [out] origintime origin time stamp of the message @@ -130,14 +134,29 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) time_point peekNextOriginal() const; + struct DropRange + { + static const size_t BEGIN = 0, END = 1; + int32_t seqno[2]; + int32_t msgno; + }; /// Find data position to pack a DATA packet for a retransmission. + /// IMPORTANT: @a packet is [in,out] because it is expected to get set + /// the sequence number of the packet expected to be sent next. The sender + /// buffer normally doesn't handle sequence numbers and the consistency + /// between the sequence number of a packet already sent and kept in the + /// buffer is achieved by having the sequence number recorded in the + /// CUDT::m_iSndLastDataAck field that should represent the oldest packet + /// still in the buffer. /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// @param [out] packet the packet to read. - /// @param [out] origintime origin time stamp of the message - /// @param [out] msglen length of the message - /// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded). + /// @param [in,out] w_packet storage for the packet, preinitialized with sequence number + /// @param [out] w_origintime origin time stamp of the message + /// @param [out] w_drop the drop information in case when dropping is to be done instead + /// @retval >0 Length of the data read. + /// @retval READ_NONE No data available or @a offset points out of the buffer occupied space. + /// @retval READ_DROP The call requested data drop due to TTL exceeded, to be handled first. SRT_ATTR_EXCLUDES(m_BufLock) - int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); + int readData(const int offset, CPacket& w_packet, time_point& w_origintime, DropRange& w_drop); /// Get the time of the last retransmission (if any) of the DATA packet. /// @param [in] offset offset from the last ACK point (backward sequence number difference) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index cd22ebece..dcb264993 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9335,28 +9335,27 @@ int srt::CUDT::packLostData(CPacket& w_packet) } } - int msglen; + typedef CSndBuffer::DropRange DropRange; + + DropRange buffer_drop; steady_clock::time_point tsOrigin; - const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (msglen)); - if (payload == -1) + const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (buffer_drop)); + if (payload == CSndBuffer::READ_DROP) { - int32_t seqpair[2]; - seqpair[0] = w_packet.m_iSeqNo; - SRT_ASSERT(msglen >= 1); - seqpair[1] = CSeqNo::incseq(seqpair[0], msglen - 1); + SRT_ASSERT(CSeqNo::seqoff(buffer_drop.seqno[DropRange::BEGIN], buffer_drop.seqno[DropRange::END]) >= 0); HLOGC(qrlog.Debug, - log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: msgno=" - << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " msglen=" << msglen << " SEQ:" << seqpair[0] << " - " - << seqpair[1]); - sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); + log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: #" + << buffer_drop.msgno << " %(" << buffer_drop.seqno[DropRange::BEGIN] << " - " + << buffer_drop.seqno[DropRange::END] << ")"); + sendCtrl(UMSG_DROPREQ, &buffer_drop.msgno, buffer_drop.seqno, sizeof(buffer_drop.seqno)); // skip all dropped packets - m_pSndLossList->removeUpTo(seqpair[1]); - m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, seqpair[1]); + m_pSndLossList->removeUpTo(buffer_drop.seqno[DropRange::END]); + m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, buffer_drop.seqno[DropRange::END]); continue; } - else if (payload == 0) + else if (payload == CSndBuffer::READ_NONE) continue; // The packet has been ecrypted, thus the authentication tag is expected to be stored From 3dba3f441890b430380fba25848e8ff4a4db4bf3 Mon Sep 17 00:00:00 2001 From: yomnes0 <127947185+yomnes0@users.noreply.github.com> Date: Thu, 25 Jan 2024 15:56:57 +0100 Subject: [PATCH 03/51] [core] Removed the possibility to use optlen=-1 in srt_setsockopt (#2849). It was used to auto-determine the length of a null-terminated string for the SRTO_BINDTODEVICE socket option. --- srtcore/api.cpp | 2 +- srtcore/socketconfig.cpp | 11 +++-------- test/test_socket_options.cpp | 23 +++++++++++++++++++++++ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 7be0d1d95..554bed9fd 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -3807,7 +3807,7 @@ int srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval int srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, int optlen) { - if (!optval) + if (!optval || optlen < 0) return APIError(MJ_NOTSUP, MN_INVAL, 0); try diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 8708e90a1..d44330f78 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -292,10 +292,8 @@ struct CSrtConfigSetter using namespace std; string val; - if (optlen == -1) - val = (const char *)optval; - else - val.assign((const char *)optval, optlen); + + val.assign((const char *)optval, optlen); if (val.size() >= IFNAMSIZ) { LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE: device name too long (max: IFNAMSIZ=" << IFNAMSIZ << ")"); @@ -597,10 +595,7 @@ struct CSrtConfigSetter static void set(CSrtConfig& co, const void* optval, int optlen) { std::string val; - if (optlen == -1) - val = (const char*)optval; - else - val.assign((const char*)optval, optlen); + val.assign((const char*)optval, optlen); // Translate alias if (val == "vod") diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index b7acda37a..78388bf30 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -850,6 +850,29 @@ TEST_F(TestSocketOptions, StreamIDWrongLen) EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); } +//Check if setting -1 as optlen returns an error +TEST_F(TestSocketOptions, StringOptLenInvalid) +{ + const string test_string = "test1234567"; + const string srto_congestion_string ="live"; + const string fec_config = "fec,cols:10,rows:10"; + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_STREAMID, test_string.c_str(), -1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_BINDTODEVICE, test_string.c_str(), -1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_CONGESTION, srto_congestion_string.c_str(), -1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_PACKETFILTER, fec_config.c_str(), -1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_PASSPHRASE, test_string.c_str(), -1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); +} + // Try to set/get a 13-character string in SRTO_STREAMID. // This tests checks that the StreamID is set to the correct size // while it is transmitted as 16 characters in the Stream ID HS extension. From 4c443d63a9e2236a69d260cb5bc5a68f87bb20d0 Mon Sep 17 00:00:00 2001 From: yomnes0 <127947185+yomnes0@users.noreply.github.com> Date: Fri, 2 Feb 2024 10:50:00 +0100 Subject: [PATCH 04/51] [core] Moved declaration of SRT_ATR_ALIGNAS to fix a warning (#2866). --- srtcore/channel.h | 7 ------- srtcore/srt_attr_defs.h | 8 ++++++++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/srtcore/channel.h b/srtcore/channel.h index 6df7ec0ce..e09b13fd9 100644 --- a/srtcore/channel.h +++ b/srtcore/channel.h @@ -49,13 +49,6 @@ written by modified by Haivision Systems Inc. *****************************************************************************/ -#if HAVE_CXX11 -#define SRT_ATR_ALIGNAS(n) alignas(n) -#elif HAVE_GCC -#define SRT_ATR_ALIGNAS(n) __attribute__((aligned(n))) -#else -#define SRT_ATR_ALIGNAS(n) -#endif #ifndef INC_SRT_CHANNEL_H #define INC_SRT_CHANNEL_H diff --git a/srtcore/srt_attr_defs.h b/srtcore/srt_attr_defs.h index 84daabeb1..85ea9f96d 100644 --- a/srtcore/srt_attr_defs.h +++ b/srtcore/srt_attr_defs.h @@ -31,6 +31,14 @@ used by SRT library internally. #define ATR_DEPRECATED #endif +#if HAVE_CXX11 +#define SRT_ATR_ALIGNAS(n) alignas(n) +#elif HAVE_GCC +#define SRT_ATR_ALIGNAS(n) __attribute__((aligned(n))) +#else +#define SRT_ATR_ALIGNAS(n) +#endif + #if defined(__cplusplus) && __cplusplus > 199711L #define HAVE_CXX11 1 // For gcc 4.7, claim C++11 is supported, as long as experimental C++0x is on, From 8b73dbc4fcbf3ba8f07883317f1c4d092b3bd8d9 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 12 Feb 2024 13:01:01 +0100 Subject: [PATCH 05/51] [core] Fixed the RCV buff nonread position update condition in case of dropping upto a sequence number. --- srtcore/buffer_rcv.cpp | 2 +- srtcore/buffer_rcv.h | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 7ea0945bc..fb389e4be 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -239,7 +239,7 @@ int CRcvBuffer::dropUpTo(int32_t seqno) // 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 (CSeqNo::seqcmp(m_iFirstNonreadPos, m_iStartPos) < 0) + if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; updateNonreadPos(); diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index d4b50fab7..d664373f5 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -239,9 +239,15 @@ 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); } + + /// @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 { - // XXX maybe not the best implementation, but this keeps up to the rule + // 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; From faefc98ada024d92a10312a0028c13614d95eda7 Mon Sep 17 00:00:00 2001 From: kura <86100095+kura979@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:47:06 +0900 Subject: [PATCH 06/51] [core] Fixed RCV loss list inserting a seqno with a big distance after the largest seqno (#2877). --- srtcore/list.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/srtcore/list.cpp b/srtcore/list.cpp index 0a175ee9d..0fe7d2b4b 100644 --- a/srtcore/list.cpp +++ b/srtcore/list.cpp @@ -653,6 +653,8 @@ bool srt::CRcvLossList::remove(int32_t seqno) } m_iLength--; + if (m_iLength == 0) + m_iLargestSeq = SRT_SEQNO_NONE; return true; } @@ -708,6 +710,8 @@ bool srt::CRcvLossList::remove(int32_t seqno) } m_iLength--; + if (m_iLength == 0) + m_iLargestSeq = SRT_SEQNO_NONE; return true; } From 0e215cdcdbb0924a9053c82376f90eac5cd7b069 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 16 Feb 2024 10:50:23 +0100 Subject: [PATCH 07/51] [docs] Updated the documentation about latency and transtype (#2875). Co-authored-by: stevomatthews --- docs/API/API-socket-options.md | 38 ++++-- docs/features/latency.md | 219 +++++++++++++++++++++++++++++++++ 2 files changed, 248 insertions(+), 9 deletions(-) create mode 100644 docs/features/latency.md diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index ef6f87513..84e361f6d 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -301,7 +301,8 @@ connection is rejected - **however** you may also change the value of this option for the accepted socket in the listener callback (see `srt_listen_callback`) if an appropriate instruction was given in the Stream ID. -Currently supported congestion controllers are designated as "live" and "file" +Currently supported congestion controllers are designated as "live" and "file", +which correspond to the Live and File modes. Note that it is not recommended to change this option manually, but you should rather change the whole set of options using the [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) option. @@ -772,7 +773,8 @@ for more details. | `SRTO_LATENCY` | 1.0.2 | pre | `int32_t` | ms | 120 * | 0.. | RW | GSD | This option sets both [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY) and [`SRTO_PEERLATENCY`](#SRTO_PEERLATENCY) -to the same value specified. +to the same value specified. Note that the default value for `SRTO_RCVLATENCY` is modified by the +[`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) option. Prior to SRT version 1.3.0 `SRTO_LATENCY` was the only option to set the latency. However it is effectively equivalent to setting `SRTO_PEERLATENCY` in the sending direction @@ -1212,6 +1214,8 @@ considered broken on timeout. The latency value (as described in [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY)) provided by the sender side as a minimum value for the receiver. +This value is only significant when [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) is enabled. + Reading the value of the option on an unconnected socket reports the configured value. Reading the value on a connected socket reports the effective receiver buffering latency of the peer. @@ -1296,16 +1300,22 @@ This value is only significant when [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) is enabl **Default value**: 120 ms in Live mode, 0 in File mode (see [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE)). The latency value defines the **minimum** receiver buffering delay before delivering an SRT data packet -from a receiving SRT socket to a receiving application. The provided value is used in the connection establishment (handshake exchange) stage -to fix the end-to-end latency of the transmission. The effective end-to-end latency `L` will be fixed -as the network transmission time of the final handshake packet (~1/2 RTT) plus the **negotiated** latency value `Ln`. -Data packets will stay in the receiver buffer for at least `L` microseconds since the timestamp of the -packet, independent of the actual network transmission times (RTT variations) of these packets. +from a receiving SRT socket to a receiving application. The actual value of the receiver buffering delay `Ln` (the negotiated latency) used on a connection is determined by the negotiation in the connection establishment (handshake exchange) phase as the maximum of the `SRTO_RCVLATENCY` value and the value of [`SRTO_PEERLATENCY`](#SRTO_PEERLATENCY) set by the peer. +The general idea for the latency mechanism is to keep the time distance between two consecutive +received packets the same as the time when these same packets were scheduled for sending by the +sender application (or per the time explicitly declared when sending - see +[`srt_sendmsg2`](API-functions.md#srt_sendmsg2) for details). This keeps any packets that have arrived +earlier than their delivery time in the receiver buffer until their delivery time comes. This should +compensate for any jitter in the network and provides an extra delay needed for a packet retransmission. + +For detailed information on how the latency setting influences the actual packet delivery time and +how this time is defined, refer to the [latency documentation](../features/latency.md). + Reading the `SRTO_RCVLATENCY` value on a socket after the connection is established provides the actual (negotiated) latency value `Ln`. @@ -1638,9 +1648,19 @@ enabled in sender if receiver supports it. Sets the transmission type for the socket, in particular, setting this option sets multiple other parameters to their default values as required for a -particular transmission type. +particular transmission type. This sets the following options to their defaults +in particular mode: + +* [`SRTO_CONGESTION`](#SRTO_CONGESTION) +* [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) +* [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) +* [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY), also set as [`SRTO_LATENCY`](#SRTO_LATENCY) +* [`SRTO_TLPKTDROP`](#SRTO_TLPKTDROP) +* [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) + + -Values defined by enum `SRT_TRANSTYPE` (see above for possible values) +Values defined by enum [`SRT_TRANSTYPE`](#SRT_TRANSTYPE). [Return to list](#list-of-options) diff --git a/docs/features/latency.md b/docs/features/latency.md new file mode 100644 index 000000000..d287ac030 --- /dev/null +++ b/docs/features/latency.md @@ -0,0 +1,219 @@ +## General statement about latency + +In the live streaming there are many things happening between the +camera's lens and the screen of the video player, all of which contribute +to a delay that is generally referred to as "latency". This overall latency +includes the time it takes for the camera frame grabber device to pass +frames to the encoder, encoding, multiplexing, **sending over the network**, +splitting, decoding and then finally displaying. + +In SRT, however, "latency" is defined as only the delay introduced by **sending +over the network**. It's the time between the moment when the `srt_sendmsg2` +function is called at the sender side up to the moment when the `srt_recvmsg2` +function is called at the receiver side. This SRT latency is the actual time difference +between these two events. + + +## The goal of the latency (TSBPD) mechanism + +SRT employs a TimeStamp Based Packet Delivery (TSBPD) mechanism +with strict goal of keeping the time interval between two consecutive packets +on the receiver side identical to what they were at the sender side. This +requires introducing an extra delay that should define when exactly the packet +can be retrieved by the receiver application -- if the packet arrives early, it must +wait in the receiver buffer until the delivery time. This time for a packet N +is roughly defined as: + +``` +PTS[N] = ETS[N] + LATENCY(option) +``` + +where `ETS[N]` is the time when the packet would arrive, if all delays +from the network and the processing software on both sides are identical +to what they were for the very first received data packet. This means that +for the very first packet `ETS[0]` is equal to this packet's arrival time. +For every following packet the delivery time interval should be equal to the +that packet's declared scheduling time interval. + + +## SRT's approach to packet arrival time + +SRT provides two socket options `SRTO_PEERLATENCY` and `SRTO_RCVLATENCY`. +While they have "latency" in their names, they do *not* define the true time +interval between the `srt_sendmsg2` and `srt_recvmsg2` calls for the same +packet. They are only used to add an extra delay (at the receiver side) to +the time when the packet "should" arrive (ETS). This extra delay is used to +compensate for two things: + +* an extra network delay (that is, if the packet arrived later than it +"should have arrived"), or + +* a packet retransmission. + +Note that many of the values included in these formulas are not controllable and +some cannot be measured directly. In many cases there are measured values +that are sums of other values, but the component values can't be extracted. + +There are two values that we can obtain at the receiver side: + +* ATS: actual arrival time, which is the time when the UDP packet +has been extracted through the `recvmsg` system call. + +* TS: time recorded in the packet header, set on the sender side and extracted +from the packet at the receiver side + +Note that the timestamp in the packet's header is 32-bit, which gives +it more or less 2.5 minutes to roll over. Therefore timestamp +rollover is tracked and a segment increase is performed in order to keep an +eye on the overall actual time. For the needs of the formula definitions +it must be stated that TS is the true difference between the connection +start time and the time when the sending time has been declared when +the sender application is calling any of the `srt_send*` functions +(see [`srt_sendmsg2`](../API/API-functions.md#srt_sendmsg2) for details). + + +## SRT latency components + +To understand the latency components we need also other definitions: + +* **ETS** (Expected Time Stamp): The packet's expected arrival time, when it +"should" arrive according to its timestamp + +* **PTS** (Presentation Time Stamp): The packet's play time, when SRT gives the packet +to the `srt_recvmsg2` call (that is, it sets up the IN flag in epoll +and resumes the blocked function call, if it was in blocking mode). + +* **STS** (Sender Time Stamp): The time when the packet was +scheduled for sending at the sender side (if you don't use the +declared time, by default it's the monotonic time used when this +function is called). + +* **RTS** (Receiver Time Stamp): The same as STS, but calculated at the receiver side. The +only way to extract it is by using some initial statements. + +The "true latency" for a particular packet in SRT can be simply defined as: + +* `TD = PTS - STS` + +Note that this is a stable definition (independent of the packet), +but this value is not really controllable. So let's define the PTS +for a packet `x`: + +* `PTS[x] = ETS[x] + LATENCY + DRIFT` + +where `LATENCY` is the negotiated latency value (out of the +`SRTO_RCVLATENCY` on the agent and `SRTO_PEERLATENCY` on the peer) +and DRIFT will be described later (for simplification you can +state it's initially 0). + +These components undergo the following formula: + +* `ETS[x] = start_time + TS[x]` + +Note that it's not possible to simply define a "true" latency based on STS +because the sender and receiver are two different machines that can only +see one another through the network. Their clocks are separate, +and can even run at different or changing speeds, and the only +visible phenomena happen when packets arrive at the receiver machine. +However, the formula above does allow us to define the start time because +we state the following for the very first data packet: + +* `ETS[0] = ATS[0]` + +This means that from this formula we can define the start time: + +* `start_time = ATS[0] - TS[0]` + +Therefore we can state that if we have two identical clocks on +both machines with identical time bases and speeds, then: + +* `ATS[x] = program_delay[x] + network_delay[x] + STS[x]` + +Note that two machines communicating over a network do not typically have a +common clock base. Therefore, although this formula is correct, it involves +components that can neither be measured nor captured at the receiver side. + +This formula for ATS doesn't apply to the real latency, which is based strictly +on ETS. But you can apply this formula for the very first arriving packet, +because in this case they are equal: `ATS[0] = ETS[0]`. + +Therefore this formula is true for the very first packet: + +* `ETS[0] = prg_delay[0] + net_delay[0] + STS[0]` + +We know also that the TS set on the sender side is: + +* `TS[x] = STS[x] - snd_connect_time` + +Taking both formulas for ETS together: + +* `ETS[x] = start_time + TS[x] = prg_delay[0] + net_delay[0] + snd_connect_time + TS[x]` + +we have then: + +* `start_time = prg_delay[0] + net_delay[0] + snd_connect_time` + +**IMPORTANT**: `start_time` is not the time of arrival of the first packet, +but that time taken backwards by using the delay already recorded in TS. As TS should +represent the delay towards `snd_connect_time`, `start_time` should be simply the same +as `snd_connect_time`, just on the receiver side, and so shifted by the +first packet's delays of `prg_delay` and `net_delay`. + +So, as we have the start time defined, the above formulas: + +* `ETS[x] = start_time + TS[x]` +* `PTS[x] = ETS[x] + LATENCY + DRIFT` + +now define the packet delivery time as: + +* `PTS[x] = start_time + TS[x] + LATENCY + DRIFT` + +and after replacing the start time we have: + +* `PTS[x] = prg_delay[0] + net_delay[0] + snd_connect_time + TS[x] + LATENCY + DRIFT` + +and from the TS formula we get STS, so we replace it: + +* `PTS[x] = prg_delay[0] + net_delay[0] + STS[x] + LATENCY + DRIFT` + +We can now get the true network latency in SRT by moving STS to the other side: + +* `PTS[x] - STS[x] = prg_delay[0] + net_delay[0] + LATENCY + DRIFT` + + +## The DRIFT + +The DRIFT is a measure of the variance over time of the base time. +To simplify the calculations above, DRIFT is considered to be 0, +which is the initial state. In time, however, it changes based on the +value of the Arrival Time Deviation: + +* `ATD[x] = ATS[x] - ETS[x]` + +The drift is then calculated as: + +* `DRIFT[x] = average(ATD[x-N] ... ATD[x])` + +The value of the drift is tracked over an appropriate number of samples. If +it exceeds a threshold value, the drift value is applied to modify the +base time. However, as you can see from the formula for ATD, the drift is +simply taken from the actual time when the packet arrived, and the time +when it would have arrived if the `prg_delay` and `net_delay` values were +exactly the same as for the very first packet. ATD then represents the +changes in these values. There can be two main factors that could result +in having this value as non-zero: + +1. A phenomenon has been observed in several types of networks where +the very first packet arrives quickly, but as subsequent data packets +come in regularly, the network delay slightly increases and then remains fixed +for a long time at this increased value. This phenomenon can be +mitigated by having a reliable value for RTT. Once the increase is observed +a special factor could be applied to decrease the positive value +of the drift. This isn't currently implemented. This phenomenon also +isn't observed in every network, especially those covering longer distances. + +2. The clock speed on both machines (sender and receiver) isn't exactly the same, +which means that if you decipher the ETS basing on the TS, over time it may result +in values that even precede the STS (suggesting a negative network delay) or that +have an enormous delay (with ATS exceeding PTS). This is actually the main reason +for tracking the drift. From 52fe11c24adad57c0f462018f342738ac5dd5269 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 16 Feb 2024 14:41:34 +0100 Subject: [PATCH 08/51] [core] Fixed the PacketFilter configuration not counting the AEAD AUTH tag (#2880). * Fixed build break due to const use of UniquePtr. --- srtcore/core.cpp | 10 +++++++++- srtcore/core.h | 1 + srtcore/fec.cpp | 3 +++ srtcore/packetfilter.cpp | 8 +++++++- srtcore/utilities.h | 2 +- 5 files changed, 21 insertions(+), 3 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index dcb264993..b15f91287 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5649,6 +5649,14 @@ bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd return true; } +int srt::CUDT::getAuthTagSize() const +{ + if (m_pCryptoControl && m_pCryptoControl->getCryptoMode() == CSrtConfig::CIPHER_MODE_AES_GCM) + return HAICRYPT_AUTHTAG_MAX; + + return 0; +} + bool srt::CUDT::prepareBuffers(CUDTException* eout) { if (m_pSndBuffer) @@ -5660,7 +5668,7 @@ bool srt::CUDT::prepareBuffers(CUDTException* eout) try { // CryptoControl has to be initialized and in case of RESPONDER the KM REQ must be processed (interpretSrtHandshake(..)) for the crypto mode to be deduced. - const int authtag = (m_pCryptoControl && m_pCryptoControl->getCryptoMode() == CSrtConfig::CIPHER_MODE_AES_GCM) ? HAICRYPT_AUTHTAG_MAX : 0; + const int authtag = getAuthTagSize(); SRT_ASSERT(m_iMaxSRTPayloadSize != 0); diff --git a/srtcore/core.h b/srtcore/core.h index 9d5fa4c3a..08e03230c 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -517,6 +517,7 @@ class CUDT /// Allocates sender and receiver buffers and loss lists. SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) bool prepareBuffers(CUDTException* eout); + int getAuthTagSize() const; SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) EConnectStatus postConnect(const CPacket* response, bool rendezvous, CUDTException* eout) ATR_NOEXCEPT; diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index 9830a3bc8..fd762b88b 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -598,6 +598,9 @@ void FECFilterBuiltin::ClipData(Group& g, uint16_t length_net, uint8_t kflg, g.flag_clip = g.flag_clip ^ kflg; g.timestamp_clip = g.timestamp_clip ^ timestamp_hw; + HLOGC(pflog.Debug, log << "FEC CLIP: data pkt.size=" << payload_size + << " to a clip buffer size=" << payloadSize()); + // Payload goes "as is". for (size_t i = 0; i < payload_size; ++i) { diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index 37785f43a..9610f04e3 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -314,9 +314,15 @@ bool srt::PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::strin init.socket_id = parent->socketID(); init.snd_isn = parent->sndSeqNo(); init.rcv_isn = parent->rcvSeqNo(); - init.payload_size = parent->OPT_PayloadSize(); + + // XXX This is a formula for a full "SRT payload" part that undergoes transmission, + // might be nice to have this formula as something more general. + init.payload_size = parent->OPT_PayloadSize() + parent->getAuthTagSize(); init.rcvbuf_size = parent->m_config.iRcvBufSize; + HLOGC(pflog.Debug, log << "PFILTER: @" << init.socket_id << " payload size=" + << init.payload_size << " rcvbuf size=" << init.rcvbuf_size); + // Found a filter, so call the creation function m_filter = selector->second->Create(init, m_provided, confstr); if (!m_filter) 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 e0d4227ad3061d4cdc1dd8554648bc466c760780 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 16 Feb 2024 16:23:38 +0100 Subject: [PATCH 09/51] [core] Refax. Removed reference fields from CPacket pinned to the SRT header (#2594). --- srtcore/buffer_snd.cpp | 16 +-- srtcore/channel.cpp | 4 +- srtcore/core.cpp | 223 ++++++++++++++++++----------------- srtcore/core.h | 2 +- srtcore/packet.cpp | 6 +- srtcore/packet.h | 11 +- srtcore/packetfilter.cpp | 2 +- srtcore/queue.cpp | 4 +- srtcore/utilities.h | 1 + srtcore/window.h | 8 +- test/test_buffer_rcv.cpp | 19 +-- test/test_crypto.cpp | 7 +- test/test_fec_rebuilding.cpp | 6 +- 13 files changed, 159 insertions(+), 150 deletions(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index 3aa81042e..ef1bc498c 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -317,7 +317,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, w_packet.m_pcData = m_pCurrBlock->m_pcData; readlen = m_pCurrBlock->m_iLength; w_packet.setLength(readlen, m_iBlockLen); - w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo; + w_packet.set_seqno(m_pCurrBlock->m_iSeqNo); // 1. On submission (addBuffer), the KK flag is set to EK_NOENC (0). // 2. The readData() is called to get the original (unique) payload not ever sent yet. @@ -343,7 +343,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, } Block* p = m_pCurrBlock; - w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset; + w_packet.set_msgflags(m_pCurrBlock->m_iMsgNoBitset); w_srctime = m_pCurrBlock->m_tsOriginTime; m_pCurrBlock = m_pCurrBlock->m_pNext; @@ -358,7 +358,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, HLOGC(bslog.Debug, log << CONID() << "CSndBuffer: picked up packet to send: size=" << readlen << " #" << w_packet.getMsgSeq() - << " %" << w_packet.m_iSeqNo + << " %" << w_packet.seqno() << " !" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); break; @@ -449,7 +449,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // This is rexmit request, so the packet should have the sequence number // already set when it was once sent uniquely. - SRT_ASSERT(p->m_iSeqNo == w_packet.m_iSeqNo); + SRT_ASSERT(p->m_iSeqNo == w_packet.seqno()); // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale. @@ -493,8 +493,8 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // to simply take the sequence number from the block. But this is a new // feature and should be only used after refax for the sender buffer to // make it manage the sequence numbers inside, instead of by CUDT::m_iSndLastDataAck. - w_drop.seqno[DropRange::BEGIN] = w_packet.m_iSeqNo; - w_drop.seqno[DropRange::END] = CSeqNo::incseq(w_packet.m_iSeqNo, msglen - 1); + w_drop.seqno[DropRange::BEGIN] = w_packet.seqno(); + w_drop.seqno[DropRange::END] = CSeqNo::incseq(w_packet.seqno(), msglen - 1); // Note the rules: here `p` is pointing to the first block AFTER the // message to be dropped, so the end sequence should be one behind @@ -515,7 +515,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // encrypted, and with all ENC flags already set. So, the first call to send // the packet originally (the other overload of this function) must set these // flags. - w_packet.m_iMsgNo = p->m_iMsgNoBitset; + w_packet.set_msgflags(p->m_iMsgNoBitset); w_srctime = p->m_tsOriginTime; // This function is called when packet retransmission is triggered. @@ -523,7 +523,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time p->m_tsRexmitTime = steady_clock::now(); HLOGC(qslog.Debug, - log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.m_iSeqNo + log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.seqno() << " size=" << readlen << " to send [REXMIT]"); return readlen; diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 0b448d681..16ccc8c1b 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -683,8 +683,8 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka #endif LOGC(kslog.Debug, - log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.m_iID - << " size=" << packet.getLength() << " pkt.ts=" << packet.m_iTimeStamp + log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.id() + << " size=" << packet.getLength() << " pkt.ts=" << packet.timestamp() << dsrc.str() << " " << packet.Info()); #endif diff --git a/srtcore/core.cpp b/srtcore/core.cpp index b15f91287..905c10457 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1982,7 +1982,7 @@ bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt) uint32_t *srtdata = (uint32_t *)ctrlpkt->m_pcData; size_t len = ctrlpkt->getLength(); int etype = ctrlpkt->getExtendedType(); - uint32_t ts = ctrlpkt->m_iTimeStamp; + uint32_t ts = ctrlpkt->timestamp(); int res = SRT_CMD_NONE; @@ -2513,7 +2513,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; // don't interpret } - int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.timestamp(), HS_VERSION_SRT1); // Interpreted? Then it should be responded with SRT_CMD_HSRSP. if (rescmd != SRT_CMD_HSRSP) { @@ -2540,7 +2540,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; // don't interpret } - int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.timestamp(), HS_VERSION_SRT1); // Interpreted? Then it should be responded with SRT_CMD_NONE. // (nothing to be responded for HSRSP, unless there was some kinda problem) if (rescmd != SRT_CMD_NONE) @@ -3572,7 +3572,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // control of methods.) // ID = 0, connection request - reqpkt.m_iID = 0; + reqpkt.set_id(0); size_t hs_size = m_iMaxSRTPayloadSize; m_ConnReq.store_to((reqpkt.m_pcData), (hs_size)); @@ -3587,7 +3587,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) setPacketTS(reqpkt, tnow); HLOGC(cnlog.Debug, - log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.m_iTimeStamp + log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.timestamp() << "). SENDING HS: " << m_ConnReq.show()); /* @@ -3659,7 +3659,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) << " > 250 ms). size=" << reqpkt.getLength()); if (m_config.bRendezvous) - reqpkt.m_iID = m_ConnRes.m_iID; + reqpkt.set_id(m_ConnRes.m_iID); #if ENABLE_HEAVY_LOGGING { @@ -3897,17 +3897,17 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, // This should have got the original value returned from // processConnectResponse through processAsyncConnectResponse. - CPacket request; - request.setControl(UMSG_HANDSHAKE); - request.allocate(m_iMaxSRTPayloadSize); + CPacket reqpkt; + reqpkt.setControl(UMSG_HANDSHAKE); + reqpkt.allocate(m_iMaxSRTPayloadSize); const steady_clock::time_point now = steady_clock::now(); - setPacketTS(request, now); + setPacketTS(reqpkt, now); HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: REQ-TIME: HIGH. Should prevent too quick responses."); m_tsLastReqTime = now; // ID = 0, connection request - request.m_iID = !m_config.bRendezvous ? 0 : m_ConnRes.m_iID; + reqpkt.set_id(!m_config.bRendezvous ? 0 : m_ConnRes.m_iID); bool status = true; @@ -3918,7 +3918,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, if (cst == CONN_RENDEZVOUS) { HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: passing to processRendezvous"); - cst = processRendezvous(pResponse, serv_addr, rst, (request)); + cst = processRendezvous(pResponse, serv_addr, rst, (reqpkt)); if (cst == CONN_ACCEPT) { HLOGC(cnlog.Debug, @@ -3938,7 +3938,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, if (m_RejectReason == SRT_REJ_UNKNOWN) m_RejectReason = SRT_REJ_ROGUE; - sendRendezvousRejection(serv_addr, (request)); + sendRendezvousRejection(serv_addr, (reqpkt)); status = false; } } @@ -3955,8 +3955,8 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, { // (this procedure will be also run for HSv4 rendezvous) HLOGC(cnlog.Debug, - log << CONID() << "processAsyncConnectRequest: serializing HS: buffer size=" << request.getLength()); - if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (request), (m_ConnReq))) + log << CONID() << "processAsyncConnectRequest: serializing HS: buffer size=" << reqpkt.getLength()); + if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (reqpkt), (m_ConnReq))) { // All 'false' returns from here are IPE-type, mostly "invalid argument" plus "all keys expired". LOGC(cnlog.Error, @@ -3968,7 +3968,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: sending HS reqtype=" << RequestTypeStr(m_ConnReq.m_iReqType) - << " to socket " << request.m_iID << " size=" << request.getLength()); + << " to socket " << reqpkt.id() << " size=" << reqpkt.getLength()); } } @@ -3978,16 +3978,16 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, /* XXX Shouldn't it send a single response packet for the rejection? // Set the version to 0 as "handshake rejection" status and serialize it CHandShake zhs; - size_t size = request.getLength(); - zhs.store_to((request.m_pcData), (size)); - request.setLength(size); + size_t size = reqpkt.getLength(); + zhs.store_to((reqpkt.m_pcData), (size)); + reqpkt.setLength(size); */ } HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: setting REQ-TIME HIGH, SENDING HS:" << m_ConnReq.show()); m_tsLastReqTime = steady_clock::now(); - m_pSndQueue->sendto(serv_addr, request, m_SourceAddr); + m_pSndQueue->sendto(serv_addr, reqpkt, m_SourceAddr); return status; } @@ -5877,15 +5877,15 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& size_t size = m_iMaxSRTPayloadSize; // Allocate the maximum possible memory for an SRT payload. // This is a maximum you can send once. - CPacket response; - response.setControl(UMSG_HANDSHAKE); - response.allocate(size); + CPacket rsppkt; + rsppkt.setControl(UMSG_HANDSHAKE); + rsppkt.allocate(size); // This will serialize the handshake according to its current form. HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); - if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (response), (w_hs))) + if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (rsppkt), (w_hs))) { LOGC(cnlog.Error, log << CONID() << "acceptAndRespond: error creating handshake response"); throw CUDTException(MJ_SETUP, MN_REJECTED, 0); @@ -5899,10 +5899,10 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // To make sure what REALLY is being sent, parse back the handshake // data that have been just written into the buffer. CHandShake debughs; - debughs.load_from(response.m_pcData, response.getLength()); + debughs.load_from(rsppkt.m_pcData, rsppkt.getLength()); HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: sending HS from agent @" - << debughs.m_iID << " to peer @" << response.m_iID + << debughs.m_iID << " to peer @" << rsppkt.id() << "HS:" << debughs.show() << " sourceIP=" << m_SourceAddr.str()); } @@ -5913,7 +5913,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // When missed this message, the caller should not accept packets // coming as connected, but continue repeated handshake until finally // received the listener's handshake. - addressAndSend((response)); + addressAndSend((rsppkt)); } bool srt::CUDT::frequentLogAllowed(size_t logid, const time_point& tnow, std::string& w_why) @@ -6159,7 +6159,7 @@ void srt::CUDT::checkSndKMRefresh() void srt::CUDT::addressAndSend(CPacket& w_pkt) { - w_pkt.m_iID = m_PeerID; + w_pkt.set_id(m_PeerID); setPacketTS(w_pkt, steady_clock::now()); // NOTE: w_pkt isn't modified in this call, @@ -7882,7 +7882,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement ctrlpkt.pack(pkttype, lparam); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; @@ -7897,7 +7897,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp size_t bytes = sizeof(*lossdata) * size; ctrlpkt.pack(pkttype, NULL, lossdata, bytes); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); enterCS(m_StatsLock); @@ -7918,7 +7918,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp if (0 < losslen) { ctrlpkt.pack(pkttype, NULL, data, losslen * 4); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); enterCS(m_StatsLock); @@ -7948,7 +7948,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_CGWARNING: // 100 - Congestion Warning ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); m_tsLastWarningTime = steady_clock::now(); @@ -7957,14 +7957,14 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_KEEPALIVE: // 001 - Keep-alive ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; case UMSG_HANDSHAKE: // 000 - Handshake ctrlpkt.pack(pkttype, NULL, rparam, sizeof(CHandShake)); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; @@ -7973,21 +7973,21 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp if (m_PeerID == 0) // Dont't send SHUTDOWN if we don't know peer ID. break; ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; case UMSG_DROPREQ: // 111 - Msg drop request ctrlpkt.pack(pkttype, lparam, rparam, 8); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; case UMSG_PEERERROR: // 1000 - acknowledge the peer side a special error ctrlpkt.pack(pkttype, lparam); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; @@ -8074,7 +8074,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { bufflock.unlock(); ctrlpkt.pack(UMSG_ACK, NULL, &ack, size); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); DebugAck(CONID() + "sendCtrl(lite): ", local_prevack, ack); return nbsent; @@ -8278,7 +8278,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) ctrlpkt.pack(UMSG_ACK, &m_iAckSeqNo, data, ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_SMALL); } - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); setPacketTS(ctrlpkt, steady_clock::now()); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); DebugAck(CONID() + "sendCtrl(UMSG_ACK): ", local_prevack, ack); @@ -8976,20 +8976,20 @@ void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType) << (have_hsreq ? " WITH SRT HS response extensions" : "")); - CPacket response; - response.setControl(UMSG_HANDSHAKE); - response.allocate(m_iMaxSRTPayloadSize); + CPacket rsppkt; + rsppkt.setControl(UMSG_HANDSHAKE); + rsppkt.allocate(m_iMaxSRTPayloadSize); // If createSrtHandshake failed, don't send anything. Actually it can only fail on IPE. // There is also no possible IPE condition in case of HSv4 - for this version it will always return true. enterCS(m_ConnectionLock); - bool create_ok = createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (response), (initdata)); + bool create_ok = createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (rsppkt), (initdata)); leaveCS(m_ConnectionLock); if (create_ok) { - response.m_iID = m_PeerID; - setPacketTS(response, steady_clock::now()); - const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response, m_SourceAddr); + rsppkt.set_id(m_PeerID); + setPacketTS(rsppkt, steady_clock::now()); + const int nbsent = m_pSndQueue->sendto(m_PeerAddr, rsppkt, m_SourceAddr); if (nbsent) { m_tsLastSndTime.store(steady_clock::now()); @@ -9121,7 +9121,7 @@ void srt::CUDT::processCtrl(const CPacket &ctrlpkt) HLOGC(inlog.Debug, log << CONID() << "incoming UMSG:" << ctrlpkt.getType() << " (" - << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.m_iID); + << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.id()); switch (ctrlpkt.getType()) { @@ -9296,37 +9296,44 @@ int srt::CUDT::packLostData(CPacket& w_packet) const steady_clock::time_point time_now = steady_clock::now(); const steady_clock::time_point time_nak = time_now - microseconds_from(m_iSRTT - 4 * m_iRTTVar); - while ((w_packet.m_iSeqNo = m_pSndLossList->popLostSeq()) >= 0) + for (;;) { + w_packet.set_seqno(m_pSndLossList->popLostSeq()); + if (w_packet.seqno() == SRT_SEQNO_NONE) + break; + // XXX See the note above the m_iSndLastDataAck declaration in core.h // This is the place where the important sequence numbers for // sender buffer are actually managed by this field here. - const int offset = CSeqNo::seqoff(m_iSndLastDataAck, w_packet.m_iSeqNo); + const int offset = CSeqNo::seqoff(m_iSndLastDataAck, w_packet.seqno()); if (offset < 0) { // XXX Likely that this will never be executed because if the upper // sequence is not in the sender buffer, then most likely the loss // was completely ignored. LOGC(qrlog.Error, - log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(m_iSeqNo " - << w_packet.m_iSeqNo << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset - << ". Continue"); + log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(seqno() " + << w_packet.seqno() << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset + << ". Continue, request DROP"); // No matter whether this is right or not (maybe the attack case should be // considered, and some LOSSREPORT flood prevention), send the drop request // to the peer. int32_t seqpair[2] = { - w_packet.m_iSeqNo, + w_packet.seqno(), CSeqNo::decseq(m_iSndLastDataAck) }; - w_packet.m_iMsgNo = 0; // Message number is not known, setting all 32 bits to 0. HLOGC(qrlog.Debug, - log << CONID() << "PEER reported LOSS not from the sending buffer - requesting DROP: msg=" - << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "(" + log << CONID() << "PEER reported LOSS not from the sending buffer - requesting DROP: #" + << MSGNO_SEQ::unwrap(w_packet.msgflags()) << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "(" << (-offset) << " packets)"); - sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); + // See interpretation in processCtrlDropReq(). We don't know the message number, + // so we request that the drop be exclusively sequence number based. + int32_t msgno = SRT_MSGNO_CONTROL; + + sendCtrl(UMSG_DROPREQ, &msgno, seqpair, sizeof(seqpair)); continue; } @@ -9336,7 +9343,7 @@ int srt::CUDT::packLostData(CPacket& w_packet) if (tsLastRexmit >= time_nak) { HLOGC(qrlog.Debug, log << CONID() << "REXMIT: ignoring seqno " - << w_packet.m_iSeqNo << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) + << w_packet.seqno() << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) << " RTT=" << m_iSRTT << " RTTVar=" << m_iRTTVar << " now=" << FormatTime(time_now)); continue; @@ -9388,7 +9395,7 @@ int srt::CUDT::packLostData(CPacket& w_packet) // So, set here the rexmit flag if the peer understands it. if (m_bPeerRexmitFlag) { - w_packet.m_iMsgNo |= PACKET_SND_REXMIT; + w_packet.set_msgflags(w_packet.msgflags() | PACKET_SND_REXMIT); } setDataPacketTS(w_packet, tsOrigin); @@ -9493,7 +9500,7 @@ void srt::CUDT::setPacketTS(CPacket& p, const time_point& ts) enterCS(m_StatsLock); const time_point tsStart = m_stats.tsStartTime; leaveCS(m_StatsLock); - p.m_iTimeStamp = makeTS(ts, tsStart); + p.set_timestamp(makeTS(ts, tsStart)); } void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) @@ -9505,14 +9512,14 @@ void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) if (!m_bPeerTsbPd) { // If TSBPD is disabled, use the current time as the source (timestamp using the sending time). - p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart); + p.set_timestamp(makeTS(steady_clock::now(), tsStart)); return; } // TODO: Might be better for performance to ensure this condition is always false, and just use SRT_ASSERT here. if (ts < tsStart) { - p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart); + p.set_timestamp(makeTS(steady_clock::now(), tsStart)); LOGC(qslog.Warn, log << CONID() << "setPacketTS: reference time=" << FormatTime(ts) << " is in the past towards start time=" << FormatTime(tsStart) @@ -9521,7 +9528,7 @@ void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) } // Use the provided source time for the timestamp. - p.m_iTimeStamp = makeTS(ts, tsStart); + p.set_timestamp(makeTS(ts, tsStart)); } bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) @@ -9632,14 +9639,14 @@ bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime new_packet_packed = true; // every 16 (0xF) packets, a packet pair is sent - if ((w_packet.m_iSeqNo & PUMASK_SEQNO_PROBE) == 0) + if ((w_packet.seqno() & PUMASK_SEQNO_PROBE) == 0) probe = true; payload = (int) w_packet.getLength(); IF_HEAVY_LOGGING(reason = "normal"); } - w_packet.m_iID = m_PeerID; // Set the destination SRT socket ID. + w_packet.set_id(m_PeerID); // Set the destination SRT socket ID. if (new_packet_packed && m_PacketFilter) { @@ -9649,7 +9656,7 @@ bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime #if ENABLE_HEAVY_LOGGING // Required because of referring to MessageFlagStr() HLOGC(qslog.Debug, - log << CONID() << "packData: " << reason << " packet seq=" << w_packet.m_iSeqNo << " (ACK=" << m_iSndLastAck + log << CONID() << "packData: " << reason << " packet seq=" << w_packet.seqno() << " (ACK=" << m_iSndLastAck << " ACKDATA=" << m_iSndLastDataAck << " MSG/FLAGS: " << w_packet.MessageFlagStr() << ")"); #endif @@ -9668,7 +9675,7 @@ bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime // Left untouched for historical reasons. // Might be possible that it was because of that this is send from // different thread than the rest of the signals. - // m_pSndTimeWindow->onPktSent(w_packet.m_iTimeStamp); + // m_pSndTimeWindow->onPktSent(w_packet.timestamp()); enterCS(m_StatsLock); m_stats.sndr.sent.count(payload); @@ -9768,7 +9775,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Fortunately the group itself isn't being accessed. if (m_parent->m_GroupOf) { - const int packetspan = CSeqNo::seqoff(current_sequence_number, w_packet.m_iSeqNo); + const int packetspan = CSeqNo::seqoff(current_sequence_number, w_packet.seqno()); if (packetspan > 0) { // After increasing by 1, but being previously set as ISN-1, this should be == ISN, @@ -9782,7 +9789,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // initialized from ISN just after connection. LOGC(qslog.Note, log << CONID() << "packUniqueData: Fixing EXTRACTION sequence " << current_sequence_number - << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " for the first packet: DIFF=" + << " from SCHEDULING sequence " << w_packet.seqno() << " for the first packet: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } else @@ -9790,7 +9797,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // There will be a serious data discrepancy between the agent and the peer. LOGC(qslog.Error, log << CONID() << "IPE: packUniqueData: Fixing EXTRACTION sequence " << current_sequence_number - << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " in the middle of transition: DIFF=" + << " from SCHEDULING sequence " << w_packet.seqno() << " in the middle of transition: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } @@ -9799,7 +9806,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Don't do it if the difference isn't positive or exceeds the threshold. int32_t seqpair[2]; seqpair[0] = current_sequence_number; - seqpair[1] = CSeqNo::decseq(w_packet.m_iSeqNo); + seqpair[1] = CSeqNo::decseq(w_packet.seqno()); const int32_t no_msgno = 0; LOGC(qslog.Debug, log << CONID() << "packUniqueData: Sending DROPREQ: SEQ: " << seqpair[0] << " - " << seqpair[1] << " (" @@ -9811,16 +9818,16 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Override extraction sequence with scheduling sequence. ScopedLock ackguard(m_RecvAckLock); - m_iSndCurrSeqNo = w_packet.m_iSeqNo; - m_iSndLastAck = w_packet.m_iSeqNo; - m_iSndLastDataAck = w_packet.m_iSeqNo; - m_iSndLastFullAck = w_packet.m_iSeqNo; - m_iSndLastAck2 = w_packet.m_iSeqNo; + m_iSndCurrSeqNo = w_packet.seqno(); + m_iSndLastAck = w_packet.seqno(); + m_iSndLastDataAck = w_packet.seqno(); + m_iSndLastFullAck = w_packet.seqno(); + m_iSndLastAck2 = w_packet.seqno(); } else if (packetspan < 0) { LOGC(qslog.Error, - log << CONID() << "IPE: packData: SCHEDULING sequence " << w_packet.m_iSeqNo + log << CONID() << "IPE: packData: SCHEDULING sequence " << w_packet.seqno() << " is behind of EXTRACTION sequence " << current_sequence_number << ", dropping this packet: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); // XXX: Probably also change the socket state to broken? @@ -9832,15 +9839,15 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) { HLOGC(qslog.Debug, log << CONID() << "packUniqueData: Applying EXTRACTION sequence " << current_sequence_number - << " over SCHEDULING sequence " << w_packet.m_iSeqNo << " for socket not in group:" - << " DIFF=" << CSeqNo::seqcmp(current_sequence_number, w_packet.m_iSeqNo) + << " over SCHEDULING sequence " << w_packet.seqno() << " for socket not in group:" + << " DIFF=" << CSeqNo::seqcmp(current_sequence_number, w_packet.seqno()) << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); // Do this always when not in a group. - w_packet.m_iSeqNo = current_sequence_number; + w_packet.set_seqno(current_sequence_number); } // Set missing fields before encrypting the packet, because those fields might be used for encryption. - w_packet.m_iID = m_PeerID; // Destination SRT Socket ID + w_packet.set_id(m_PeerID); // Destination SRT Socket ID setDataPacketTS(w_packet, tsOrigin); if (kflg != EK_NOENC) @@ -9860,7 +9867,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) } #if SRT_DEBUG_TRACE_SND - g_snd_logger.state.iPktSeqno = w_packet.m_iSeqNo; + g_snd_logger.state.iPktSeqno = w_packet.seqno(); g_snd_logger.state.isRetransmitted = w_packet.getRexmitFlag(); g_snd_logger.trace(); #endif @@ -10029,7 +10036,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& bool adding_successful = true; - const int32_t bufidx = CSeqNo::seqoff(bufseq, rpkt.m_iSeqNo); + const int32_t bufidx = CSeqNo::seqoff(bufseq, rpkt.seqno()); IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED"); @@ -10054,7 +10061,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // which never contains losses, so discarding this packet does not // discard a loss coverage, even if this were past ACK. - if (bufidx < 0 || CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvLastAck) < 0) + if (bufidx < 0 || CSeqNo::seqcmp(rpkt.seqno(), m_iRcvLastAck) < 0) { time_point pts = getPktTsbPdTime(NULL, rpkt); @@ -10067,7 +10074,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& m_stats.rcvr.recvdBelated.count(rpkt.getLength()); leaveCS(m_StatsLock); HLOGC(qrlog.Debug, - log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo << " bufidx=" << bufidx << " (BELATED/" + log << CONID() << "RECEIVED: %" << rpkt.seqno() << " bufidx=" << bufidx << " (BELATED/" << s_rexmitstat_str[pktrexmitflag] << ") with ACK %" << m_iRcvLastAck << " FLAGS: " << rpkt.MessageFlagStr()); continue; @@ -10089,7 +10096,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& LOGC(qrlog.Error, log << CONID() << "SEQUENCE DISCREPANCY. BREAKING CONNECTION." - " %" << rpkt.m_iSeqNo + " %" << rpkt.seqno() << " buffer=(%" << bufseq << ":%" << m_iRcvCurrSeqNo // -1 = size to last index << "+%" << CSeqNo::incseq(bufseq, int(m_pRcvBuffer->capacity()) - 1) @@ -10100,7 +10107,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& } else { - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.seqno() << ", insert offset " << bufidx << ". " << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) ); @@ -10219,7 +10226,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // Empty buffer info in case of groupwise receiver. // There's no way to obtain this information here. - LOGC(qrlog.Debug, log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo + LOGC(qrlog.Debug, log << CONID() << "RECEIVED: %" << rpkt.seqno() << bufinfo.str() << " RSL=" << expectspec.str() << " SN=" << s_rexmitstat_str[pktrexmitflag] @@ -10233,12 +10240,12 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& { HLOGC(qrlog.Debug, log << CONID() - << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); + << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.seqno())); - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. + if (CSeqNo::seqcmp(rpkt.seqno(), CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. { int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); - int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); + int32_t seqhi = CSeqNo::decseq(rpkt.seqno()); w_srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); HLOGC(qrlog.Debug, log << "pkt/LOSS DETECTED: %" << seqlo << " - %" << seqhi); } @@ -10246,9 +10253,9 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // 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) + if (CSeqNo::seqcmp(rpkt.seqno(), m_iRcvCurrSeqNo) > 0) { - m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received + m_iRcvCurrSeqNo = rpkt.seqno(); // Latest possible received } else { @@ -10296,7 +10303,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Search the sequence in the loss record. rexmit_reason = " by "; ScopedLock lock(m_RcvLossLock); - if (!m_pRcvLossList->find(packet.m_iSeqNo, packet.m_iSeqNo)) + if (!m_pRcvLossList->find(packet.seqno(), packet.seqno())) rexmit_reason += "BLIND"; else rexmit_reason += "NAKREPORT"; @@ -10340,7 +10347,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Conditions and any extra data required for the packet // this function will extract and test as needed. - const bool unordered = CSeqNo::seqcmp(packet.m_iSeqNo, m_iRcvCurrSeqNo) <= 0; + const bool unordered = CSeqNo::seqcmp(packet.seqno(), m_iRcvCurrSeqNo) <= 0; // Retransmitted and unordered packets do not provide expected measurement. // We expect the 16th and 17th packet to be sent regularly, @@ -10366,7 +10373,7 @@ int srt::CUDT::processData(CUnit* in_unit) // 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); + const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.seqno()); // Difference between these two sequence numbers is expected to be: // 0 - duplicated last packet (theory only) // 1 - subsequent packet (alright) @@ -10382,13 +10389,13 @@ int srt::CUDT::processData(CUnit* in_unit) HLOGC(qrlog.Debug, log << CONID() << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " - << CSeqNo::decseq(packet.m_iSeqNo) << "]"); + << CSeqNo::decseq(packet.seqno()) << "]"); } if (diff > 0) { // Record if it was further than latest - m_iRcvCurrPhySeqNo = packet.m_iSeqNo; + m_iRcvCurrPhySeqNo = packet.seqno(); } } @@ -10454,7 +10461,7 @@ int srt::CUDT::processData(CUnit* in_unit) // offset from RcvLastAck in RcvBuffer must remain valid between seqoff() and addData() UniqueLock recvbuf_acklock(m_RcvBufferLock); // Needed for possibly check for needsQuickACK. - const bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.m_iSeqNo, m_pRcvBuffer->getStartSeqNo()) < 0); + const bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.seqno(), m_pRcvBuffer->getStartSeqNo()) < 0); const int res = handleSocketPacketReception(incoming, (new_inserted), @@ -10739,7 +10746,7 @@ void srt::CUDT::updateIdleLinkFrom(CUDT* source) void srt::CUDT::unlose(const CPacket &packet) { ScopedLock lg(m_RcvLossLock); - int32_t sequence = packet.m_iSeqNo; + int32_t sequence = packet.seqno(); m_pRcvLossList->remove(sequence); // Rest of this code concerns only the "belated lossreport" feature. @@ -10759,7 +10766,7 @@ void srt::CUDT::unlose(const CPacket &packet) { HLOGC(qrlog.Debug, log << "received out-of-band packet %" << sequence); - const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.m_iSeqNo)); + const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.seqno())); enterCS(m_StatsLock); m_stats.traceReorderDistance = max(seqdiff, m_stats.traceReorderDistance); leaveCS(m_StatsLock); @@ -10974,7 +10981,7 @@ int32_t srt::CUDT::bake(const sockaddr_any& addr, int32_t current_cookie, int co int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) { // XXX ASSUMPTIONS: - // [[using assert(packet.m_iID == 0)]] + // [[using assert(packet.id() == 0)]] HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: received a connection request"); @@ -11062,7 +11069,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // array need not be aligned to int32_t - changed to union in a hope that using int32_t // inside a union will enforce whole union to be aligned to int32_t. hs.m_iCookie = cookie_val; - packet.m_iID = hs.m_iID; + packet.set_id(hs.m_iID); // Ok, now's the time. The listener sets here the version 5 handshake, // even though the request was 4. This is because the old client would @@ -11130,7 +11137,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding."); } - int32_t id = hs.m_iID; + SRTSOCKET id = hs.m_iID; // HANDSHAKE: The old client sees the version that does not match HS_VERSION_UDT4 (5). // In this case it will respond with URQ_ERROR_REJECT. Rest of the data are the same @@ -11178,8 +11185,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) 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()); + packet.set_id(id); + setPacketTS((packet), steady_clock::now()); HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (e): " << hs.show()); m_pSndQueue->sendto(addr, packet, use_source_addr); } @@ -11290,7 +11297,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) CPacket rsp; setPacketTS((rsp), steady_clock::now()); rsp.pack(UMSG_SHUTDOWN); - rsp.m_iID = m_PeerID; + rsp.set_id(m_PeerID); m_pSndQueue->sendto(addr, rsp, use_source_addr); } else @@ -11301,7 +11308,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); packet.setLength(size); - packet.m_iID = id; + packet.set_id(id); setPacketTS(packet, steady_clock::now()); HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (a): " << hs.show()); m_pSndQueue->sendto(addr, packet, use_source_addr); diff --git a/srtcore/core.h b/srtcore/core.h index 08e03230c..74b097efd 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -409,7 +409,7 @@ class CUDT static void setPacketTS(CPacket& p, const time_point& start_time, const time_point& ts) { - p.m_iTimeStamp = makeTS(ts, start_time); + p.set_timestamp(makeTS(ts, start_time)); } /// @brief Set the timestamp field of the packet using the provided value (no check) diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index fbb56a42c..b41ed6b9b 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -179,10 +179,6 @@ CPacket::CPacket() : m_nHeader() // Silences GCC 12 warning "used uninitialized". , m_extra_pad() , m_data_owned(false) - , m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO])) - , m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO])) - , m_iTimeStamp((int32_t&)(m_nHeader[SRT_PH_TIMESTAMP])) - , m_iID((int32_t&)(m_nHeader[SRT_PH_ID])) , m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef())) { m_nHeader.clear(); @@ -601,7 +597,7 @@ inline void SprintSpecialWord(std::ostream& os, int32_t val) std::string CPacket::Info() { std::ostringstream os; - os << "TARGET=@" << m_iID << " "; + os << "TARGET=@" << id() << " "; if (isControl()) { diff --git a/srtcore/packet.h b/srtcore/packet.h index 027d5f0b3..9b757118f 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -352,12 +352,15 @@ class CPacket CPacket(const CPacket&); public: - int32_t& m_iSeqNo; // alias: sequence number - int32_t& m_iMsgNo; // alias: message number - int32_t& m_iTimeStamp; // alias: timestamp - int32_t& m_iID; // alias: destination SRT socket ID char*& m_pcData; // alias: payload (data packet) / control information fields (control packet) + SRTU_PROPERTY_RO(SRTSOCKET, id, SRTSOCKET(m_nHeader[SRT_PH_ID])); + SRTU_PROPERTY_WO_ARG(SRTSOCKET, id, m_nHeader[SRT_PH_ID] = int32_t(arg)); + + SRTU_PROPERTY_RW(int32_t, seqno, m_nHeader[SRT_PH_SEQNO]); + SRTU_PROPERTY_RW(int32_t, msgflags, m_nHeader[SRT_PH_MSGNO]); + SRTU_PROPERTY_RW(int32_t, timestamp, m_nHeader[SRT_PH_TIMESTAMP]); + // Experimental: sometimes these references don't work! char* getData(); char* release(); diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index 9610f04e3..dc7e5b422 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -226,7 +226,7 @@ bool srt::PacketFilter::packControlPacket(int32_t seq, int kflg, CPacket& w_pack // - Crypto // - Message Number // will be set to 0/false - w_packet.m_iMsgNo = SRT_MSGNO_CONTROL | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + w_packet.set_msgflags(SRT_MSGNO_CONTROL | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags w_packet.setMsgCryptoFlags(EncryptionKeySpec(kflg)); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index cf0016901..345151b4e 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -900,7 +900,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst // Need a stub value for a case when there's no unit provided ("storage depleted" case). // It should be normally NOT IN USE because in case of "storage depleted", rst != RST_OK. - const SRTSOCKET dest_id = pkt ? pkt->m_iID : 0; + const SRTSOCKET dest_id = pkt ? pkt->id() : 0; // If no socket were qualified for further handling, finish here. // Otherwise toRemove and toProcess contain items to handle. @@ -1385,7 +1385,7 @@ srt::EReadStatus srt::CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_un if (rst == RST_OK) { - w_id = w_unit->m_Packet.m_iID; + w_id = w_unit->m_Packet.id(); HLOGC(qrlog.Debug, log << "INCOMING PACKET: FROM=" << w_addr.str() << " BOUND=" << m_pChannel->bindAddressAny().str() << " " << w_unit->m_Packet.Info()); diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 3d9cf1c09..9b9288cac 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -1280,6 +1280,7 @@ inline ValueType avg_iir_w(ValueType old_value, ValueType new_value, size_t new_ #define SRTU_PROPERTY_RR(type, name, field) type name() { return field; } #define SRTU_PROPERTY_RO(type, name, field) type name() const { return field; } #define SRTU_PROPERTY_WO(type, name, field) void set_##name(type arg) { field = arg; } +#define SRTU_PROPERTY_WO_ARG(type, name, expr) void set_##name(type arg) { expr; } #define SRTU_PROPERTY_WO_CHAIN(otype, type, name, field) otype& set_##name(type arg) { field = arg; return *this; } #define SRTU_PROPERTY_RW(type, name, field) SRTU_PROPERTY_RO(type, name, field); SRTU_PROPERTY_WO(type, name, field) #define SRTU_PROPERTY_RRW(type, name, field) SRTU_PROPERTY_RR(type, name, field); SRTU_PROPERTY_WO(type, name, field) diff --git a/srtcore/window.h b/srtcore/window.h index 132440368..dbe4b7179 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -244,7 +244,7 @@ class CPktTimeWindow: CPktTimeWindowTools void probeArrival(const CPacket& pkt, bool unordered) { SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); - const int inorder16 = pkt.m_iSeqNo & PUMASK_SEQNO_PROBE; + const int inorder16 = pkt.seqno() & PUMASK_SEQNO_PROBE; // for probe1, we want 16th packet if (inorder16 == 0) @@ -266,7 +266,7 @@ class CPktTimeWindow: CPktTimeWindowTools void probe1Arrival(const CPacket& pkt, bool unordered) { SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); - if (unordered && pkt.m_iSeqNo == m_Probe1Sequence) + if (unordered && pkt.seqno() == m_Probe1Sequence) { // Reset the starting probe into "undefined", when // a packet has come as retransmitted before the @@ -276,7 +276,7 @@ class CPktTimeWindow: CPktTimeWindowTools } m_tsProbeTime = sync::steady_clock::now(); - m_Probe1Sequence = pkt.m_iSeqNo; // Record the sequence where 16th packet probe was taken + m_Probe1Sequence = pkt.seqno(); // Record the sequence where 16th packet probe was taken } /// Record the arrival time of the second probing packet and the interval between packet pairs. @@ -292,7 +292,7 @@ class CPktTimeWindow: CPktTimeWindowTools // expected packet pair, behave as if the 17th packet was lost. // no start point yet (or was reset) OR not very next packet - if (m_Probe1Sequence == SRT_SEQNO_NONE || CSeqNo::incseq(m_Probe1Sequence) != pkt.m_iSeqNo) + if (m_Probe1Sequence == SRT_SEQNO_NONE || CSeqNo::incseq(m_Probe1Sequence) != pkt.seqno()) return; // Grab the current time before trying to acquire diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 0c67fa91c..554c6aae5 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -54,22 +54,25 @@ class CRcvBufferReadMsg EXPECT_NE(unit, nullptr); CPacket& packet = unit->m_Packet; - packet.m_iSeqNo = seqno; - packet.m_iTimeStamp = ts; + packet.set_seqno(seqno); + packet.set_timestamp(ts); packet.setLength(m_payload_sz); - generatePayload(packet.data(), packet.getLength(), packet.m_iSeqNo); + generatePayload(packet.data(), packet.getLength(), packet.seqno()); - packet.m_iMsgNo = msgno; - packet.m_iMsgNo |= PacketBoundaryBits(PB_SUBSEQUENT); + int32_t pktMsgFlags = msgno; + pktMsgFlags |= PacketBoundaryBits(PB_SUBSEQUENT); if (pb_first) - packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST); + pktMsgFlags |= PacketBoundaryBits(PB_FIRST); if (pb_last) - packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST); + pktMsgFlags |= PacketBoundaryBits(PB_LAST); + + if (!out_of_order) + pktMsgFlags |= MSGNO_PACKET_INORDER::wrap(1); + packet.set_msgflags(pktMsgFlags); if (!out_of_order) { - packet.m_iMsgNo |= MSGNO_PACKET_INORDER::wrap(1); EXPECT_TRUE(packet.getMsgOrderFlag()); } diff --git a/test/test_crypto.cpp b/test/test_crypto.cpp index ce68dd32e..f4fa7f614 100644 --- a/test/test_crypto.cpp +++ b/test/test_crypto.cpp @@ -85,9 +85,9 @@ namespace srt const int inorder = 1; const int kflg = m_crypt.getSndCryptoFlags(); - pkt.m_iSeqNo = seqno; - pkt.m_iMsgNo = msgno | inorder | PacketBoundaryBits(PB_SOLO) | MSGNO_ENCKEYSPEC::wrap(kflg);; - pkt.m_iTimeStamp = 356; + pkt.set_seqno(seqno); + pkt.set_msgflags(msgno | inorder | PacketBoundaryBits(PB_SOLO) | MSGNO_ENCKEYSPEC::wrap(kflg)); + pkt.set_timestamp(356); std::iota(pkt.data(), pkt.data() + pld_size, '0'); pkt.setLength(pld_size); @@ -103,7 +103,6 @@ namespace srt // Modify the payload and expect auth to fail. pkt_enc->data()[10] = '5'; EXPECT_EQ(m_crypt.decrypt(*pkt_enc.get()), ENCS_FAILED); - } } // namespace srt diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 602ff6d13..581d406fd 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -809,7 +809,7 @@ TEST_F(TestFECRebuilding, NoRebuild) // - Crypto // - Message Number // will be set to 0/false - fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + fecpkt->set_msgflags(MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0)); @@ -886,7 +886,7 @@ TEST_F(TestFECRebuilding, Rebuild) // - Crypto // - Message Number // will be set to 0/false - fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + fecpkt->set_msgflags(MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0)); @@ -904,7 +904,7 @@ TEST_F(TestFECRebuilding, Rebuild) // Set artificially the SN_REXMIT flag in the skipped source packet // because the rebuilt packet shall have REXMIT flag set. - skipped.m_iMsgNo |= MSGNO_REXMIT::wrap(true); + skipped.set_msgflags(skipped.msgflags() | MSGNO_REXMIT::wrap(true)); // Compare the header EXPECT_EQ(skipped.getHeader()[SRT_PH_SEQNO], rebuilt.hdr[SRT_PH_SEQNO]); From ea8ea9f2c21d513e149b87bf28db9375ab146151 Mon Sep 17 00:00:00 2001 From: yomnes0 <127947185+yomnes0@users.noreply.github.com> Date: Mon, 19 Feb 2024 10:04:36 +0100 Subject: [PATCH 10/51] [build] Moved code coverage testing from travis to github actions (#2874). --- .github/workflows/cxx11-ubuntu.yaml | 9 +++++++-- .travis.yml | 7 +------ CMakeLists.txt | 6 +----- codecov.yml | 4 ++++ 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/cxx11-ubuntu.yaml b/.github/workflows/cxx11-ubuntu.yaml index 500ff1beb..40724d54b 100644 --- a/.github/workflows/cxx11-ubuntu.yaml +++ b/.github/workflows/cxx11-ubuntu.yaml @@ -16,8 +16,13 @@ jobs: - name: configure run: | mkdir _build && cd _build - cmake ../ -DENABLE_STDCXX_SYNC=ON -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON -DENABLE_TESTING=ON -DENABLE_EXAMPLES=ON + cmake ../ -DENABLE_STDCXX_SYNC=ON -DENABLE_ENCRYPTION=ON -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON -DENABLE_TESTING=ON -DENABLE_EXAMPLES=ON -DENABLE_CODE_COVERAGE=ON - name: build run: cd _build && cmake --build ./ - name: test - run: cd _build && ctest --extra-verbose + run: | + cd _build && ctest --extra-verbose + - name: codecov + run: | + source ./scripts/collect-gcov.sh + bash <(curl -s https://codecov.io/bash) diff --git a/.travis.yml b/.travis.yml index 72df01bf1..fafa5e2b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,9 +25,8 @@ matrix: - os: linux env: - BUILD_TYPE=Debug - - BUILD_OPTS='-DENABLE_CODE_COVERAGE=ON -DENABLE_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' + - BUILD_OPTS='-DENABLE_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' - RUN_SONARCUBE=1 - - RUN_CODECOV=1 - env: - BUILD_TYPE=Debug - BUILD_OPTS='-DENABLE_LOGGING=OFF -DUSE_ENCLIB=mbedtls -DENABLE_MONOTONIC_CLOCK=ON -DENABLE_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' @@ -100,10 +99,6 @@ script: test $SUCCESS == 0; fi after_success: - - if (( "$RUN_CODECOV" )); then - source ./scripts/collect-gcov.sh; - bash <(curl -s https://codecov.io/bash); - fi - if (( "$RUN_SONARCUBE" )); then sonar-scanner -D"sonar.cfamily.gcov.reportPath=."; fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 82b0e3cff..a429cf495 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1522,15 +1522,11 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) ) #set_tests_properties(test-srt PROPERTIES RUN_SERIAL TRUE) else() - gtest_add_tests( - TEST_LIST tests_srt - TARGET test-srt - ) set_tests_properties(${tests_srt} PROPERTIES RUN_SERIAL TRUE) + gtest_discover_tests(test-srt) endif() enable_testing() - endif() diff --git a/codecov.yml b/codecov.yml index f91e5c1fe..2716c7f60 100644 --- a/codecov.yml +++ b/codecov.yml @@ -6,3 +6,7 @@ coverage: threshold: null patch: false changes: false +ignore: + - "testing" + - "apps" + - "example" \ No newline at end of file From 99fa92160bb2164025b6003c35b3e77416e6e9a8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 20 Feb 2024 17:21:02 +0100 Subject: [PATCH 11/51] [core] Added getter for the SRTO_MAXREXMITBW socket option. --- srtcore/core.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 905c10457..3835a0f04 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -559,6 +559,15 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) optlen = sizeof(int32_t); break; +#ifdef ENABLE_MAXREXMITBW + case SRTO_MAXREXMITBW: + if (size_t(optlen) < sizeof(m_config.llMaxRexmitBW)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + *(int64_t*)optval = m_config.llMaxRexmitBW; + optlen = sizeof(int64_t); + break; +#endif + case SRTO_STATE: *(int32_t *)optval = uglobal().getStatus(m_SocketID); optlen = sizeof(int32_t); From bed548897962a3822ca08b5fdc76fc41bd5c6474 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 20 Feb 2024 17:21:29 +0100 Subject: [PATCH 12/51] [tests] Added unittest for SRTO_MAXREXMITBW socket option. --- test/test_socket_options.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index 78388bf30..051a17330 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -189,6 +189,9 @@ const OptionTestEntry g_test_matrix_options[] = //SRTO_LINGER { SRTO_LOSSMAXTTL, "SRTO_LOSSMAXTTL", RestrictionType::POST, sizeof(int), 0, INT32_MAX, 0, 10, {} }, //SRTO_MAXBW +#ifdef ENABLE_MAXREXMITBW + { SRTO_MAXREXMITBW, "SRTO_MAXREXMITBW", RestrictionType::POST, sizeof(int64_t), -1ll, INT64_MAX, - 1ll, 200000ll, {-2ll}}, +#endif { SRTO_MESSAGEAPI, "SRTO_MESSAGEAPI", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, //SRTO_MININPUTBW { SRTO_MINVERSION, "SRTO_MINVERSION", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0x010000, 0x010300, {} }, @@ -234,7 +237,7 @@ template void CheckGetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const ValueType& value, const char* desc) { ValueType opt_val; - int opt_len = 0; + int opt_len = entry.opt_len; EXPECT_EQ(srt_getsockopt(sock, 0, entry.optid, &opt_val, &opt_len), SRT_SUCCESS) << "Getting " << entry.optname << " returned error: " << srt_getlasterror_str(); From 07e22cac775c7f4e1abe17b8c2a845bd1f0616ed Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 21 Feb 2024 09:37:52 +0100 Subject: [PATCH 13/51] [tests] Added yielding for tests that spawn a thread for connect (#2883). --- test/test_fec_rebuilding.cpp | 29 ++++++++++++++++++++++++++++- test/test_socket_options.cpp | 3 +++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 581d406fd..185b54e98 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -305,9 +305,12 @@ TEST(TestFEC, Connection) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); - SRTSOCKET la[] = { l }; + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + // Given 2s timeout for accepting as it has occasionally happened with Travis // that 1s might not be enough. + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); @@ -362,6 +365,9 @@ TEST(TestFEC, ConnectionReorder) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); @@ -417,6 +423,9 @@ TEST(TestFEC, ConnectionFull1) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); @@ -472,6 +481,9 @@ TEST(TestFEC, ConnectionFull2) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); @@ -527,6 +539,9 @@ TEST(TestFEC, ConnectionMess) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); @@ -580,6 +595,9 @@ TEST(TestFEC, ConnectionForced) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); @@ -630,6 +648,9 @@ TEST(TestFEC, RejectionConflict) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + EXPECT_EQ(connect_res.get(), SRT_ERROR); EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER); @@ -671,6 +692,9 @@ TEST(TestFEC, RejectionIncompleteEmpty) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + EXPECT_EQ(connect_res.get(), SRT_ERROR); EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER); @@ -716,6 +740,9 @@ TEST(TestFEC, RejectionIncomplete) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + EXPECT_EQ(connect_res.get(), SRT_ERROR); EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER); diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index 051a17330..9f0b2b532 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -70,6 +70,9 @@ class TestSocketOptions }; auto accept_res = async(launch::async, accept_async, m_listen_sock); + // Make sure the thread was kicked + this_thread::yield(); + const int connect_res = Connect(); EXPECT_EQ(connect_res, SRT_SUCCESS); From 6b75145f4918af7dbba27ea2521ed474aa209e97 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 21 Feb 2024 20:49:05 +0100 Subject: [PATCH 14/51] [tests] Prevent socket from being closed twice in EE test (#2886). --- test/test_enforced_encryption.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/test_enforced_encryption.cpp b/test/test_enforced_encryption.cpp index 6fd284c23..717b4549c 100644 --- a/test/test_enforced_encryption.cpp +++ b/test/test_enforced_encryption.cpp @@ -257,8 +257,16 @@ class TestEnforcedEncryption { // Code here will be called just after the test completes. // OK to throw exceptions from here if needed. - EXPECT_NE(srt_close(m_caller_socket), SRT_ERROR) << srt_getlasterror_str(); - EXPECT_NE(srt_close(m_listener_socket), SRT_ERROR) << srt_getlasterror_str(); + + if (m_caller_socket != SRT_INVALID_SOCK) + { + EXPECT_NE(srt_close(m_caller_socket), SRT_ERROR) << srt_getlasterror_str(); + } + + if (m_listener_socket != SRT_INVALID_SOCK) + { + EXPECT_NE(srt_close(m_listener_socket), SRT_ERROR) << srt_getlasterror_str(); + } } @@ -541,6 +549,7 @@ class TestEnforcedEncryption // Just give it some time and close the socket. std::this_thread::sleep_for(std::chrono::milliseconds(50)); ASSERT_NE(srt_close(m_listener_socket), SRT_ERROR); + m_listener_socket = SRT_INVALID_SOCK; // mark closed already accepting_thread.join(); } } From b2d48bdf432d24b2b341273ecb876d7b990ea5e4 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 22 Feb 2024 09:34:57 +0100 Subject: [PATCH 15/51] [core] Fixed POST options setting on a connected group. The list of options was unordered, hence the binary search failed to find those options. --- srtcore/core.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 3835a0f04..4493c397d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -119,6 +119,7 @@ const int UDT::ERROR = srt::CUDT::ERROR; 2[15..0]: TsbPD delay [0..60000] msec */ +// IMPORTANT!!! This array must be ordered by value, because std::binary_search is performed on it! extern const SRT_SOCKOPT srt_post_opt_list [SRT_SOCKOPT_NPOST] = { SRTO_SNDSYN, SRTO_RCVSYN, @@ -127,11 +128,14 @@ extern const SRT_SOCKOPT srt_post_opt_list [SRT_SOCKOPT_NPOST] = { SRTO_RCVTIMEO, SRTO_MAXBW, SRTO_INPUTBW, - SRTO_MININPUTBW, SRTO_OHEADBW, SRTO_SNDDROPDELAY, SRTO_DRIFTTRACER, + SRTO_MININPUTBW, SRTO_LOSSMAXTTL +#ifdef ENABLE_MAXREXMITBW + ,SRTO_MAXREXMITBW +#endif }; const int32_t From 29fda2c1d618133b1daee3807e1ff28dcd235f75 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 22 Feb 2024 13:50:06 +0100 Subject: [PATCH 16/51] [tests] Added SRTO_MAXBW and SRTO_MININPUTBW to unit tests (#2888). --- test/test_socket_options.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index 9f0b2b532..271d84186 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -191,12 +191,12 @@ const OptionTestEntry g_test_matrix_options[] = { SRTO_LATENCY, "SRTO_LATENCY", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 120, 200, {-1} }, //SRTO_LINGER { SRTO_LOSSMAXTTL, "SRTO_LOSSMAXTTL", RestrictionType::POST, sizeof(int), 0, INT32_MAX, 0, 10, {} }, - //SRTO_MAXBW + { SRTO_MAXBW, "SRTO_MAXBW", RestrictionType::POST, sizeof(int64_t), int64_t(-1), INT64_MAX, int64_t(-1), int64_t(200000), {int64_t(-2)}}, #ifdef ENABLE_MAXREXMITBW - { SRTO_MAXREXMITBW, "SRTO_MAXREXMITBW", RestrictionType::POST, sizeof(int64_t), -1ll, INT64_MAX, - 1ll, 200000ll, {-2ll}}, + { SRTO_MAXREXMITBW, "SRTO_MAXREXMITBW", RestrictionType::POST, sizeof(int64_t), int64_t(-1), INT64_MAX, int64_t(-1), int64_t(200000), {int64_t(-2)}}, #endif { SRTO_MESSAGEAPI, "SRTO_MESSAGEAPI", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, - //SRTO_MININPUTBW + { SRTO_MININPUTBW, "SRTO_MININPUTBW", RestrictionType::POST, sizeof(int64_t), int64_t(0), INT64_MAX, int64_t(0), int64_t(200000), {int64_t(-1)}}, { SRTO_MINVERSION, "SRTO_MINVERSION", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0x010000, 0x010300, {} }, { SRTO_MSS, "SRTO_MSS", RestrictionType::PREBIND, sizeof(int), 76, 65536, 1500, 1400, {-1, 0, 75} }, { SRTO_NAKREPORT, "SRTO_NAKREPORT", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, @@ -240,7 +240,7 @@ template void CheckGetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const ValueType& value, const char* desc) { ValueType opt_val; - int opt_len = entry.opt_len; + int opt_len = (int) entry.opt_len; EXPECT_EQ(srt_getsockopt(sock, 0, entry.optid, &opt_val, &opt_len), SRT_SUCCESS) << "Getting " << entry.optname << " returned error: " << srt_getlasterror_str(); From 16cb043d2c0897b620023bc3f4694691ffe86f7e Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 22 Feb 2024 13:29:39 +0100 Subject: [PATCH 17/51] [core] Fixed SRT_SOCKOPT_NPOST when ENABLE_MAXREXMITBW is defined. A follow up fix for #2889. --- srtcore/core.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/srtcore/core.h b/srtcore/core.h index 74b097efd..10746c8c9 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -120,7 +120,12 @@ enum AckDataItem }; const size_t ACKD_FIELD_SIZE = sizeof(int32_t); +#ifdef ENABLE_MAXREXMITBW +static const size_t SRT_SOCKOPT_NPOST = 13; +#else static const size_t SRT_SOCKOPT_NPOST = 12; +#endif + extern const SRT_SOCKOPT srt_post_opt_list []; enum GroupDataItem From 04acbbad93a40a9c258b9c404adce1f96b1dacdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 22 Feb 2024 17:12:57 +0100 Subject: [PATCH 18/51] [core] Fixed the problem of getting option values from groups --- srtcore/api.cpp | 2 +- srtcore/group.cpp | 86 +++++++++++++++++----------- test/test_bonding.cpp | 127 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+), 32 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 554bed9fd..cb9e5e65d 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1394,7 +1394,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i for (size_t i = 0; i < g.m_config.size(); ++i) { HLOGC(aclog.Debug, log << "groupConnect: OPTION @" << sid << " #" << g.m_config[i].so); - error_reason = "setting group-derived option: #" + Sprint(g.m_config[i].so); + error_reason = "group-derived option: #" + Sprint(g.m_config[i].so); ns->core().setOpt(g.m_config[i].so, &g.m_config[i].value[0], (int)g.m_config[i].value.size()); } diff --git a/srtcore/group.cpp b/srtcore/group.cpp index b9b0a0d1c..1539245a0 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -717,6 +717,16 @@ static bool getOptDefault(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) return true; } +struct FOptionValue +{ + SRT_SOCKOPT expected; + FOptionValue(SRT_SOCKOPT v): expected(v) {} + bool operator()(const CUDTGroup::ConfigItem& i) const + { + return i.so == expected; + } +}; + void CUDTGroup::getOpt(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) { // Options handled in group @@ -732,46 +742,60 @@ void CUDTGroup::getOpt(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) w_optlen = sizeof(bool); return; - default:; // pass on - } + case SRTO_SNDTIMEO: + *(int*)pw_optval = m_iSndTimeOut; + w_optlen = sizeof(int); + return; - // XXX Suspicous: may require locking of GlobControlLock - // to prevent from deleting a socket in the meantime. - // Deleting a socket requires removing from the group first, - // so after GroupLock this will be either already NULL or - // a valid socket that will only be closed after time in - // the GC, so this is likely safe like all other API functions. - CUDTSocket* ps = 0; + case SRTO_RCVTIMEO: + *(int*)pw_optval = m_iRcvTimeOut; + w_optlen = sizeof(int); + return; - { - // In sockets. All sockets should have all options - // set the same and should represent the group state - // well enough. If there are no sockets, just use default. + case SRTO_GROUPMINSTABLETIMEO: + *(uint32_t*)pw_optval = m_uOPT_MinStabilityTimeout_us / 1000; + w_optlen = sizeof(uint32_t); + return; - // Group lock to protect the container itself. - // Once a socket is extracted, we state it cannot be - // closed without the group send/recv function or closing - // being involved. - ScopedLock lg(m_GroupLock); - if (m_Group.empty()) - { - if (!getOptDefault(optname, (pw_optval), (w_optlen))) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + // Write-only options for security reasons or + // options that refer to a socket state, that + // makes no sense for a group. + case SRTO_PASSPHRASE: + case SRTO_KMSTATE: + case SRTO_PBKEYLEN: + case SRTO_KMPREANNOUNCE: + case SRTO_KMREFRESHRATE: + case SRTO_BINDTODEVICE: + case SRTO_GROUPCONNECT: + case SRTO_STATE: + case SRTO_EVENT: + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - return; - } + default:; // pass on + } + + // Check if the option is in the storage, which means that + // it was modified on the group. - ps = m_Group.begin()->ps; + vector::const_iterator i = find_if(m_config.begin(), m_config.end(), + FOptionValue(optname)); - // Release the lock on the group, as it's not necessary, - // as well as it might cause a deadlock when combined - // with the others. + if (i == m_config.end()) + { + // Not found, see the defaults + if (!getOptDefault(optname, (pw_optval), (w_optlen))) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + return; } - if (!ps) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + // Found, return the value from the storage. + // Check the size first. + if (w_optlen < int(i->value.size())) + throw CUDTException(MJ_NOTSUP, MN_XSIZE, 0); - return ps->core().getOpt(optname, (pw_optval), (w_optlen)); + w_optlen = i->value.size(); + memcpy((pw_optval), &i->value[0], i->value.size()); } SRT_SOCKSTATUS CUDTGroup::getStatus() diff --git a/test/test_bonding.cpp b/test/test_bonding.cpp index a03b7c5a0..241d26d99 100644 --- a/test/test_bonding.cpp +++ b/test/test_bonding.cpp @@ -330,3 +330,130 @@ TEST(Bonding, CloseGroupAndSocket) listen_promise.wait(); } +TEST(Bonding, Options) +{ + using namespace std; + using namespace srt; + + TestInit srtinit; + + // Create a group + const SRTSOCKET grp = srt_create_group(SRT_GTYPE_BROADCAST); + + // rendezvous shall not be allowed to be set on the group + // XXX actually it is possible, but no one tested it. POSTPONE. + //int yes = 1; + //EXPECT_EQ(srt_setsockflag(grp, SRTO_RENDEZVOUS, &yes, sizeof yes), SRT_ERROR); + +#ifdef SRT_ENABLE_ENCRYPTION + string pass = "longenoughpassword"; + // passphrase should be ok. + EXPECT_NE(srt_setsockflag(grp, SRTO_PASSPHRASE, pass.c_str(), pass.size()), SRT_ERROR); +#endif + + int lat = 500; + EXPECT_NE(srt_setsockflag(grp, SRTO_RCVLATENCY, &lat, sizeof lat), SRT_ERROR); + + mutex mx; + condition_variable latch; + atomic started {false}; + + thread accept_and_close { [&]() { + + unique_lock ux(mx); + + SRTSOCKET lsn = srt_create_socket(); +#ifdef SRT_ENABLE_ENCRYPTION + EXPECT_NE(srt_setsockflag(lsn, SRTO_PASSPHRASE, pass.c_str(), pass.size()), SRT_ERROR); +#endif + int allow = 1; + ASSERT_NE(srt_setsockflag(lsn, SRTO_GROUPCONNECT, &allow, sizeof allow), SRT_ERROR); + sockaddr_any sa = CreateAddr("127.0.0.1", 5555, AF_INET); + ASSERT_NE(srt_bind(lsn, sa.get(), sa.size()), SRT_ERROR); + ASSERT_NE(srt_listen(lsn, 1), SRT_ERROR); + started = true; + + // First wait - until it's let go with accepting + latch.wait(ux); + + sockaddr_any revsa; + SRTSOCKET gs = srt_accept(lsn, revsa.get(), &revsa.len); + ASSERT_NE(gs, SRT_ERROR); + + // Connected, wait to close + latch.wait(ux); + + srt_close(gs); + srt_close(lsn); + }}; + + // Give the thread a chance to start + this_thread::yield(); + + while (!started) + { + // In case of a bad luck, just wait for the thread to + // acquire the mutex before you do + this_thread::sleep_for(chrono::milliseconds(10)); + } + + // Wait for the possibility to connect + { + // Make sure that the thread reached the wait() call. + unique_lock ux(mx); + latch.notify_all(); + } + + // Now the thread is accepting, so we call the connect. + sockaddr_any sa = CreateAddr("127.0.0.1", 5555, AF_INET); + SRTSOCKET member = srt_connect(grp, sa.get(), sa.size()); + + // We've released the mutex and signaled the CV, so accept should proceed now. + // Exit from srt_connect() means also exit from srt_accept(). + + EXPECT_NE(member, SRT_INVALID_SOCK); + + // conenct_res should be a socket + EXPECT_NE(member, 0); // XXX Change to SRT_SOCKID_CONNREQ + + // Now get the option value from the group + + int revlat = -1; + int optsize = sizeof revlat; + EXPECT_NE(srt_getsockflag(grp, SRTO_RCVLATENCY, &revlat, &optsize), SRT_ERROR); + EXPECT_EQ(optsize, sizeof revlat); + EXPECT_EQ(revlat, 500); + + revlat = -1; + optsize = sizeof revlat; + // Expect the same value set on the member socket + EXPECT_NE(srt_getsockflag(member, SRTO_RCVLATENCY, &revlat, &optsize), SRT_ERROR); + EXPECT_EQ(optsize, sizeof revlat); + EXPECT_EQ(revlat, 500); + + // Individual socket option modified on group + int ohead = 12; + optsize = sizeof ohead; + EXPECT_NE(srt_setsockflag(grp, SRTO_OHEADBW, &ohead, optsize), SRT_ERROR); + + // Modifyting a post-option should be possible on a socket + ohead = 11; + optsize = sizeof ohead; + EXPECT_NE(srt_setsockflag(member, SRTO_OHEADBW, &ohead, optsize), SRT_ERROR); + + // But getting the option value should be equal to the group setting + EXPECT_NE(srt_getsockflag(grp, SRTO_OHEADBW, &ohead, &optsize), SRT_ERROR); + EXPECT_EQ(optsize, sizeof ohead); + EXPECT_EQ(ohead, 12); + + // We're done, the thread can close connection and exit + { + // Make sure that the thread reached the wait() call. + std::unique_lock ux(mx); + latch.notify_all(); + } + + accept_and_close.join(); + srt_close(grp); +} + From 12aad6e4396c03cb37a0c4275f069195ea47501d Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 29 Feb 2024 15:56:22 +0100 Subject: [PATCH 19/51] [tests] Improved tests to avoid all-breaking asserts and inter-test mess-up (#2892). --- CMakeLists.txt | 7 +- srtcore/api.cpp | 23 +- srtcore/logging.h | 2 +- test/test_env.h | 12 +- test/test_epoll.cpp | 22 +- test/test_file_transmission.cpp | 23 +- test/test_main.cpp | 14 +- test/test_many_connections.cpp | 14 +- test/test_reuseaddr.cpp | 595 ++++++++++++++++---------------- test/test_sync.cpp | 3 +- 10 files changed, 375 insertions(+), 340 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a429cf495..8951dc46c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,12 @@ if (NOT MICROSOFT) # that ENABLE_DEBUG is set as it should. if (ENABLE_DEBUG EQUAL 2) set (CMAKE_BUILD_TYPE "RelWithDebInfo") - add_definitions(-DNDEBUG) + if (ENABLE_ASSERT) + # Add _DEBUG macro if explicitly requested, to enable SRT_ASSERT(). + add_definitions(-D_DEBUG) + else() + add_definitions(-DNDEBUG) + endif() elseif (ENABLE_DEBUG) # 1, ON, YES, TRUE, Y, or any other non-zero number set (CMAKE_BUILD_TYPE "Debug") diff --git a/srtcore/api.cpp b/srtcore/api.cpp index cb9e5e65d..f9ac6a8d1 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2662,31 +2662,34 @@ void srt::CUDTUnited::checkBrokenSockets() for (sockets_t::iterator j = m_ClosedSockets.begin(); j != m_ClosedSockets.end(); ++j) { + CUDTSocket* ps = j->second; + CUDT& u = ps->core(); + // HLOGC(smlog.Debug, log << "checking CLOSED socket: " << j->first); - if (!is_zero(j->second->core().m_tsLingerExpiration)) + if (!is_zero(u.m_tsLingerExpiration)) { // asynchronous close: - if ((!j->second->core().m_pSndBuffer) || (0 == j->second->core().m_pSndBuffer->getCurrBufSize()) || - (j->second->core().m_tsLingerExpiration <= steady_clock::now())) + if ((!u.m_pSndBuffer) || (0 == u.m_pSndBuffer->getCurrBufSize()) || + (u.m_tsLingerExpiration <= steady_clock::now())) { - HLOGC(smlog.Debug, log << "checkBrokenSockets: marking CLOSED qualified @" << j->second->m_SocketID); - j->second->core().m_tsLingerExpiration = steady_clock::time_point(); - j->second->core().m_bClosing = true; - j->second->m_tsClosureTimeStamp = steady_clock::now(); + HLOGC(smlog.Debug, log << "checkBrokenSockets: marking CLOSED qualified @" << ps->m_SocketID); + u.m_tsLingerExpiration = steady_clock::time_point(); + u.m_bClosing = true; + ps->m_tsClosureTimeStamp = steady_clock::now(); } } // timeout 1 second to destroy a socket AND it has been removed from // RcvUList const steady_clock::time_point now = steady_clock::now(); - const steady_clock::duration closed_ago = now - j->second->m_tsClosureTimeStamp; + const steady_clock::duration closed_ago = now - ps->m_tsClosureTimeStamp; if (closed_ago > seconds_from(1)) { - CRNode* rnode = j->second->core().m_pRNode; + CRNode* rnode = u.m_pRNode; if (!rnode || !rnode->m_bOnList) { HLOGC(smlog.Debug, - log << "checkBrokenSockets: @" << j->second->m_SocketID << " closed " + log << "checkBrokenSockets: @" << ps->m_SocketID << " closed " << FormatDuration(closed_ago) << " ago and removed from RcvQ - will remove"); // HLOGC(smlog.Debug, log << "will unref socket: " << j->first); diff --git a/srtcore/logging.h b/srtcore/logging.h index 608234eab..c17781c24 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -154,7 +154,7 @@ struct SRT_API LogDispatcher LogLevel::type level; static const size_t MAX_PREFIX_SIZE = 32; char prefix[MAX_PREFIX_SIZE+1]; - bool enabled; + srt::sync::atomic enabled; LogConfig* src_config; bool isset(int flg) { return (src_config->flags & flg) != 0; } diff --git a/test/test_env.h b/test/test_env.h index 68ec516b3..e20905351 100644 --- a/test/test_env.h +++ b/test/test_env.h @@ -63,18 +63,26 @@ class TestInit class UniqueSocket { int32_t sock; + std::string lab, f; + int l; public: - UniqueSocket(int32_t s): sock(s) + UniqueSocket(int32_t s, const char* label, const char* file, int line): sock(s) { if (s == -1) throw std::invalid_argument("Invalid socket"); + lab = label; + f = file; + l = line; } - UniqueSocket(): sock(-1) +#define MAKE_UNIQUE_SOCK(name, label, expr) srt::UniqueSocket name (expr, label, __FILE__, __LINE__) + + UniqueSocket(): sock(-1), l(0) { } + void close(); ~UniqueSocket(); operator int32_t() const diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 116978c08..a8a88aa7a 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -66,7 +66,7 @@ TEST(CEPoll, WaitEmptyCall) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int no = 0; @@ -89,7 +89,7 @@ TEST(CEPoll, UWaitEmptyCall) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int no = 0; @@ -112,7 +112,7 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int yes = 1; @@ -146,7 +146,7 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased2) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int yes = 1; @@ -174,7 +174,7 @@ TEST(CEPoll, WrongEpoll_idOnAddUSock) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int no = 0; @@ -197,7 +197,7 @@ TEST(CEPoll, HandleEpollEvent) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); EXPECT_NE(client_sock, SRT_ERROR); const int yes = 1; @@ -258,7 +258,7 @@ TEST(CEPoll, NotifyConnectionBreak) srt::TestInit srtinit; // 1. Prepare client - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int yes SRT_ATR_UNUSED = 1; @@ -280,7 +280,7 @@ TEST(CEPoll, NotifyConnectionBreak) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa_client.sin_addr), 1); // 2. Prepare server - srt::UniqueSocket server_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(server_sock, "server_sock", srt_create_socket()); ASSERT_NE(server_sock, SRT_ERROR); ASSERT_NE(srt_setsockopt(server_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect @@ -372,7 +372,7 @@ TEST(CEPoll, HandleEpollEvent2) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); EXPECT_NE(client_sock, SRT_ERROR); const int yes = 1; @@ -433,7 +433,7 @@ TEST(CEPoll, HandleEpollNoEvent) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); EXPECT_NE(client_sock, SRT_ERROR); const int yes = 1; @@ -483,7 +483,7 @@ TEST(CEPoll, ThreadedUpdate) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); EXPECT_NE(client_sock, SRT_ERROR); const int no = 0; diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 97f9e684a..bfd668ac7 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -25,12 +25,14 @@ #include #include #include +#include //#pragma comment (lib, "ws2_32.lib") TEST(Transmission, FileUpload) { srt::TestInit srtinit; + srtinit.HandlePerTestOptions(); // Generate the source file // We need a file that will contain more data @@ -93,7 +95,7 @@ TEST(Transmission, FileUpload) // Start listener-receiver thread - bool thread_exit = false; + std::atomic thread_exit { false }; auto client = std::thread([&] { @@ -116,12 +118,17 @@ TEST(Transmission, FileUpload) for (;;) { int n = srt_recv(accepted_sock, buf.data(), 1456); - ASSERT_NE(n, SRT_ERROR); + EXPECT_NE(n, SRT_ERROR) << srt_getlasterror_str(); if (n == 0) { std::cerr << "Received 0 bytes, breaking.\n"; break; } + else if (n == -1) + { + std::cerr << "READ FAILED, breaking anyway\n"; + break; + } // Write to file any amount of data received copyfile.write(buf.data(), n); @@ -173,7 +180,7 @@ TEST(Transmission, FileUpload) std::cout << "Sockets closed, joining receiver thread\n"; client.join(); - std::ifstream tarfile("file.target"); + std::ifstream tarfile("file.target", std::ios::in | std::ios::binary); EXPECT_EQ(!!tarfile, true); tarfile.seekg(0, std::ios::end); @@ -182,8 +189,14 @@ TEST(Transmission, FileUpload) std::cout << "Comparing files\n"; // Compare files - tarfile.seekg(0, std::ios::end); - ifile.seekg(0, std::ios::beg); + + // Theoretically it should work if you just rewind to 0, but + // on Windows this somehow doesn't work. + tarfile.close(); + tarfile.open("file.target", std::ios::in | std::ios::binary); + + ifile.close(); + ifile.open("file.source", std::ios::in | std::ios::binary); for (size_t i = 0; i < tar_size; ++i) { diff --git a/test/test_main.cpp b/test/test_main.cpp index cc5acc487..cb3b7c5c5 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -113,6 +113,11 @@ void TestInit::HandlePerTestOptions() { srt_setloglevel(LOG_DEBUG); } + + if (TestEnv::me->OptionPresent("lognote")) + { + srt_setloglevel(LOG_NOTICE); + } } // Copied from ../apps/apputil.cpp, can't really link this file here. @@ -178,7 +183,14 @@ sockaddr_any CreateAddr(const std::string& name, unsigned short port, int pref_f UniqueSocket::~UniqueSocket() { - srt_close(sock); + // Could be closed explicitly + if (sock != -1) + close(); +} + +void UniqueSocket::close() +{ + EXPECT_NE(srt_close(sock), SRT_ERROR) << lab << " CREATED: "<< f << ":" << l; } } diff --git a/test/test_many_connections.cpp b/test/test_many_connections.cpp index 20deccf70..29cd6bdd2 100644 --- a/test/test_many_connections.cpp +++ b/test/test_many_connections.cpp @@ -135,6 +135,8 @@ TEST_F(TestConnection, Multiple) cerr << "Opening " << NSOCK << " connections\n"; + bool overall_test = true; + for (size_t i = 0; i < NSOCK; i++) { m_connections[i] = srt_create_socket(); @@ -145,13 +147,18 @@ TEST_F(TestConnection, Multiple) int conntimeo = 60; srt_setsockflag(m_connections[i], SRTO_CONNTIMEO, &conntimeo, sizeof conntimeo); + SRTSOCKET connres = SRT_INVALID_SOCK; + //cerr << "Connecting #" << i << " to " << sockaddr_any(psa).str() << "...\n"; //cerr << "Connecting to: " << sockaddr_any(psa).str() << endl; - ASSERT_NE(srt_connect(m_connections[i], psa, sizeof lsa), SRT_ERROR); + connres = srt_connect(m_connections[i], psa, sizeof lsa); + EXPECT_NE(connres, SRT_INVALID_SOCK) << "conn #" << i << ": " << srt_getlasterror_str(); + if (connres == SRT_INVALID_SOCK) + overall_test = false; // Set now async sending so that sending isn't blocked int no = 0; - ASSERT_NE(srt_setsockflag(m_connections[i], SRTO_SNDSYN, &no, sizeof no), -1); + EXPECT_NE(srt_setsockflag(m_connections[i], SRTO_SNDSYN, &no, sizeof no), -1); } for (size_t j = 1; j <= 100; j++) @@ -170,6 +177,7 @@ TEST_F(TestConnection, Multiple) EXPECT_FALSE(m_accept_exit) << "AcceptLoop already broken for some reason!"; // Up to this moment the server sock should survive + cerr << "Closing server socket\n"; // Close server socket to break the accept loop EXPECT_EQ(srt_close(m_server_sock), 0); @@ -177,6 +185,8 @@ TEST_F(TestConnection, Multiple) cerr << "Synchronize with the accepting thread\n"; ex.wait(); cerr << "Synchronization done\n"; + + ASSERT_TRUE(overall_test); } diff --git a/test/test_reuseaddr.cpp b/test/test_reuseaddr.cpp index fe9027311..fa92bc635 100644 --- a/test/test_reuseaddr.cpp +++ b/test/test_reuseaddr.cpp @@ -1,5 +1,6 @@ #include #include +#include #ifndef _WIN32 #include #endif @@ -30,7 +31,7 @@ struct AtReturnJoin // iphlp library to be attached to the executable, which is kinda // problematic. Temporarily block tests using this function on Windows. -std::string GetLocalIP(int af = AF_UNSPEC) +static std::string GetLocalIP(int af = AF_UNSPEC) { std::cout << "!!!WARNING!!!: GetLocalIP not supported, test FORCEFULLY passed\n"; return ""; @@ -50,7 +51,7 @@ struct IfAddr } }; -std::string GetLocalIP(int af = AF_UNSPEC) +static std::string GetLocalIP(int af = AF_UNSPEC) { struct ifaddrs * ifa=NULL; void * tmpAddrPtr=NULL; @@ -101,306 +102,364 @@ std::string GetLocalIP(int af = AF_UNSPEC) } #endif -int client_pollid = SRT_ERROR; -SRTSOCKET g_client_sock = SRT_ERROR; - -void clientSocket(std::string ip, int port, bool expect_success) +class ReuseAddr : public srt::Test { - int yes = 1; - int no = 0; - - int family = AF_INET; - std::string famname = "IPv4"; - if (ip.substr(0, 2) == "6.") - { - family = AF_INET6; - ip = ip.substr(2); - famname = "IPv6"; - } + int m_server_pollid = SRT_ERROR; - std::cout << "[T/C] Creating client socket\n"; +protected: - g_client_sock = srt_create_socket(); - ASSERT_NE(g_client_sock, SRT_ERROR); + std::string showEpollContents(const char* label, int* array, int length) + { + std::ostringstream out; + out << label << ":["; + if (length) + { + // Now is at least 1 + out << "@" << array[0]; - ASSERT_NE(srt_setsockopt(g_client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockflag(g_client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); + for (int i = 1; i < length; ++i) + out << " @" << array[i]; + } + out << "]"; + return out.str(); + } - ASSERT_NE(srt_setsockopt(g_client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + struct UniquePollid + { + int pollid = SRT_ERROR; + UniquePollid() + { + pollid = srt_epoll_create(); + } - int epoll_out = SRT_EPOLL_OUT; - srt_epoll_add_usock(client_pollid, g_client_sock, &epoll_out); + ~UniquePollid() + { + srt_epoll_release(pollid); + } - sockaddr_any sa = srt::CreateAddr(ip, port, family); + operator int() const + { + return pollid; + } + }; - std::cout << "[T/C] Connecting to: " << sa.str() << " (" << famname << ")" << std::endl; + void clientSocket(SRTSOCKET client_sock, std::string ip, int port, bool expect_success) + { + using namespace std; - int connect_res = srt_connect(g_client_sock, sa.get(), sa.size()); + int yes = 1; + int no = 0; - if (connect_res == -1) - { - std::cout << "srt_connect: " << srt_getlasterror_str() << std::endl; - } + int family = AF_INET; + string famname = "IPv4"; + if (ip.substr(0, 2) == "6.") + { + family = AF_INET6; + ip = ip.substr(2); + famname = "IPv6"; + } - if (expect_success) - { - EXPECT_NE(connect_res, -1); - if (connect_res == -1) - return; + cout << "[T/C] Setting up client socket\n"; + ASSERT_NE(client_sock, SRT_INVALID_SOCK); + ASSERT_EQ(srt_getsockstate(client_sock), SRTS_INIT); - // Socket readiness for connection is checked by polling on WRITE allowed sockets. + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); + EXPECT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); - if (connect_res != -1) - { - int rlen = 2; - SRTSOCKET read[2]; + UniquePollid client_pollid; + ASSERT_NE(int(client_pollid), SRT_ERROR); - int wlen = 2; - SRTSOCKET write[2]; + int epoll_out = SRT_EPOLL_OUT; + srt_epoll_add_usock(client_pollid, client_sock, &epoll_out); - std::cout << "[T/C] Waiting for connection readiness...\n"; + sockaddr_any sa = srt::CreateAddr(ip, port, family); - EXPECT_NE(srt_epoll_wait(client_pollid, read, &rlen, - write, &wlen, - -1, // -1 is set for debuging purpose. - // in case of production we need to set appropriate value - 0, 0, 0, 0), SRT_ERROR); + cout << "[T/C] Connecting to: " << sa.str() << " (" << famname << ")" << endl; + int connect_res = srt_connect(client_sock, sa.get(), sa.size()); - EXPECT_EQ(rlen, 0); // get exactly one write event without reads - EXPECT_EQ(wlen, 1); // get exactly one write event without reads - EXPECT_EQ(write[0], g_client_sock); // for our client socket + if (connect_res == -1) + { + cout << "srt_connect: " << srt_getlasterror_str() << endl; + } - char buffer[1316] = {1, 2, 3, 4}; - EXPECT_NE(srt_sendmsg(g_client_sock, buffer, sizeof buffer, - -1, // infinit ttl - true // in order must be set to true - ), - SRT_ERROR); + if (expect_success) + { + EXPECT_NE(connect_res, -1); + if (connect_res == -1) + return; + + // Socket readiness for connection is checked by polling on WRITE allowed sockets. + + if (connect_res != -1) + { + int rlen = 2; + SRTSOCKET read[2]; + + int wlen = 2; + SRTSOCKET write[2]; + + cout << "[T/C] Waiting for connection readiness...\n"; + + EXPECT_NE(srt_epoll_wait(client_pollid, read, &rlen, + write, &wlen, + -1, // -1 is set for debuging purpose. + // in case of production we need to set appropriate value + 0, 0, 0, 0), SRT_ERROR) << srt_getlasterror_str(); + + EXPECT_EQ(rlen, 0) << showEpollContents("[T/C] R", read, rlen); // get exactly one write event without reads + EXPECT_EQ(wlen, 1) << showEpollContents("[T/C] W", write, wlen); // get exactly one write event without reads + EXPECT_EQ(write[0], client_sock); // for our client socket + + char buffer[1316] = {1, 2, 3, 4}; + EXPECT_NE(srt_sendmsg(client_sock, buffer, sizeof buffer, + -1, // infinit ttl + true // in order must be set to true + ), + SRT_ERROR); + } + else + { + cout << "[T/C] (NOT TESTING TRANSMISSION - CONNECTION FAILED ALREADY)\n"; + } } else { - std::cout << "[T/C] (NOT TESTING TRANSMISSION - CONNECTION FAILED ALREADY)\n"; + EXPECT_EQ(connect_res, -1); } - } - else - { - EXPECT_EQ(connect_res, -1); + + cout << "[T/C] Client exit\n"; } - std::cout << "[T/C] Client exit\n"; -} + SRTSOCKET prepareServerSocket() + { + SRTSOCKET bindsock = srt_create_socket(); + EXPECT_NE(bindsock, SRT_ERROR); -int server_pollid = SRT_ERROR; + int yes = 1; + int no = 0; -SRTSOCKET prepareSocket() -{ - SRTSOCKET bindsock = srt_create_socket(); - EXPECT_NE(bindsock, SRT_ERROR); + EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); - int yes = 1; - int no = 0; + int epoll_in = SRT_EPOLL_IN; - EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + std::cout << "[T/S] Listener/binder sock @" << bindsock << " added to m_server_pollid\n"; + srt_epoll_add_usock(m_server_pollid, bindsock, &epoll_in); - int epoll_in = SRT_EPOLL_IN; + return bindsock; + } - std::cout << "[T/S] Listener/binder sock @" << bindsock << " added to server_pollid\n"; - srt_epoll_add_usock(server_pollid, bindsock, &epoll_in); + bool bindSocket(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) + { + sockaddr_any sa = srt::CreateAddr(ip, port, AF_INET); - return bindsock; -} + std::string fam = (sa.family() == AF_INET) ? "IPv4" : "IPv6"; -bool bindSocket(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) -{ - sockaddr_any sa = srt::CreateAddr(ip, port, AF_INET); + std::cout << "[T/S] Bind @" << bindsock << " to: " << sa.str() << " (" << fam << ")" << std::endl; - std::string fam = (sa.family() == AF_INET) ? "IPv4" : "IPv6"; + int bind_res = srt_bind(bindsock, sa.get(), sa.size()); - std::cout << "[T/S] Bind @" << bindsock << " to: " << sa.str() << " (" << fam << ")" << std::endl; + std::cout << "[T/S] ... result " << bind_res << " (expected to " + << (expect_success ? "succeed" : "fail") << ")\n"; - int bind_res = srt_bind(bindsock, sa.get(), sa.size()); + if (!expect_success) + { + std::cout << "[T/S] Binding should fail: " << srt_getlasterror_str() << std::endl; + EXPECT_EQ(bind_res, SRT_ERROR); + return false; + } - std::cout << "[T/S] ... result " << bind_res << " (expected to " - << (expect_success ? "succeed" : "fail") << ")\n"; + EXPECT_NE(bind_res, SRT_ERROR); + return true; + } - if (!expect_success) + bool bindListener(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) { - std::cout << "[T/S] Binding should fail: " << srt_getlasterror_str() << std::endl; - EXPECT_EQ(bind_res, SRT_ERROR); - return false; - } + if (!bindSocket(bindsock, ip, port, expect_success)) + return false; - EXPECT_NE(bind_res, SRT_ERROR); - return true; -} + EXPECT_NE(srt_listen(bindsock, SOMAXCONN), SRT_ERROR); -bool bindListener(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) -{ - if (!bindSocket(bindsock, ip, port, expect_success)) - return false; + return true; + } - EXPECT_NE(srt_listen(bindsock, SOMAXCONN), SRT_ERROR); + SRTSOCKET createListener(std::string ip, int port, bool expect_success) + { + std::cout << "[T/S] serverSocket: creating listener socket\n"; - return true; -} + SRTSOCKET bindsock = prepareServerSocket(); -SRTSOCKET createListener(std::string ip, int port, bool expect_success) -{ - std::cout << "[T/S] serverSocket: creating listener socket\n"; + if (!bindListener(bindsock, ip, port, expect_success)) + return SRT_INVALID_SOCK; - SRTSOCKET bindsock = prepareSocket(); + return bindsock; + } - if (!bindListener(bindsock, ip, port, expect_success)) - return SRT_INVALID_SOCK; + SRTSOCKET createBinder(std::string ip, int port, bool expect_success) + { + std::cout << "[T/S] serverSocket: creating binder socket\n"; - return bindsock; -} + SRTSOCKET bindsock = prepareServerSocket(); -SRTSOCKET createBinder(std::string ip, int port, bool expect_success) -{ - std::cout << "[T/S] serverSocket: creating binder socket\n"; + if (!bindSocket(bindsock, ip, port, expect_success)) + { + srt_close(bindsock); + return SRT_INVALID_SOCK; + } - SRTSOCKET bindsock = prepareSocket(); + return bindsock; + } - if (!bindSocket(bindsock, ip, port, expect_success)) + void testAccept(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) { - srt_close(bindsock); - return SRT_INVALID_SOCK; - } + MAKE_UNIQUE_SOCK(client_sock, "[T/C]connect", srt_create_socket()); - return bindsock; -} + auto run = [this, &client_sock, ip, port, expect_success]() { clientSocket(client_sock, ip, port, expect_success); }; -void testAccept(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) -{ + auto launched = std::async(std::launch::async, run); - auto run = [ip, port, expect_success]() { clientSocket(ip, port, expect_success); }; + AtReturnJoin atreturn_join {launched}; - auto launched = std::async(std::launch::async, run); + { // wait for connection from client + int rlen = 2; + SRTSOCKET read[2]; - AtReturnJoin atreturn_join {launched}; + int wlen = 2; + SRTSOCKET write[2]; - { // wait for connection from client - int rlen = 2; - SRTSOCKET read[2]; + std::cout << "[T/S] Wait 10s on E" << m_server_pollid << " for acceptance on @" << bindsock << " ...\n"; - int wlen = 2; - SRTSOCKET write[2]; + EXPECT_NE(srt_epoll_wait(m_server_pollid, + read, &rlen, + write, &wlen, + 10000, // -1 is set for debuging purpose. + // in case of production we need to set appropriate value + 0, 0, 0, 0), SRT_ERROR) << srt_getlasterror_str(); - std::cout << "[T/S] Wait 10s for acceptance on @" << bindsock << " ...\n"; - ASSERT_NE(srt_epoll_wait(server_pollid, - read, &rlen, - write, &wlen, - 10000, // -1 is set for debuging purpose. - // in case of production we need to set appropriate value - 0, 0, 0, 0), SRT_ERROR ); + EXPECT_EQ(rlen, 1) << showEpollContents("[T/S] R", read, rlen); // get exactly one read event without writes + EXPECT_EQ(wlen, 0) << showEpollContents("[T/S] W", write, wlen); // get exactly one read event without writes + ASSERT_EQ(read[0], bindsock); // read event is for bind socket + } + { + sockaddr_any scl; + MAKE_UNIQUE_SOCK(accepted_sock, "[T/S]accept", srt_accept(bindsock, scl.get(), &scl.len)); - ASSERT_EQ(rlen, 1); // get exactly one read event without writes - ASSERT_EQ(wlen, 0); // get exactly one read event without writes - ASSERT_EQ(read[0], bindsock); // read event is for bind socket - } + if (accepted_sock == -1) + { + std::cout << "srt_accept: " << srt_getlasterror_str() << std::endl; + } + EXPECT_NE(accepted_sock.ref(), SRT_INVALID_SOCK); - sockaddr_any scl; + sockaddr_any showacp = (sockaddr*)&scl; + std::cout << "[T/S] Accepted from: " << showacp.str() << std::endl; - SRTSOCKET accepted_sock = srt_accept(bindsock, scl.get(), &scl.len); - if (accepted_sock == -1) - { - std::cout << "srt_accept: " << srt_getlasterror_str() << std::endl; - } - ASSERT_NE(accepted_sock, SRT_INVALID_SOCK); + int epoll_in = SRT_EPOLL_IN; + srt_epoll_add_usock(m_server_pollid, accepted_sock, &epoll_in); // wait for input - sockaddr_any showacp = (sockaddr*)&scl; - std::cout << "[T/S] Accepted from: " << showacp.str() << std::endl; + char buffer[1316]; + { // wait for 1316 packet from client + int rlen = 2; + SRTSOCKET read[2]; - int epoll_in = SRT_EPOLL_IN; - srt_epoll_add_usock(server_pollid, accepted_sock, &epoll_in); // wait for input + int wlen = 2; + SRTSOCKET write[2]; - char buffer[1316]; - { // wait for 1316 packet from client - int rlen = 2; - SRTSOCKET read[2]; + std::cout << "[T/S] Wait for data reception...\n"; - int wlen = 2; - SRTSOCKET write[2]; + EXPECT_NE(srt_epoll_wait(m_server_pollid, + read, &rlen, + write, &wlen, + -1, // -1 is set for debuging purpose. + // in case of production we need to set appropriate value + 0, 0, 0, 0), SRT_ERROR) << srt_getlasterror_str(); - std::cout << "[T/S] Wait for data reception...\n"; - ASSERT_NE(srt_epoll_wait(server_pollid, - read, &rlen, - write, &wlen, - -1, // -1 is set for debuging purpose. - // in case of production we need to set appropriate value - 0, 0, 0, 0), SRT_ERROR ); + EXPECT_EQ(rlen, 1) << showEpollContents("[T/S] R", read, rlen); // get exactly one read event without writes + EXPECT_EQ(wlen, 0) << showEpollContents("[T/S] W", write, wlen); // get exactly one read event without writes + EXPECT_EQ(read[0], accepted_sock.ref()); // read event is for bind socket + } + char pattern[4] = {1, 2, 3, 4}; - ASSERT_EQ(rlen, 1); // get exactly one read event without writes - ASSERT_EQ(wlen, 0); // get exactly one read event without writes - ASSERT_EQ(read[0], accepted_sock); // read event is for bind socket - } + EXPECT_EQ(srt_recvmsg(accepted_sock, buffer, sizeof buffer), + 1316); - char pattern[4] = {1, 2, 3, 4}; + EXPECT_EQ(memcmp(pattern, buffer, sizeof pattern), 0); - ASSERT_EQ(srt_recvmsg(accepted_sock, buffer, sizeof buffer), - 1316); + // XXX There is a possibility that a broken socket can be closed automatically, + // just the srt_close() call would simply return error in case of nonexistent + // socket. Therefore close them both at once; this problem needs to be fixed + // separately. + // + // The test only intends to send one portion of data from the client, so once + // received, the client has nothing more to do and should exit. + std::cout << "[T/S] closing client socket\n"; + client_sock.close(); + std::cout << "[T/S] closing sockets: ACP:@" << accepted_sock << "...\n"; + } + // client_sock closed through UniqueSocket. + // cannot close client_sock after srt_sendmsg because of issue in api.c:2346 - EXPECT_EQ(memcmp(pattern, buffer, sizeof pattern), 0); + std::cout << "[T/S] joining client async \n"; + launched.get(); + } - std::cout << "[T/S] closing sockets: ACP:@" << accepted_sock << " LSN:@" << bindsock << " CLR:@" << g_client_sock << " ...\n"; - ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); - ASSERT_NE(srt_close(g_client_sock), SRT_ERROR); // cannot close g_client_sock after srt_sendmsg because of issue in api.c:2346 + static void shutdownListener(SRTSOCKET bindsock) + { + // Silently ignore. Usually it should have been checked earlier, + // and an invalid sock might be expected in particular tests. + if (bindsock == SRT_INVALID_SOCK) + return; - std::cout << "[T/S] joining client async...\n"; - launched.get(); -} + int yes = 1; + EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_RCVSYN, &yes, sizeof yes), SRT_ERROR); // for async connect + EXPECT_NE(srt_close(bindsock), SRT_ERROR); -void shutdownListener(SRTSOCKET bindsock) -{ - // Silently ignore. Usually it should have been checked earlier, - // and an invalid sock might be expected in particular tests. - if (bindsock == SRT_INVALID_SOCK) - return; + std::chrono::milliseconds check_period (100); + int credit = 400; // 10 seconds + auto then = std::chrono::steady_clock::now(); - int yes = 1; - EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_RCVSYN, &yes, sizeof yes), SRT_ERROR); // for async connect - EXPECT_NE(srt_close(bindsock), SRT_ERROR); + std::cout << "[T/S] waiting for cleanup of @" << bindsock << " up to 10s" << std::endl; + while (srt_getsockstate(bindsock) != SRTS_NONEXIST) + { + std::this_thread::sleep_for(check_period); + --credit; + if (!credit) + break; + } + auto now = std::chrono::steady_clock::now(); + auto dur = std::chrono::duration_cast(now - then); - std::chrono::milliseconds check_period (100); - int credit = 400; // 10 seconds - auto then = std::chrono::steady_clock::now(); + // Keep as single string because this tends to be mixed from 2 threads. + std::ostringstream sout; + sout << "[T/S] @" << bindsock << " dissolved after " + << (dur.count() / 1000.0) << "s" << std::endl; + std::cout << sout.str() << std::flush; - std::cout << "[T/S] waiting for cleanup of @" << bindsock << " up to 10s" << std::endl; - while (srt_getsockstate(bindsock) != SRTS_NONEXIST) - { - std::this_thread::sleep_for(check_period); - --credit; - if (!credit) - break; + EXPECT_NE(credit, 0); } - auto now = std::chrono::steady_clock::now(); - auto dur = std::chrono::duration_cast(now - then); - - // Keep as single string because this tends to be mixed from 2 threads. - std::ostringstream sout; - sout << "[T/S] @" << bindsock << " dissolved after " - << (dur.count() / 1000.0) << "s" << std::endl; - std::cout << sout.str() << std::flush; - EXPECT_NE(credit, 0); -} +private: -TEST(ReuseAddr, SameAddr1) -{ - srt::TestInit srtinit; + void setup() + { + m_server_pollid = srt_epoll_create(); + ASSERT_NE(m_server_pollid, SRT_ERROR); + } - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); + void teardown() + { + (void)srt_epoll_release(m_server_pollid); + m_server_pollid = SRT_ERROR; + } +}; - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); +TEST_F(ReuseAddr, SameAddr1) +{ SRTSOCKET bindsock_1 = createBinder("127.0.0.1", 5000, true); SRTSOCKET bindsock_2 = createListener("127.0.0.1", 5000, true); @@ -413,24 +472,14 @@ TEST(ReuseAddr, SameAddr1) s1.join(); s2.join(); - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, SameAddr2) +TEST_F(ReuseAddr, SameAddr2) { - srt::TestInit srtinit; std::string localip = GetLocalIP(AF_INET); if (localip == "") return; // DISABLE TEST if this doesn't work. - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - SRTSOCKET bindsock_1 = createBinder(localip, 5000, true); SRTSOCKET bindsock_2 = createListener(localip, 5000, true); @@ -445,21 +494,11 @@ TEST(ReuseAddr, SameAddr2) testAccept(bindsock_3, localip, 5000, true); shutdownListener(bindsock_3); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, SameAddrV6) +TEST_F(ReuseAddr, SameAddrV6) { SRTST_REQUIRES(IPv6); - srt::TestInit srtinit; - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); SRTSOCKET bindsock_1 = createBinder("::1", 5000, true); SRTSOCKET bindsock_2 = createListener("::1", 5000, true); @@ -475,26 +514,15 @@ TEST(ReuseAddr, SameAddrV6) testAccept(bindsock_3, "::1", 5000, true); shutdownListener(bindsock_3); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, DiffAddr) +TEST_F(ReuseAddr, DiffAddr) { - srt::TestInit srtinit; std::string localip = GetLocalIP(AF_INET); if (localip == "") return; // DISABLE TEST if this doesn't work. - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - SRTSOCKET bindsock_1 = createBinder("127.0.0.1", 5000, true); SRTSOCKET bindsock_2 = createListener(localip, 5000, true); @@ -505,14 +533,10 @@ TEST(ReuseAddr, DiffAddr) shutdownListener(bindsock_1); shutdownListener(bindsock_2); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, Wildcard) +TEST_F(ReuseAddr, Wildcard) { - srt::TestInit srtinit; #if defined(_WIN32) || defined(CYGWIN) std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n" "Forcing test to pass, PLEASE FIX.\n"; @@ -524,14 +548,6 @@ TEST(ReuseAddr, Wildcard) std::string localip = GetLocalIP(AF_INET); if (localip == "") return; // DISABLE TEST if this doesn't work. - - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - SRTSOCKET bindsock_1 = createListener("0.0.0.0", 5000, true); // Binding a certain address when wildcard is already bound should fail. @@ -541,15 +557,11 @@ TEST(ReuseAddr, Wildcard) shutdownListener(bindsock_1); shutdownListener(bindsock_2); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, Wildcard6) +TEST_F(ReuseAddr, Wildcard6) { SRTST_REQUIRES(IPv6); - srt::TestInit srtinit; #if defined(_WIN32) || defined(CYGWIN) std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n" "Forcing test to pass, PLEASE FIX.\n"; @@ -567,17 +579,10 @@ TEST(ReuseAddr, Wildcard6) // performed there. std::string localip_v4 = GetLocalIP(AF_INET); - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - // This must be obligatory set before binding a socket to "::" int strict_ipv6 = 1; - SRTSOCKET bindsock_1 = prepareSocket(); + SRTSOCKET bindsock_1 = prepareServerSocket(); srt_setsockflag(bindsock_1, SRTO_IPV6ONLY, &strict_ipv6, sizeof strict_ipv6); bindListener(bindsock_1, "::", 5000, true); @@ -601,7 +606,7 @@ TEST(ReuseAddr, Wildcard6) strict_ipv6 = 0; - bindsock_1 = prepareSocket(); + bindsock_1 = prepareServerSocket(); srt_setsockflag(bindsock_1, SRTO_IPV6ONLY, &strict_ipv6, sizeof strict_ipv6); bindListener(bindsock_1, "::", 5000, true); @@ -620,33 +625,23 @@ TEST(ReuseAddr, Wildcard6) shutdownListener(bindsock_1); shutdownListener(bindsock_2); shutdownListener(bindsock_3); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, ProtocolVersion6) +TEST_F(ReuseAddr, ProtocolVersion6) { SRTST_REQUIRES(IPv6); - srt::TestInit srtinit; #if defined(_WIN32) || defined(CYGWIN) std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n" "Forcing test to pass, PLEASE FIX.\n"; return; #endif - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - SRTSOCKET bindsock_1 = createListener("0.0.0.0", 5000, true); // We need a small interception in this one. - // createListener = prepareSocket | bindListener - SRTSOCKET bindsock_2 = prepareSocket(); + // createListener = prepareServerSocket | bindListener + SRTSOCKET bindsock_2 = prepareServerSocket(); { int yes = 1; @@ -659,32 +654,23 @@ TEST(ReuseAddr, ProtocolVersion6) shutdownListener(bindsock_1); shutdownListener(bindsock_2); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, ProtocolVersionFaux6) +TEST_F(ReuseAddr, ProtocolVersionFaux6) { SRTST_REQUIRES(IPv6); - srt::TestInit srtinit; + #if defined(_WIN32) || defined(CYGWIN) std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n" "Forcing test to pass, PLEASE FIX.\n"; return; #endif - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - SRTSOCKET bindsock_1 = createListener("0.0.0.0", 5000, true); // We need a small interception in this one. - // createListener = prepareSocket | bindListener - SRTSOCKET bindsock_2 = prepareSocket(); + // createListener = prepareServerSocket | bindListener + SRTSOCKET bindsock_2 = prepareServerSocket(); { int no = 0; @@ -696,7 +682,4 @@ TEST(ReuseAddr, ProtocolVersionFaux6) shutdownListener(bindsock_1); shutdownListener(bindsock_2); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 844705ea6..e0454a581 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -587,7 +587,8 @@ TEST(SyncEvent, WaitForNotifyAll) /*****************************************************************************/ void* dummythread(void* param) { - *(bool*)(param) = true; + auto& thread_finished = *(srt::sync::atomic*)param; + thread_finished = true; return nullptr; } From 710c2022c94d9f05765fc1b900ad540424ba2ec0 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 6 Mar 2024 15:31:08 +0100 Subject: [PATCH 20/51] [core] Withdrawn changes from #2858 as they break ABI compat (#2899). --- srtcore/api.cpp | 5 ---- srtcore/logging.cpp | 63 --------------------------------------------- srtcore/logging.h | 50 ++++++++++++++++++++++++----------- 3 files changed, 35 insertions(+), 83 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index f9ac6a8d1..a3734a603 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -4656,21 +4656,18 @@ void setloglevel(LogLevel::type ll) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.max_level = ll; - srt_logger_config.updateLoggersState(); } void addlogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, true); - srt_logger_config.updateLoggersState(); } void dellogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, false); - srt_logger_config.updateLoggersState(); } void resetlogfa(set fas) @@ -4678,7 +4675,6 @@ void resetlogfa(set fas) ScopedLock gg(srt_logger_config.mutex); for (int i = 0; i <= SRT_LOGFA_LASTNONE; ++i) srt_logger_config.enabled_fa.set(i, fas.count(i)); - srt_logger_config.updateLoggersState(); } void resetlogfa(const int* fara, size_t fara_size) @@ -4687,7 +4683,6 @@ void resetlogfa(const int* fara, size_t fara_size) srt_logger_config.enabled_fa.reset(); for (const int* i = fara; i != fara + fara_size; ++i) srt_logger_config.enabled_fa.set(*i, true); - srt_logger_config.updateLoggersState(); } void setlogstream(std::ostream& stream) diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index d309b1b8a..ee9270b03 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -23,69 +23,6 @@ using namespace std; namespace srt_logging { -// Note: subscribe() and unsubscribe() functions are being called -// in the global constructor and destructor only, as the -// Logger objects (and inside them also their LogDispatcher) -// are being created. It's not predicted that LogDispatcher -// object are going to be created any other way than as -// global objects. Therefore the construction and destruction -// of them happens always in the main thread. - -void LogConfig::subscribe(LogDispatcher* lg) -{ - vector::iterator p = std::find(loggers.begin(), loggers.end(), lg); - if (p != loggers.end()) - return; // Do not register twice - - loggers.push_back(lg); -} - -void LogConfig::unsubscribe(LogDispatcher* lg) -{ - vector::iterator p = std::find(loggers.begin(), loggers.end(), lg); - if (p != loggers.end()) - { - loggers.erase(p); - } -} - -// This function doesn't have any protection on itself, -// however the API functions from which it is called, call -// it already under a mutex protection. -void LogConfig::updateLoggersState() -{ - for (vector::iterator p = loggers.begin(); - p != loggers.end(); ++p) - { - (*p)->Update(); - } -} - -void LogDispatcher::Update() -{ - bool enabled_in_fa = src_config->enabled_fa[fa]; - enabled = enabled_in_fa && level <= src_config->max_level; -} - - -// SendLogLine can be compiled normally. It's intermediately used by: -// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING -// - PrintLogLine, which has empty body when !ENABLE_LOGGING -void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg) -{ - src_config->lock(); - if ( src_config->loghandler_fn ) - { - (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str()); - } - else if ( src_config->log_stream ) - { - (*src_config->log_stream) << msg; - src_config->log_stream->flush(); - } - src_config->unlock(); -} - #if ENABLE_LOGGING diff --git a/srtcore/logging.h b/srtcore/logging.h index c17781c24..993f8ad2c 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -20,7 +20,6 @@ written by #include #include #include -#include #include #include #ifdef _WIN32 @@ -116,7 +115,6 @@ struct LogConfig void* loghandler_opaque; srt::sync::Mutex mutex; int flags; - std::vector loggers; LogConfig(const fa_bitset_t& efa, LogLevel::type l = LogLevel::warning, @@ -139,10 +137,6 @@ struct LogConfig SRT_ATTR_RELEASE(mutex) void unlock() { mutex.unlock(); } - - void subscribe(LogDispatcher*); - void unsubscribe(LogDispatcher*); - void updateLoggersState(); }; // The LogDispatcher class represents the object that is responsible for @@ -154,7 +148,6 @@ struct SRT_API LogDispatcher LogLevel::type level; static const size_t MAX_PREFIX_SIZE = 32; char prefix[MAX_PREFIX_SIZE+1]; - srt::sync::atomic enabled; LogConfig* src_config; bool isset(int flg) { return (src_config->flags & flg) != 0; } @@ -165,7 +158,6 @@ struct SRT_API LogDispatcher const char* logger_pfx /*[[nullable]]*/, LogConfig& config): fa(functional_area), level(log_level), - enabled(false), src_config(&config) { // XXX stpcpy desired, but not enough portable @@ -193,18 +185,13 @@ struct SRT_API LogDispatcher prefix[MAX_PREFIX_SIZE] = '\0'; #endif } - config.subscribe(this); - Update(); } ~LogDispatcher() { - src_config->unsubscribe(this); } - void Update(); - - bool CheckEnabled() { return enabled; } + bool CheckEnabled(); void CreateLogLinePrefix(std::ostringstream&); void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl); @@ -428,6 +415,22 @@ class Logger } }; +inline bool LogDispatcher::CheckEnabled() +{ + // Don't use enabler caching. Check enabled state every time. + + // These assume to be atomically read, so the lock is not needed + // (note that writing to this field is still mutex-protected). + // It's also no problem if the level was changed at the moment + // when the enabler check is tested here. Worst case, the log + // will be printed just a moment after it was turned off. + const LogConfig* config = src_config; // to enforce using const operator[] + int configured_enabled_fa = config->enabled_fa[fa]; + int configured_maxlevel = config->max_level; + + return configured_enabled_fa && level <= configured_maxlevel; +} + #if HAVE_CXX11 @@ -478,7 +481,24 @@ inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int lin #endif // HAVE_CXX11 +// SendLogLine can be compiled normally. It's intermediately used by: +// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING +// - PrintLogLine, which has empty body when !ENABLE_LOGGING +inline void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg) +{ + src_config->lock(); + if ( src_config->loghandler_fn ) + { + (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str()); + } + else if ( src_config->log_stream ) + { + (*src_config->log_stream) << msg; + (*src_config->log_stream).flush(); + } + src_config->unlock(); } -#endif // INC_SRT_LOGGING_H +} +#endif // INC_SRT_LOGGING_H From 7f490242e4dad2c4247e3cf173703e05bc9b6606 Mon Sep 17 00:00:00 2001 From: yomnes0 <127947185+yomnes0@users.noreply.github.com> Date: Wed, 6 Mar 2024 15:32:06 +0100 Subject: [PATCH 21/51] [build] Add a github action for big endian testing (#2898). --- .github/workflows/s390x-focal.yaml | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/s390x-focal.yaml diff --git a/.github/workflows/s390x-focal.yaml b/.github/workflows/s390x-focal.yaml new file mode 100644 index 000000000..f1b6c7508 --- /dev/null +++ b/.github/workflows/s390x-focal.yaml @@ -0,0 +1,41 @@ +name: QEMU to run s390x-focal + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + Tests: + runs-on: ubuntu-latest + steps: + - name: Setup multiarch/qemu-user-static + run: | + docker run --rm --privileged multiarch/qemu-user-static:register --reset + - name: ubuntu-core:s390x-focal + uses: docker://multiarch/ubuntu-core:s390x-focal + with: + args: > + bash -c + "uname -a && + lscpu | grep Endian + " + - name: Checkout + uses: actions/checkout@v3 + - name: configure + uses: docker://multiarch/ubuntu-core:s390x-focal + with: + args: > + bash -c + "apt-get -y update && + export DEBIAN_FRONTEND=noninteractive && + export TZ=Etc/UTC && + apt-get -y install tzdata && + uname -a && + lscpu | grep Endian && + apt-get -y install cmake g++ libssl-dev git && + mkdir _build && cd _build && + cmake ../ -DENABLE_ENCRYPTION=ON -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON -DENABLE_TESTING=ON -DENABLE_EXAMPLES=ON && + cmake --build ./ && + ./test-srt -disable-ipv6" From 618ddfed45a53d99e4a0ce8528b2ecfe2860b5bc Mon Sep 17 00:00:00 2001 From: Tommy Wu <7903172+tommyvct@users.noreply.github.com> Date: Thu, 7 Mar 2024 03:45:03 -0600 Subject: [PATCH 22/51] [build] Fix: link bcrypt on Windows when mbedtls >=v3.5.0 (#2860). Only link to bcrypt if mbedtls is not found by find_package(mbedtls). --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8951dc46c..c5994d3b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -365,11 +365,11 @@ if (ENABLE_ENCRYPTION) set (SSL_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) set (SSL_LIBRARIES ${MBEDTLS_LIBRARIES}) endif() + if (WIN32) + set (SSL_LIBRARIES ${SSL_LIBRARIES} bcrypt) + endif() if ("${SSL_LIBRARIES}" STREQUAL "") set (SSL_LIBRARIES mbedtls mbedcrypto) - if (WIN32) - set (SSL_LIBRARIES ${SSL_LIBRARIES} bcrypt) - endif() endif() message(STATUS "SSL enforced mbedtls: -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") From 9b8aaa6a1bea8adb2f55bf7ea898857be71c229f Mon Sep 17 00:00:00 2001 From: yomnes0 <127947185+yomnes0@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:51:11 +0100 Subject: [PATCH 23/51] [core] Fix some warnings when compiling with MinGW (#2868). Removed duplicate declaration of SRT_API. Moved around variables to avoid set but not used warnings. Removed SRT_API attribute of SysStrError. --- srtcore/channel.cpp | 15 +++++++------- srtcore/platform_sys.h | 14 +++++++++++++ srtcore/srt.h | 46 +++++++++++++++++------------------------- srtcore/srt_compat.c | 1 - srtcore/srt_compat.h | 45 +---------------------------------------- 5 files changed, 41 insertions(+), 80 deletions(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 16ccc8c1b..557be8fd7 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -51,7 +51,6 @@ modified by *****************************************************************************/ #include "platform_sys.h" - #include #include // Logging #include @@ -189,6 +188,7 @@ void srt::CChannel::createSocket(int family) m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); cloexec_flag = true; #endif + #else // ENABLE_SOCK_CLOEXEC m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); #endif // ENABLE_SOCK_CLOEXEC @@ -197,17 +197,18 @@ void srt::CChannel::createSocket(int family) throw CUDTException(MJ_SETUP, MN_NONE, NET_ERROR); #if ENABLE_SOCK_CLOEXEC -#ifdef _WIN32 - // XXX ::SetHandleInformation(hInputWrite, HANDLE_FLAG_INHERIT, 0) -#else + if (cloexec_flag) { +#ifdef _WIN32 + // XXX ::SetHandleInformation(hInputWrite, HANDLE_FLAG_INHERIT, 0) +#else if (0 != set_cloexec(m_iSocket, 1)) { throw CUDTException(MJ_SETUP, MN_NONE, NET_ERROR); } +#endif //_WIN32 } -#endif #endif // ENABLE_SOCK_CLOEXEC if ((m_mcfg.iIpV6Only != -1) && (family == AF_INET6)) // (not an error if it fails) @@ -795,8 +796,8 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka { if (NET_ERROR == WSA_IO_PENDING) { - res = WSAWaitForMultipleEvents(1, &m_SendOverlapped.hEvent, TRUE, 100 /*ms*/, FALSE); - if (res == WAIT_FAILED) + DWORD res_wait = WSAWaitForMultipleEvents(1, &m_SendOverlapped.hEvent, TRUE, 100 /*ms*/, FALSE); + if (res_wait == WAIT_FAILED) { LOGC(kslog.Warn, log << "CChannel::WSAWaitForMultipleEvents: failed with " << NET_ERROR); res = -1; diff --git a/srtcore/platform_sys.h b/srtcore/platform_sys.h index e2f0aa4d9..83763e5ea 100644 --- a/srtcore/platform_sys.h +++ b/srtcore/platform_sys.h @@ -21,6 +21,20 @@ // // SRT_IMPORT_TIME (mach time on Mac, portability gettimeofday on WIN32) // SRT_IMPORT_EVENT (includes kevent on Mac) +#ifdef _WIN32 + #ifndef __MINGW32__ + // Explicitly define 32-bit and 64-bit numbers + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int32 uint32_t; + #ifndef LEGACY_WIN32 + typedef unsigned __int64 uint64_t; + #else + // VC 6.0 does not support unsigned __int64: may cause potential problems. + typedef __int64 uint64_t; + #endif + #endif +#endif #ifdef _WIN32 diff --git a/srtcore/srt.h b/srtcore/srt.h index 53b6fd274..3c60f3086 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -16,10 +16,28 @@ written by #ifndef INC_SRTC_H #define INC_SRTC_H +#ifndef SRT_API +#ifdef _WIN32 + #ifdef SRT_DYNAMIC + #ifdef SRT_EXPORTS + #define SRT_API __declspec(dllexport) + #else + #define SRT_API __declspec(dllimport) + #endif + #else // !SRT_DYNAMIC + #define SRT_API + #endif +#else + #define SRT_API __attribute__ ((visibility("default"))) +#endif +#endif + #include "version.h" #include "platform_sys.h" +#include "srt_compat.h" + #include #include @@ -33,34 +51,6 @@ written by //if compiling with MinGW, it only works on XP or above //use -D_WIN32_WINNT=0x0501 - -#ifdef _WIN32 - #ifndef __MINGW32__ - // Explicitly define 32-bit and 64-bit numbers - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int32 uint32_t; - #ifndef LEGACY_WIN32 - typedef unsigned __int64 uint64_t; - #else - // VC 6.0 does not support unsigned __int64: may cause potential problems. - typedef __int64 uint64_t; - #endif - #endif - #ifdef SRT_DYNAMIC - #ifdef SRT_EXPORTS - #define SRT_API __declspec(dllexport) - #else - #define SRT_API __declspec(dllimport) - #endif - #else // !SRT_DYNAMIC - #define SRT_API - #endif -#else - #define SRT_API __attribute__ ((visibility("default"))) -#endif - - // For feature tests if you need. // You can use these constants with SRTO_MINVERSION option. #define SRT_VERSION_FEAT_HSv5 0x010300 diff --git a/srtcore/srt_compat.c b/srtcore/srt_compat.c index fbf4859ae..bbdf7f795 100644 --- a/srtcore/srt_compat.c +++ b/srtcore/srt_compat.c @@ -17,7 +17,6 @@ written by // Prevents from misconfiguration through preprocessor. #include "platform_sys.h" - #include #include diff --git a/srtcore/srt_compat.h b/srtcore/srt_compat.h index 960c1b85a..d4fd2361e 100644 --- a/srtcore/srt_compat.h +++ b/srtcore/srt_compat.h @@ -20,55 +20,12 @@ written by #include #include -#ifndef SRT_API -#ifdef _WIN32 - #ifndef __MINGW32__ - #ifdef SRT_DYNAMIC - #ifdef SRT_EXPORTS - #define SRT_API __declspec(dllexport) - #else - #define SRT_API __declspec(dllimport) - #endif - #else - #define SRT_API - #endif - #else - #define SRT_API - #endif -#else - #define SRT_API __attribute__ ((visibility("default"))) -#endif -#endif - -#ifdef _WIN32 - // https://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx - // printf() Format for ssize_t - #if !defined(PRIzd) - #define PRIzd "Id" - #endif - // printf() Format for size_t - #if !defined(PRIzu) - #define PRIzu "Iu" - #endif -#else - // http://www.gnu.org/software/libc/manual/html_node/Integer-Conversions.html - // printf() Format for ssize_t - #if !defined(PRIzd) - #define PRIzd "zd" - #endif - // printf() Format for size_t - #if !defined(PRIzu) - #define PRIzu "zu" - #endif -#endif - - #ifdef __cplusplus extern "C" { #endif /* Ensures that we store the error in the buffer and return the bufer. */ -SRT_API const char * SysStrError(int errnum, char * buf, size_t buflen); +const char * SysStrError(int errnum, char * buf, size_t buflen); #ifdef __cplusplus } // extern C From 2c3f6f97d650e9903a982e46bb29b8641a416dc4 Mon Sep 17 00:00:00 2001 From: yomnes0 <127947185+yomnes0@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:32:49 +0100 Subject: [PATCH 24/51] [core] Fix some indirect inclusion issues (#2906). * Removed unnecessary include of srt_compat.h from srt.h. * Removed include of srt_compat.h and directly added it where it belongs. * Added srt_compat.h include to srt-test-live. * Added cstddef include to packet.cpp. --- apps/srt-live-transmit.cpp | 2 +- apps/srt-tunnel.cpp | 1 + srtcore/common.cpp | 2 -- srtcore/logging.cpp | 2 +- srtcore/logging.h | 1 - srtcore/packet.cpp | 2 +- srtcore/srt.h | 2 -- testing/srt-test-live.cpp | 1 + 8 files changed, 5 insertions(+), 8 deletions(-) diff --git a/apps/srt-live-transmit.cpp b/apps/srt-live-transmit.cpp index a4ed93a29..0a918e6b0 100644 --- a/apps/srt-live-transmit.cpp +++ b/apps/srt-live-transmit.cpp @@ -65,7 +65,7 @@ #include #include - +#include "srt_compat.h" #include "apputil.hpp" // CreateAddr #include "uriparser.hpp" // UriParser #include "socketoptions.hpp" diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index 569a0c26e..90bd43eae 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -27,6 +27,7 @@ #include #include +#include "srt_compat.h" #include "apputil.hpp" // CreateAddr #include "uriparser.hpp" // UriParser #include "socketoptions.hpp" diff --git a/srtcore/common.cpp b/srtcore/common.cpp index fe5217dfd..6d747ecaa 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -68,8 +68,6 @@ modified by #include "packet.h" #include "threadname.h" -#include // SysStrError - using namespace std; using namespace srt::sync; using namespace srt_logging; diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index ee9270b03..d0ba3fd4a 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -14,7 +14,7 @@ written by *****************************************************************************/ - +#include "srt_compat.h" #include "logging.h" using namespace std; diff --git a/srtcore/logging.h b/srtcore/logging.h index 993f8ad2c..3abfe0326 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -33,7 +33,6 @@ written by #include "utilities.h" #include "threadname.h" #include "logging_api.h" -#include "srt_compat.h" #include "sync.h" #ifdef __GNUC__ diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index b41ed6b9b..668c4b5b3 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -159,7 +159,7 @@ modified by // the original sequence numbers in the field. #include "platform_sys.h" - +#include #include #include "packet.h" #include "handshake.h" diff --git a/srtcore/srt.h b/srtcore/srt.h index 3c60f3086..614a85aea 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -36,8 +36,6 @@ written by #include "platform_sys.h" -#include "srt_compat.h" - #include #include diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index c4c752667..17f4020dc 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -64,6 +64,7 @@ #include #include +#include "srt_compat.h" #include "apputil.hpp" #include "uriparser.hpp" // UriParser #include "socketoptions.hpp" From 03aa5e41125169733558854aa7a527f73c2adcc6 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 8 Mar 2024 14:50:16 +0100 Subject: [PATCH 25/51] [core] Formal errors fixed (#2907). --- apps/verbose.hpp | 2 +- srtcore/logging.h | 14 +++++----- srtcore/utilities.h | 3 ++- testing/srt-test-mpbond.cpp | 7 ++--- testing/srt-test-relay.cpp | 1 + testing/testactivemedia.cpp | 3 +++ testing/testactivemedia.hpp | 12 ++++----- testing/testmedia.hpp | 52 ++++++++++++++++++------------------- 8 files changed, 49 insertions(+), 45 deletions(-) diff --git a/apps/verbose.hpp b/apps/verbose.hpp index 10591888b..879d54086 100644 --- a/apps/verbose.hpp +++ b/apps/verbose.hpp @@ -80,7 +80,7 @@ inline void Print(Log& ) {} template inline void Print(Log& out, Arg1&& arg1, Args&&... args) { - out << arg1; + out << std::forward(arg1); Print(out, args...); } diff --git a/srtcore/logging.h b/srtcore/logging.h index 3abfe0326..2ec5f46aa 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -203,16 +203,16 @@ struct SRT_API LogDispatcher template void PrintLogLine(const char* file, int line, const std::string& area, Args&&... args); - template - void operator()(Arg1&& arg1, Args&&... args) + template + void operator()(Args&&... args) { - PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", arg1, args...); + PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", args...); } - template - void printloc(const char* file, int line, const std::string& area, Arg1&& arg1, Args&&... args) + template + void printloc(const char* file, int line, const std::string& area, Args&&... args) { - PrintLogLine(file, line, area, arg1, args...); + PrintLogLine(file, line, area, args...); } #else template @@ -440,7 +440,7 @@ inline void PrintArgs(std::ostream&) {} template inline void PrintArgs(std::ostream& serr, Arg1&& arg1, Args&&... args) { - serr << arg1; + serr << std::forward(arg1); PrintArgs(serr, args...); } diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 9b9288cac..8a1374eb7 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -35,6 +35,7 @@ written by #include #include #include +#include #if HAVE_CXX11 #include @@ -575,7 +576,7 @@ inline Stream& Print(Stream& in) { return in;} template inline Stream& Print(Stream& sout, Arg1&& arg1, Args&&... args) { - sout << arg1; + sout << std::forward(arg1); return Print(sout, args...); } diff --git a/testing/srt-test-mpbond.cpp b/testing/srt-test-mpbond.cpp index 03066363a..bf9739675 100644 --- a/testing/srt-test-mpbond.cpp +++ b/testing/srt-test-mpbond.cpp @@ -42,6 +42,10 @@ srt_logging::Logger applog(SRT_LOGFA_APP, srt_logger_config, "srt-mpbond"); +using namespace srt; +using namespace std; + + volatile bool mpbond_int_state = false; void OnINT_SetIntState(int) { @@ -49,9 +53,6 @@ void OnINT_SetIntState(int) mpbond_int_state = true; } -using namespace srt; - - int main( int argc, char** argv ) { // This is mainly required on Windows to initialize the network system, diff --git a/testing/srt-test-relay.cpp b/testing/srt-test-relay.cpp index dfabef62b..45d657128 100755 --- a/testing/srt-test-relay.cpp +++ b/testing/srt-test-relay.cpp @@ -45,6 +45,7 @@ written by #include "threadname.h" +using namespace std; bool Upload(UriParser& srt, UriParser& file); diff --git a/testing/testactivemedia.cpp b/testing/testactivemedia.cpp index 7d062f49d..96344f0b2 100644 --- a/testing/testactivemedia.cpp +++ b/testing/testactivemedia.cpp @@ -1,6 +1,9 @@ #include "testactivemedia.hpp" +using namespace std; + + void SourceMedium::Runner() { srt::ThreadName::set("SourceRN"); diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp index f4bc360ba..011dcbfe7 100644 --- a/testing/testactivemedia.hpp +++ b/testing/testactivemedia.hpp @@ -59,7 +59,7 @@ struct Medium std::ostringstream tns; tns << typeid(*this).name() << ":" << this; srt::ThreadName tn(tns.str()); - thr = thread( [this] { RunnerBase(); } ); + thr = std::thread( [this] { RunnerBase(); } ); } void quit() @@ -89,12 +89,12 @@ struct Medium if (Verbose::on) Verb() << VerbLock << "Medium " << this << " exited with Transmission Error:\n\t" << e.what(); else - cerr << "Transmission Error: " << e.what() << endl; + std::cerr << "Transmission Error: " << e.what() << std::endl; } catch (...) { if (Verbose::on) Verb() << VerbLock << "Medium " << this << " exited with UNKNOWN EXCEPTION:"; else - cerr << "UNKNOWN EXCEPTION on medium\n"; + std::cerr << "UNKNOWN EXCEPTION on medium\n"; } } @@ -150,7 +150,7 @@ struct TargetMedium: Medium bool Schedule(const MediaPacket& data) { LOGP(applog.Debug, "TargetMedium::Schedule LOCK ... "); - lock_guard lg(buffer_lock); + std::lock_guard lg(buffer_lock); LOGP(applog.Debug, "TargetMedium::Schedule LOCKED - checking: running=", running, " interrupt=", ::transmit_int_state); if (!running || ::transmit_int_state) { @@ -166,13 +166,13 @@ struct TargetMedium: Medium void Clear() { - lock_guard lg(buffer_lock); + std::lock_guard lg(buffer_lock); buffer.clear(); } void Interrupt() { - lock_guard lg(buffer_lock); + std::lock_guard lg(buffer_lock); running = false; ready.notify_one(); } diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index 337f5f365..be72471d1 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -29,8 +29,6 @@ extern std::atomic transmit_int_state; extern std::shared_ptr transmit_stats_writer; -using namespace std; - const srt_logging::LogFA SRT_LOGFA_APP = 10; extern srt_logging::Logger applog; @@ -57,7 +55,7 @@ class SrtCommon struct ConnectionBase { - string host; + std::string host; int port; int weight = 0; SRTSOCKET socket = SRT_INVALID_SOCK; @@ -65,7 +63,7 @@ class SrtCommon srt::sockaddr_any target; int token = -1; - ConnectionBase(string h, int p): host(h), port(p), source(AF_INET) {} + ConnectionBase(std::string h, int p): host(h), port(p), source(AF_INET) {} }; struct Connection: ConnectionBase @@ -76,7 +74,7 @@ class SrtCommon int error = SRT_SUCCESS; int reason = SRT_REJ_UNKNOWN; - Connection(string h, int p): ConnectionBase(h, p) {} + Connection(std::string h, int p): ConnectionBase(h, p) {} Connection(Connection&& old): ConnectionBase(old) { #if ENABLE_BONDING @@ -101,14 +99,14 @@ class SrtCommon int m_timeout = 0; //< enforces using SRTO_SNDTIMEO or SRTO_RCVTIMEO, depending on @a m_direction bool m_tsbpdmode = true; int m_outgoing_port = 0; - string m_mode; - string m_adapter; - map m_options; // All other options, as provided in the URI - vector m_group_nodes; - string m_group_type; - string m_group_config; + std::string m_mode; + std::string m_adapter; + std::map m_options; // All other options, as provided in the URI + std::vector m_group_nodes; + std::string m_group_type; + std::string m_group_config; #if ENABLE_BONDING - vector m_group_data; + std::vector m_group_data; #ifdef SRT_OLD_APP_READER int32_t m_group_seqno = -1; @@ -117,7 +115,7 @@ class SrtCommon int32_t sequence; bytevector packet; }; - map m_group_positions; + std::map m_group_positions; SRTSOCKET m_group_active; // The link from which the last packet was delivered #endif #endif @@ -131,8 +129,8 @@ class SrtCommon void UpdateGroupStatus(const SRT_SOCKGROUPDATA* grpdata, size_t grpdata_size); public: - void InitParameters(string host, string path, map par); - void PrepareListener(string host, int port, int backlog); + void InitParameters(std::string host, std::string path, std::map par); + void PrepareListener(std::string host, int port, int backlog); void StealFrom(SrtCommon& src); void AcceptNewClient(); @@ -150,22 +148,22 @@ class SrtCommon protected: - void Error(string src, int reason = SRT_REJ_UNKNOWN, int force_result = 0); - void Init(string host, int port, string path, map par, SRT_EPOLL_OPT dir); + void Error(std::string src, int reason = SRT_REJ_UNKNOWN, int force_result = 0); + void Init(std::string host, int port, std::string path, std::map par, SRT_EPOLL_OPT dir); int AddPoller(SRTSOCKET socket, int modes); virtual int ConfigurePost(SRTSOCKET sock); virtual int ConfigurePre(SRTSOCKET sock); - void OpenClient(string host, int port); + void OpenClient(std::string host, int port); #if ENABLE_BONDING void OpenGroupClient(); #endif void PrepareClient(); void SetupAdapter(const std::string& host, int port); - void ConnectClient(string host, int port); - void SetupRendezvous(string adapter, string host, int port); + void ConnectClient(std::string host, int port); + void SetupRendezvous(std::string adapter, std::string host, int port); - void OpenServer(string host, int port, int backlog = 1) + void OpenServer(std::string host, int port, int backlog = 1) { PrepareListener(host, port, backlog); if (transmit_accept_hook_fn) @@ -175,7 +173,7 @@ class SrtCommon AcceptNewClient(); } - void OpenRendezvous(string adapter, string host, int port) + void OpenRendezvous(std::string adapter, std::string host, int port) { PrepareClient(); SetupRendezvous(adapter, host, port); @@ -284,11 +282,11 @@ class SrtModel: public SrtCommon public: bool is_caller = false; bool is_rend = false; - string m_host; + std::string m_host; int m_port = 0; - SrtModel(string host, int port, map par); + SrtModel(std::string host, int port, std::map par); void Establish(std::string& w_name); void Close() @@ -320,7 +318,7 @@ class UdpSource: public virtual Source, public virtual UdpCommon bool eof = true; public: - UdpSource(string host, int port, const map& attr); + UdpSource(std::string host, int port, const std::map& attr); MediaPacket Read(size_t chunk) override; @@ -331,7 +329,7 @@ class UdpSource: public virtual Source, public virtual UdpCommon class UdpTarget: public virtual Target, public virtual UdpCommon { public: - UdpTarget(string host, int port, const map& attr); + UdpTarget(std::string host, int port, const std::map& attr); void Write(const MediaPacket& data) override; bool IsOpen() override { return m_sock != -1; } @@ -341,7 +339,7 @@ class UdpTarget: public virtual Target, public virtual UdpCommon class UdpRelay: public Relay, public UdpSource, public UdpTarget { public: - UdpRelay(string host, int port, const map& attr): + UdpRelay(std::string host, int port, const std::map& attr): UdpSource(host, port, attr), UdpTarget(host, port, attr) { From 0a046d4c2072a5e1f8f02b8883934824a550f1d8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 12 Mar 2024 11:44:02 +0100 Subject: [PATCH 26/51] [build] Migrate SonarCloud to GitHub Action (#2910). --- .github/workflows/cxx11-ubuntu.yaml | 15 ++++++++++++--- .travis.yml | 21 +-------------------- sonar-project.properties | 6 +++--- 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/.github/workflows/cxx11-ubuntu.yaml b/.github/workflows/cxx11-ubuntu.yaml index 40724d54b..d52afd31b 100644 --- a/.github/workflows/cxx11-ubuntu.yaml +++ b/.github/workflows/cxx11-ubuntu.yaml @@ -5,20 +5,23 @@ on: branches: [ master ] pull_request: branches: [ master ] - + types: [opened, synchronize, reopened] jobs: build: name: ubuntu runs-on: ubuntu-20.04 - + env: + BUILD_WRAPPER_OUT_DIR: sonar-output # Directory where build-wrapper output will be placed steps: - uses: actions/checkout@v3 + - name: Install sonar-scanner and build-wrapper + uses: sonarsource/sonarcloud-github-c-cpp@v2 - name: configure run: | mkdir _build && cd _build cmake ../ -DENABLE_STDCXX_SYNC=ON -DENABLE_ENCRYPTION=ON -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON -DENABLE_TESTING=ON -DENABLE_EXAMPLES=ON -DENABLE_CODE_COVERAGE=ON - name: build - run: cd _build && cmake --build ./ + run: cd _build && build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build . - name: test run: | cd _build && ctest --extra-verbose @@ -26,3 +29,9 @@ jobs: run: | source ./scripts/collect-gcov.sh bash <(curl -s https://codecov.io/bash) + - name: Run SonarCloud Scan for C and C++ + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + # Consult https://docs.sonarcloud.io/advanced-setup/ci-based-analysis/sonarscanner-cli/ for more information and options. + run: sonar-scanner --define sonar.cfamily.build-wrapper-output=_build/"${{ env.BUILD_WRAPPER_OUT_DIR }}" diff --git a/.travis.yml b/.travis.yml index fafa5e2b8..94d5dac3a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,10 +11,6 @@ addons: - build-essential - libmbedtls-dev - gdb - sonarcloud: - organization: "haivision" - token: - secure: "wJZC0kyyjuf4SZyonZ6p/5Ga9asEqSnKWF9NpRbu6S6ceERO7vbebuSJF5qX3A6ivPuw0TTk5WASOdnvIyfA28FU/D0MWRdH8K7T3w77wdE9EgAEYTUXzdrbzJY18+9pxjljHwWXWALPSGf3MClg4irWrdk1e6uHK+68R39+ZvBGBFpWeeZy/+at9+xwhtAGKBlSHe8zc+3wPxuYdvviLVJ25qbpNmnzkUR0X89G+UBl90raCPSN32EHFdImHZ5DxfEQQJgZFRjzQUY4EW/iYwaMel7jufAq0ClgV4psKujl9Lz8cPqx3WgqRfJyiIthOMTsac7G4zAw8LK2CI0VsssBp0JalLXaumi6vG7o6c3rIwKckzSKccq3pHa7h45praIVVn9s3nq+Q/JGA11FMkKQxdQtmwgFsLhbi6ZxabgsUi5KtWoWY2z6MgpJuROuAjNxZi9XJzUoJs7zSTUtRRW7V8Q2lRiOnknYh25N6TCA5bpyy1EZmRdJErm071YNI9P01gbFz5137FWJFiJzro9TGF0KoHSGiCIdUt3WlMzwr/i/wFLxFBQOZQ2rjTXvhs4hxONxMZV3gzxA1NdLaf9i5Mh6jxVMV+ujaRSV7JmPGzxqiAlpT9cJUhTCYuar9diLLeDrpe7RawEZR8V1xVDQ7yT8ruDNQ78VbSn/sC0=" homebrew: update: false packages: @@ -26,7 +22,6 @@ matrix: env: - BUILD_TYPE=Debug - BUILD_OPTS='-DENABLE_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' - - RUN_SONARCUBE=1 - env: - BUILD_TYPE=Debug - BUILD_OPTS='-DENABLE_LOGGING=OFF -DUSE_ENCLIB=mbedtls -DENABLE_MONOTONIC_CLOCK=ON -DENABLE_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' @@ -80,17 +75,7 @@ script: export PKG_CONFIG_PATH=$(brew --prefix openssl)"/lib/pkgconfig"; cmake . -DCMAKE_BUILD_TYPE=$BUILD_TYPE $BUILD_OPTS -DENABLE_UNITTESTS="ON"; fi - - echo "TRAVIS_REPO_SLUG=$TRAVIS_REPO_SLUG" - - echo "TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST" - - if [[ "$TRAVIS_REPO_SLUG" != "Haivision/srt" || "$TRAVIS_PULL_REQUEST" -gt 0 ]]; then - export RUN_SONARCUBE=0; - fi - - echo "RUN_SONARCUBE=$RUN_SONARCUBE" - - if (( "$RUN_SONARCUBE" )); then - build-wrapper-linux-x86-64 --out-dir bw-output make; - else - make -j$(nproc); - fi + - make -j$(nproc); - if [ "$TRAVIS_COMPILER" != "x86_64-w64-mingw32-g++" ]; then ulimit -c unlimited; ./test-srt -disable-ipv6; @@ -98,7 +83,3 @@ script: if [ -f core ]; then gdb -batch ./test-srt -c core -ex bt -ex "info thread" -ex quit; else echo "NO CORE - NO CRY!"; fi; test $SUCCESS == 0; fi -after_success: - - if (( "$RUN_SONARCUBE" )); then - sonar-scanner -D"sonar.cfamily.gcov.reportPath=."; - fi diff --git a/sonar-project.properties b/sonar-project.properties index 22b16d549..d11323df8 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -19,7 +19,6 @@ sonar.organization=haivision # ===================================================== sonar.links.homepage=https://github.com/Haivision/srt -sonar.links.ci=https://travis-ci.org/Haivision/srt sonar.links.scm=https://github.com/Haivision/srt sonar.links.issue=https://github.com/Haivision/srt/issues @@ -29,8 +28,9 @@ sonar.links.issue=https://github.com/Haivision/srt/issues # ===================================================== # SQ standard properties -sonar.sources=. +sonar.sources=srtcore/,apps/,common/,examples/,haicrypt/,scripts/,testing/ +sonar.tests=test/ # Properties specific to the C/C++ analyzer: -sonar.cfamily.build-wrapper-output=bw-output +sonar.cfamily.build-wrapper-output=_build/sonar-output sonar.cfamily.gcov.reportsPath=. From 62ebfb90fe02283d2adace3f1ac4e7ddf83b3584 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 12 Mar 2024 11:46:18 +0100 Subject: [PATCH 27/51] [core] A minor fix (reference to const). --- srtcore/api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index a3734a603..d3e8af887 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2926,7 +2926,7 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& reqaddr, cons bool reuse_attempt = false; for (map::iterator i = m_mMultiplexer.begin(); i != m_mMultiplexer.end(); ++i) { - CMultiplexer& m = i->second; + CMultiplexer const& m = i->second; // First, we need to find a multiplexer with the same port. if (m.m_iPort != port) From 94bfa8682c654d22162f0f569ee8d9f6846625aa Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 12 Mar 2024 12:08:19 +0100 Subject: [PATCH 28/51] [build] Don't run a SonarScan on forks. --- .github/workflows/cxx11-ubuntu.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cxx11-ubuntu.yaml b/.github/workflows/cxx11-ubuntu.yaml index d52afd31b..aa74e1fce 100644 --- a/.github/workflows/cxx11-ubuntu.yaml +++ b/.github/workflows/cxx11-ubuntu.yaml @@ -30,6 +30,7 @@ jobs: source ./scripts/collect-gcov.sh bash <(curl -s https://codecov.io/bash) - name: Run SonarCloud Scan for C and C++ + if: ${{ !github.event.pull_request.head.repo.fork }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From aa69c3db0daa221b0a9b4e86fee2efd80c6e6f2b Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 13 Mar 2024 10:36:33 +0100 Subject: [PATCH 29/51] [tests] Fixed tests that were weirdly failing (#2908). --- test/test_main.cpp | 21 ++++++++++++++++++++- test/test_reuseaddr.cpp | 28 ++++++++++++---------------- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/test/test_main.cpp b/test/test_main.cpp index cb3b7c5c5..e2243a306 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -190,7 +190,26 @@ UniqueSocket::~UniqueSocket() void UniqueSocket::close() { - EXPECT_NE(srt_close(sock), SRT_ERROR) << lab << " CREATED: "<< f << ":" << l; + int close_result = srt_close(sock); + int close_error = srt_getlasterror(nullptr); + + // XXX SRT_EINVSOCK is reported when the socket + // has been already wiped out, which may happen to a broken socket. + // This isn't exactly intended, although trying to close a nonexistent + // socket is not a problem, as long as it happens before the id value rollover + // (that is, when it's closed immediately after getting broken). + // This solution is still slick though and should be fixed. + // + // Restore this, when fixed + // EXPECT_NE(srt_close(sock), SRT_ERROR) << lab << " CREATED: "<< f << ":" << l; + if (close_result == SRT_ERROR) + { + EXPECT_NE(close_error, SRT_EINVSOCK) << lab << " CREATED: "<< f << ":" << l; + } + else + { + EXPECT_EQ(close_result, 0) << lab << " CREATED: "<< f << ":" << l; + } } } diff --git a/test/test_reuseaddr.cpp b/test/test_reuseaddr.cpp index fa92bc635..d00628e63 100644 --- a/test/test_reuseaddr.cpp +++ b/test/test_reuseaddr.cpp @@ -104,8 +104,6 @@ static std::string GetLocalIP(int af = AF_UNSPEC) class ReuseAddr : public srt::Test { - int m_server_pollid = SRT_ERROR; - protected: std::string showEpollContents(const char* label, int* array, int length) @@ -243,11 +241,6 @@ class ReuseAddr : public srt::Test EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); - int epoll_in = SRT_EPOLL_IN; - - std::cout << "[T/S] Listener/binder sock @" << bindsock << " added to m_server_pollid\n"; - srt_epoll_add_usock(m_server_pollid, bindsock, &epoll_in); - return bindsock; } @@ -314,7 +307,7 @@ class ReuseAddr : public srt::Test void testAccept(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) { - MAKE_UNIQUE_SOCK(client_sock, "[T/C]connect", srt_create_socket()); + MAKE_UNIQUE_SOCK(client_sock, "[T/S]connect", srt_create_socket()); auto run = [this, &client_sock, ip, port, expect_success]() { clientSocket(client_sock, ip, port, expect_success); }; @@ -322,6 +315,11 @@ class ReuseAddr : public srt::Test AtReturnJoin atreturn_join {launched}; + int server_pollid = srt_epoll_create(); + int epoll_in = SRT_EPOLL_IN; + std::cout << "[T/S] Listener/binder sock @" << bindsock << " added to server_pollid\n"; + srt_epoll_add_usock(server_pollid, bindsock, &epoll_in); + { // wait for connection from client int rlen = 2; SRTSOCKET read[2]; @@ -329,9 +327,9 @@ class ReuseAddr : public srt::Test int wlen = 2; SRTSOCKET write[2]; - std::cout << "[T/S] Wait 10s on E" << m_server_pollid << " for acceptance on @" << bindsock << " ...\n"; + std::cout << "[T/S] Wait 10s on E" << server_pollid << " for acceptance on @" << bindsock << " ...\n"; - EXPECT_NE(srt_epoll_wait(m_server_pollid, + EXPECT_NE(srt_epoll_wait(server_pollid, read, &rlen, write, &wlen, 10000, // -1 is set for debuging purpose. @@ -358,7 +356,7 @@ class ReuseAddr : public srt::Test std::cout << "[T/S] Accepted from: " << showacp.str() << std::endl; int epoll_in = SRT_EPOLL_IN; - srt_epoll_add_usock(m_server_pollid, accepted_sock, &epoll_in); // wait for input + srt_epoll_add_usock(server_pollid, accepted_sock, &epoll_in); // wait for input char buffer[1316]; { // wait for 1316 packet from client @@ -370,7 +368,7 @@ class ReuseAddr : public srt::Test std::cout << "[T/S] Wait for data reception...\n"; - EXPECT_NE(srt_epoll_wait(m_server_pollid, + EXPECT_NE(srt_epoll_wait(server_pollid, read, &rlen, write, &wlen, -1, // -1 is set for debuging purpose. @@ -401,6 +399,8 @@ class ReuseAddr : public srt::Test client_sock.close(); std::cout << "[T/S] closing sockets: ACP:@" << accepted_sock << "...\n"; } + srt_epoll_release(server_pollid); + // client_sock closed through UniqueSocket. // cannot close client_sock after srt_sendmsg because of issue in api.c:2346 @@ -447,14 +447,10 @@ class ReuseAddr : public srt::Test void setup() { - m_server_pollid = srt_epoll_create(); - ASSERT_NE(m_server_pollid, SRT_ERROR); } void teardown() { - (void)srt_epoll_release(m_server_pollid); - m_server_pollid = SRT_ERROR; } }; From 84b5bb8c2bfd331ac55aba70fe09e5d3cbb00dee Mon Sep 17 00:00:00 2001 From: yomnes0 <127947185+yomnes0@users.noreply.github.com> Date: Fri, 15 Mar 2024 14:50:53 +0100 Subject: [PATCH 30/51] [build] Add a GitHub CI Action to check API/ABI compatibility (#2902). --- .github/workflows/abi.yml | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 .github/workflows/abi.yml diff --git a/.github/workflows/abi.yml b/.github/workflows/abi.yml new file mode 100644 index 000000000..2c05cc06b --- /dev/null +++ b/.github/workflows/abi.yml @@ -0,0 +1,61 @@ +name: ABI checks + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + SRT_BASE: v1.5.0 + +jobs: + build: + name: ABI checks + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v3 + with: + path: pull_request + - name: configure + run: | + cd pull_request + mkdir _build && cd _build + cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_UNITTESTS=ON ../ + - name: build + run: | + sudo apt install -y abi-dumper + cd pull_request/_build && cmake --build ./ + make install DESTDIR=./installdir + SRT_TAG_VERSION=$(cat version.h |grep SRT_VERSION_MINOR |head -n1 |awk {'print $3'}) + abi-dumper libsrt.so -o libsrt-pr.dump -public-headers installdir/usr/local/include/srt/ -lver 0 + SRT_BASE="v1.$SRT_TAG_VERSION.0" + echo "SRT_BASE=$SRT_BASE" >> "$GITHUB_ENV" + - uses: actions/checkout@v3 + with: + path: tag + ref: ${{ env.SRT_BASE }} + - name: configure_tag + run: | + echo $SRT_TAG_VERSION + cd tag + mkdir _build && cd _build + cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_UNITTESTS=ON ../ + - name: build_tag + run: | + cd tag + cd _build && cmake --build ./ + make install DESTDIR=./installdir + abi-dumper libsrt.so -o libsrt-tag.dump -public-headers installdir/usr/local/include/srt/ -lver 1 + - name: abi-check + run: | + git clone https://github.com/lvc/abi-compliance-checker.git + cd abi-compliance-checker && sudo make install && cd ../ + abi-compliance-checker -l libsrt -old tag/_build/libsrt-tag.dump -new pull_request/_build/libsrt-pr.dump + RES=$? + if (( $RES != 0 )) + then + echo "ABI/API Compatibility check failed with value $?" + exit $RES + fi From c6afa19d53d2740e6bca6e3eba591ee92373046b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C3=ABl=20Carr=C3=A9?= Date: Thu, 4 Apr 2024 16:03:20 +0200 Subject: [PATCH 31/51] [core] Fix HaiCrypt_Clone(): set up RX crypto ctx properly (#2905). --- haicrypt/hcrypt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/haicrypt/hcrypt.c b/haicrypt/hcrypt.c index 2568654b1..dc3f06801 100644 --- a/haicrypt/hcrypt.c +++ b/haicrypt/hcrypt.c @@ -320,6 +320,7 @@ int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handl cryptoClone->ctx_pair[1].flags &= ~HCRYPT_CTX_F_ENCRYPT; memset(cryptoClone->ctx_pair[0].salt, 0, sizeof(cryptoClone->ctx_pair[0].salt)); cryptoClone->ctx_pair[0].salt_len = 0; + cryptoClone->ctx = &cryptoClone->ctx_pair[0]; } *phhc = (void *)cryptoClone; From a15cf4e9f97228f45a59719972fe47bba9e77b50 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 9 Apr 2024 12:16:47 +0200 Subject: [PATCH 32/51] [core] Drop noenc packets if RcvKmState is "secured". --- srtcore/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 4493c397d..2e6551423 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -10190,7 +10190,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& #endif } } - else if (m_pCryptoControl && m_pCryptoControl->getCryptoMode() == CSrtConfig::CIPHER_MODE_AES_GCM) + else if (m_pCryptoControl && m_pCryptoControl->m_RcvKmState == SRT_KM_S_SECURED) { // Unencrypted packets are not allowed. const int iDropCnt = m_pRcvBuffer->dropMessage(u->m_Packet.getSeqNo(), u->m_Packet.getSeqNo(), SRT_MSGNO_NONE, CRcvBuffer::DROP_EXISTING); From 83077aa90c9d9b875577c410a75a7ee91f655c3d Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 9 Apr 2024 14:34:36 +0200 Subject: [PATCH 33/51] [docs] Update the SRT_KM_S_SECURED description. Updated pktRcvUndecrypted description. --- docs/API/API-socket-options.md | 2 +- docs/API/statistics.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 84e361f6d..a06f8556d 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -58,7 +58,7 @@ Exchange for the initial key is done in the handshake. - `SRT_KM_S_SECURED` (`2`): KM exchange was successful and the data will be sent encrypted and will be decrypted by the receiver. This state is only possible on -both sides in both directions simultaneously. +both sides in both directions simultaneously. Any unencrypted packet will be dropped by the receiver. - `SRT_KM_S_NOSECRET` (`3`): If this state is in the sending direction (`SRTO_SNDKMSTATE`), then it means that the sending party has set a passphrase, but the peer did not. diff --git a/docs/API/statistics.md b/docs/API/statistics.md index 60bba59ee..bc34ecca8 100644 --- a/docs/API/statistics.md +++ b/docs/API/statistics.md @@ -245,6 +245,8 @@ Packets may be dropped conditionally when both `SRTO_TSBPDMODE` and `SRTO_TLPKTD #### pktRcvUndecryptTotal The total number of packets that failed to be decrypted at the receiver side. Available for receiver. +The statistic also counts unencrypted packets that were expected to be uncrypted on a secured connection (see [SRTO_KM_S_SECURED](API-socket-options.md#srt_km_state)) +and hence dropped as not encrypted (undecrypted). #### pktSndFilterExtraTotal @@ -822,4 +824,4 @@ The ratio of unrecovered by the socket group packets `Dropped Packets Ratio` can ``` Dropped Packets Ratio = pktRcvDropTotal / pktSentUniqueTotal; in case both sender and receiver statistics is available Dropped Packets Ratio = pktRcvDropTotal / (pktRecvUniqueTotal + pktRcvDropTotal); in case receiver only statistics is available -``` \ No newline at end of file +``` From c156dab75b83c12bb8dbbda77dab569db22b3b78 Mon Sep 17 00:00:00 2001 From: yomnes0 Date: Fri, 5 Apr 2024 15:23:39 +0200 Subject: [PATCH 34/51] [build] Switch default enclib from openssl to openssl-evp --- CMakeLists.txt | 4 ++-- docs/build/build-options.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5994d3b7..1c30e49ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -237,12 +237,12 @@ if (NOT USE_ENCLIB) message("NOTE: USE_GNUTLS is deprecated. Use -DUSE_ENCLIB=gnutls instead.") set (USE_ENCLIB gnutls) else() - set (USE_ENCLIB openssl) + set (USE_ENCLIB openssl-evp) endif() endif() set(USE_ENCLIB "${USE_ENCLIB}" CACHE STRING "The crypto library that SRT uses") -set_property(CACHE USE_ENCLIB PROPERTY STRINGS "openssl" "gnutls" "mbedtls" "botan") +set_property(CACHE USE_ENCLIB PROPERTY STRINGS "openssl" "openssl-evp" "gnutls" "mbedtls" "botan") # Make sure DLLs and executabes go to the same path regardles of subdirectory set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/docs/build/build-options.md b/docs/build/build-options.md index 88fcb85bb..529bd5cd7 100644 --- a/docs/build/build-options.md +++ b/docs/build/build-options.md @@ -597,8 +597,8 @@ remember that: Encryption library to be used. Possible options for ``: -* openssl (default) -* openssl-evp (OpenSSL EVP API, since 1.5.1) +* openssl-evp (default) +* openssl * gnutls (with nettle) * mbedtls * botan From 3b84386e16f9f12cab50b2b8f2d1970ef6c9078a Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 11 Apr 2024 11:37:38 +0200 Subject: [PATCH 35/51] [core] Use common functions for byte order conversion. --- srtcore/channel.cpp | 24 +++--------------------- srtcore/packet.cpp | 29 ++++++++++------------------- srtcore/packet.h | 6 ++++-- srtcore/utilities.h | 9 ++++++--- 4 files changed, 23 insertions(+), 45 deletions(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 557be8fd7..035904a39 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -748,7 +748,7 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka #endif // convert control information into network order - packet.toNL(); + packet.toNetworkByteOrder(); #ifndef _WIN32 msghdr mh; @@ -818,7 +818,7 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka res = (0 == res) ? size : -1; #endif - packet.toHL(); + packet.toHostByteOrder(); return res; } @@ -1067,25 +1067,7 @@ srt::EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet } w_packet.setLength(recv_size - CPacket::HDR_SIZE); - - // convert back into local host order - // XXX use NtoHLA(). - // for (int i = 0; i < 4; ++ i) - // w_packet.m_nHeader[i] = ntohl(w_packet.m_nHeader[i]); - { - uint32_t* p = w_packet.m_nHeader; - for (size_t i = 0; i < SRT_PH_E_SIZE; ++i) - { - *p = ntohl(*p); - ++p; - } - } - - if (w_packet.isControl()) - { - for (size_t j = 0, n = w_packet.getLength() / sizeof(uint32_t); j < n; ++j) - *((uint32_t*)w_packet.m_pcData + j) = ntohl(*((uint32_t*)w_packet.m_pcData + j)); - } + w_packet.toHostByteOrder(); return RST_OK; diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index 668c4b5b3..180623039 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -432,38 +432,29 @@ void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, } } -void CPacket::toNL() +void CPacket::toNetworkByteOrder() { - // XXX USE HtoNLA! + // The payload of data packet should remain in network byte order. if (isControl()) { - for (ptrdiff_t i = 0, n = getLength() / 4; i < n; ++i) - *((uint32_t*)m_pcData + i) = htonl(*((uint32_t*)m_pcData + i)); + HtoNLA((uint32_t*) m_pcData, (const uint32_t*) m_pcData, getLength() / 4); } - // convert packet header into network order + // Convert packet header independent of packet type. uint32_t* p = m_nHeader; - for (int j = 0; j < 4; ++j) - { - *p = htonl(*p); - ++p; - } + HtoNLA(p, p, 4); } -void CPacket::toHL() +void CPacket::toHostByteOrder() { - // convert back into local host order + // Convert packet header independent of packet type. uint32_t* p = m_nHeader; - for (int k = 0; k < 4; ++k) - { - *p = ntohl(*p); - ++p; - } + NtoHLA(p, p, 4); + // The payload of data packet should remain in network byte order. if (isControl()) { - for (ptrdiff_t l = 0, n = getLength() / 4; l < n; ++l) - *((uint32_t*)m_pcData + l) = ntohl(*((uint32_t*)m_pcData + l)); + NtoHLA((uint32_t*)m_pcData, (const uint32_t*)m_pcData, getLength() / 4); } } diff --git a/srtcore/packet.h b/srtcore/packet.h index 9b757118f..5094247b5 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -331,8 +331,10 @@ class CPacket }; public: - void toNL(); - void toHL(); + /// @brief Convert the packet inline to a network byte order (Little-endian). + void toNetworkByteOrder(); + /// @brief Convert the packet inline to a host byte order. + void toHostByteOrder(); protected: // DynamicStruct is the same as array of given type and size, just it diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 8a1374eb7..1786cf0ae 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -237,17 +237,20 @@ written by #endif -// Hardware <--> Network (big endian) convention +/// Hardware --> Network (big-endian) byte order conversion +/// @param size source length in four octets inline void HtoNLA(uint32_t* dst, const uint32_t* src, size_t size) { for (size_t i = 0; i < size; ++ i) - dst[i] = htonl(src[i]); + dst[i] = htobe32(src[i]); } +/// Network (big-endian) --> Hardware byte order conversion +/// @param size source length in four octets inline void NtoHLA(uint32_t* dst, const uint32_t* src, size_t size) { for (size_t i = 0; i < size; ++ i) - dst[i] = ntohl(src[i]); + dst[i] = be32toh(src[i]); } // Hardware <--> Intel (little endian) convention From df9b1f68e9aba46cc9b52d233628e4251b1b3ce3 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 16 Apr 2024 15:27:35 +0200 Subject: [PATCH 36/51] [core] Revert PR #2834. Overlapped send with 100 ms timeout on Windows added a data race. --- srtcore/channel.cpp | 47 ++++++++++----------------------------------- srtcore/channel.h | 3 --- 2 files changed, 10 insertions(+), 40 deletions(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 035904a39..2fd1b3805 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -142,14 +142,6 @@ srt::CChannel::CChannel() , m_bBindMasked(true) #endif { -#ifdef _WIN32 - SecureZeroMemory((PVOID)&m_SendOverlapped, sizeof(WSAOVERLAPPED)); - m_SendOverlapped.hEvent = WSACreateEvent(); - if (m_SendOverlapped.hEvent == NULL) { - LOGC(kmlog.Error, log << CONID() << "IPE: WSACreateEvent failed with error: " << NET_ERROR); - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } -#endif #ifdef SRT_ENABLE_PKTINFO // Do the check for ancillary data buffer size, kinda assertion static const size_t CMSG_MAX_SPACE = sizeof (CMSGNodeIPv4) + sizeof (CMSGNodeIPv6); @@ -165,12 +157,7 @@ srt::CChannel::CChannel() #endif } -srt::CChannel::~CChannel() -{ -#ifdef _WIN32 - WSACloseEvent(m_SendOverlapped.hEvent); -#endif -} +srt::CChannel::~CChannel() {} void srt::CChannel::createSocket(int family) { @@ -789,32 +776,18 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka #else DWORD size = (DWORD)(CPacket::HDR_SIZE + packet.getLength()); int addrsize = addr.size(); + WSAOVERLAPPED overlapped; + SecureZeroMemory((PVOID)&overlapped, sizeof(WSAOVERLAPPED)); + int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, &overlapped, NULL); - int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, &m_SendOverlapped, NULL); - - if (res == SOCKET_ERROR) + if (res == SOCKET_ERROR && NET_ERROR == WSA_IO_PENDING) { - if (NET_ERROR == WSA_IO_PENDING) - { - DWORD res_wait = WSAWaitForMultipleEvents(1, &m_SendOverlapped.hEvent, TRUE, 100 /*ms*/, FALSE); - if (res_wait == WAIT_FAILED) - { - LOGC(kslog.Warn, log << "CChannel::WSAWaitForMultipleEvents: failed with " << NET_ERROR); - res = -1; - } - else - { - DWORD dwFlags = 0; - const bool bCompleted = WSAGetOverlappedResult(m_iSocket, &m_SendOverlapped, &size, false, &dwFlags); - res = bCompleted ? 0 : -1; - } - } - else - { - LOGC(kmlog.Error, log << CONID() << "WSASendTo failed with error: " << NET_ERROR); - } + DWORD dwFlags = 0; + const bool bCompleted = WSAGetOverlappedResult(m_iSocket, &overlapped, &size, true, &dwFlags); + WSACloseEvent(overlapped.hEvent); + res = bCompleted ? 0 : -1; } - WSAResetEvent(m_SendOverlapped.hEvent); + res = (0 == res) ? size : -1; #endif diff --git a/srtcore/channel.h b/srtcore/channel.h index e09b13fd9..e12310001 100644 --- a/srtcore/channel.h +++ b/srtcore/channel.h @@ -169,9 +169,6 @@ class CChannel private: UDPSOCKET m_iSocket; // socket descriptor -#ifdef _WIN32 - mutable WSAOVERLAPPED m_SendOverlapped; -#endif // Mutable because when querying original settings // this comprises the cache for extracted values, From fd4084f0f9e660c6568f146e84d810fed0ed6f3d Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 16 Apr 2024 16:16:16 +0200 Subject: [PATCH 37/51] [core] Fixed thread safety using WSAOVERLAPPED in WSASendTo. The lpOverlapped parameter must be valid for the duration of the overlapped operation. If multiple I/O operations are simultaneously outstanding, each must reference a separate WSAOVERLAPPED structure. Resolves #973, #2632, #2834, #2838. Co-authored-by: Jiangjie Gao --- srtcore/channel.cpp | 57 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 2fd1b3805..0a4e1e318 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -774,18 +774,59 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka const int res = (int)::sendmsg(m_iSocket, &mh, 0); #else - DWORD size = (DWORD)(CPacket::HDR_SIZE + packet.getLength()); - int addrsize = addr.size(); + class WSAEventRef + { + public: + WSAEventRef() + : e(::WSACreateEvent()) + { + } + ~WSAEventRef() + { + ::WSACloseEvent(e); + e = NULL; + } + void reset() + { + ::WSAResetEvent(e); + } + WSAEVENT Handle() + { + return e; + } + + private: + WSAEVENT e; + }; +#if !defined(__MINGW32__) && defined(ENABLE_CXX11) + thread_local WSAEventRef lEvent; +#else + WSAEventRef lEvent; +#endif WSAOVERLAPPED overlapped; - SecureZeroMemory((PVOID)&overlapped, sizeof(WSAOVERLAPPED)); + ::SecureZeroMemory(&overlapped, sizeof(overlapped)); + overlapped.hEvent = lEvent.Handle(); + + DWORD size = (DWORD)(packet.m_PacketVector[0].size() + packet.m_PacketVector[1].size()); + int addrsize = addr.size(); int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, &overlapped, NULL); - if (res == SOCKET_ERROR && NET_ERROR == WSA_IO_PENDING) + if (res == SOCKET_ERROR) { - DWORD dwFlags = 0; - const bool bCompleted = WSAGetOverlappedResult(m_iSocket, &overlapped, &size, true, &dwFlags); - WSACloseEvent(overlapped.hEvent); - res = bCompleted ? 0 : -1; + if (NET_ERROR == WSA_IO_PENDING) + { + DWORD dwFlags = 0; + const bool bCompleted = WSAGetOverlappedResult(m_iSocket, &overlapped, &size, TRUE, &dwFlags); + if (bCompleted) + res = 0; + else + LOGC(kslog.Warn, log << "CChannel::sendto call on ::WSAGetOverlappedResult failed with error: " << NET_ERROR); + lEvent.reset(); + } + else + { + LOGC(kmlog.Error, log << CONID() << "WSASendTo failed with error: " << NET_ERROR); + } } res = (0 == res) ? size : -1; From cf132005044232689cdc890a266b976e74333ca6 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 19 Apr 2024 09:51:53 +0200 Subject: [PATCH 38/51] [core] Minor connection logging improvement (#2930). Also downgraded some logs from 'note' level to 'debug'. --- srtcore/api.cpp | 3 +++ srtcore/core.cpp | 13 +++++++------ srtcore/queue.cpp | 6 +++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index d3e8af887..56c581fec 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -656,6 +656,9 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, HLOGC(cnlog.Debug, log << "newConnection: mapping peer " << ns->m_PeerID << " to that socket (" << ns->m_SocketID << ")"); m_PeerRec[ns->getPeerSpec()].insert(ns->m_SocketID); + + LOGC(cnlog.Note, log << "@" << ns->m_SocketID << " connection on listener @" << listen + << " (" << ns->m_SelfAddr.str() << ") from peer @" << ns->m_PeerID << " (" << peer.str() << ")"); } catch (...) { diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 2e6551423..4a8ce550e 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2101,9 +2101,9 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint return SRT_CMD_NONE; } - LOGC(cnlog.Note, log << "HSREQ/rcv: cmd=" << SRT_CMD_HSREQ << "(HSREQ) len=" << bytelen - << hex << " vers=0x" << srtdata[SRT_HS_VERSION] << " opts=0x" << srtdata[SRT_HS_FLAGS] - << dec << " delay=" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); + LOGC(cnlog.Debug, log << "HSREQ/rcv: cmd=" << SRT_CMD_HSREQ << "(HSREQ) len=" << bytelen + << hex << " vers=0x" << srtdata[SRT_HS_VERSION] << " opts=0x" << srtdata[SRT_HS_FLAGS] + << dec << " delay=" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); m_uPeerSrtVersion = srtdata[SRT_HS_VERSION]; m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS]; @@ -4968,8 +4968,9 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, } */ - - LOGC(cnlog.Note, log << CONID() << "Connection established to: " << m_PeerAddr.str()); + + LOGC(cnlog.Note, log << CONID() << "Connection established from (" + << m_SourceAddr.str() << ") to peer @" << m_PeerID << " (" << m_PeerAddr.str() << ")"); return CONN_ACCEPT; } @@ -11328,7 +11329,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) } } } - LOGC(cnlog.Note, log << CONID() << "listen ret: " << hs.m_iReqType << " - " << RequestTypeStr(hs.m_iReqType)); + LOGC(cnlog.Debug, log << CONID() << "listen ret: " << hs.m_iReqType << " - " << RequestTypeStr(hs.m_iReqType)); return RejectReasonForURQ(hs.m_iReqType); } diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 345151b4e..8ad27217c 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1407,7 +1407,7 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, ScopedLock cg(m_LSLock); if (m_pListener) { - LOGC(cnlog.Note, log << "PASSING request from: " << addr.str() << " to agent:" << m_pListener->socketID()); + LOGC(cnlog.Debug, log << "PASSING request from: " << addr.str() << " to listener:" << m_pListener->socketID()); listener_ret = m_pListener->processConnectRequest(addr, unit->m_Packet); // This function does return a code, but it's hard to say as to whether @@ -1426,8 +1426,8 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, if (have_listener) // That is, the above block with m_pListener->processConnectRequest was executed { - LOGC(cnlog.Note, - log << CONID() << "Listener managed the connection request from: " << addr.str() + LOGC(cnlog.Debug, + log << CONID() << "Listener got the connection request from: " << addr.str() << " result:" << RequestTypeStr(UDTRequestType(listener_ret))); return listener_ret == SRT_REJ_UNKNOWN ? CONN_CONTINUE : CONN_REJECT; } From f6c231588da17f5bf91f82be7122664d072a93cc Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 22 Apr 2024 10:09:44 +0200 Subject: [PATCH 39/51] [core] Fixed stats counting packets dropped by a group (#2934). Also fixed m_iAvgPayloadSize initialization. --- srtcore/group.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 1539245a0..52b31d29f 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -259,6 +259,7 @@ CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) , m_uOPT_MinStabilityTimeout_us(1000 * CSrtConfig::COMM_DEF_MIN_STABILITY_TIMEOUT_MS) // -1 = "undefined"; will become defined with first added socket , m_iMaxPayloadSize(-1) + , m_iAvgPayloadSize(-1) , m_bSynRecving(true) , m_bSynSending(true) , m_bTsbPd(true) @@ -2309,6 +2310,19 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) } fillGroupData((w_mc), w_mc); + // TODO: What if a drop happens before the very first packet was read? Maybe set to ISN? + if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) + { + const int32_t iNumDropped = (CSeqNo(w_mc.pktseq) - CSeqNo(m_RcvBaseSeqNo)) - 1; + if (iNumDropped > 0) + { + m_stats.recvDrop.count(stats::BytesPackets(iNumDropped * static_cast(avgRcvPacketSize()), iNumDropped)); + LOGC(grlog.Warn, + log << "@" << m_GroupID << " GROUP RCV-DROPPED " << iNumDropped << " packet(s): seqno %" + << m_RcvBaseSeqNo << " to %" << w_mc.pktseq); + } + } + HLOGC(grlog.Debug, log << "grp/recv: $" << id() << ": Update m_RcvBaseSeqNo: %" << m_RcvBaseSeqNo << " -> %" << w_mc.pktseq); m_RcvBaseSeqNo = w_mc.pktseq; From 4f925fbca586c0d39bf80febd9d8315a8092f1f7 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 22 Apr 2024 10:11:25 +0200 Subject: [PATCH 40/51] [core] Stats: do not count discarded packets as dropped (#2932). Valid for a broadcast group member dropping existing packets from the RCV buffer because they were read from another member. Co-authored-by: Sektor van Skijlen --- srtcore/buffer_rcv.cpp | 18 +++++++++++++----- srtcore/buffer_rcv.h | 4 ++-- srtcore/core.cpp | 19 +++++++++++++------ srtcore/core.h | 9 ++++++++- srtcore/group.cpp | 2 +- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index fb389e4be..2ec42487d 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -206,7 +206,7 @@ int CRcvBuffer::insert(CUnit* unit) return 0; } -int CRcvBuffer::dropUpTo(int32_t seqno) +std::pair 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); @@ -215,16 +215,23 @@ int CRcvBuffer::dropUpTo(int32_t seqno) if (len <= 0) { IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); - return 0; + return std::make_pair(0, 0); } m_iMaxPosOff -= len; if (m_iMaxPosOff < 0) m_iMaxPosOff = 0; - const int iDropCnt = len; + 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 > 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. + if (m_entries[m_iStartPos].status == EntryState_Avail) + ++iNumDiscarded; + else if (m_entries[m_iStartPos].status == EntryState_Empty) + ++iNumDropped; 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); @@ -246,7 +253,7 @@ int CRcvBuffer::dropUpTo(int32_t seqno) } if (!m_tsbpd.isEnabled() && m_bMessageAPI) updateFirstReadableOutOfOrder(); - return iDropCnt; + return std::make_pair(iNumDropped, iNumDiscarded); } int CRcvBuffer::dropAll() @@ -255,7 +262,8 @@ int CRcvBuffer::dropAll() return 0; const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); - return dropUpTo(end_seqno); + const std::pair numDropped = dropUpTo(end_seqno); + return numDropped.first + numDropped.second; } int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, DropActionIfExists actionOnExisting) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index d664373f5..3c60be21d 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -66,8 +66,8 @@ class CRcvBuffer /// 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 - /// @return number of dropped packets. - int dropUpTo(int32_t seqno); + /// @return number of dropped (missing) and discarded (available) packets as a pair(dropped, discarded). + std::pair dropUpTo(int32_t seqno); /// @brief Drop all the packets in the receiver buffer. /// The starting position and seqno are shifted right after the last packet in the buffer. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 4a8ce550e..6752d7e9f 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5576,7 +5576,7 @@ void * srt::CUDT::tsbpd(void* param) return NULL; } -int srt::CUDT::rcvDropTooLateUpTo(int seqno) +int srt::CUDT::rcvDropTooLateUpTo(int seqno, DropReason reason) { // Make sure that it would not drop over m_iRcvCurrSeqNo, which may break senders. if (CSeqNo::seqcmp(seqno, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) @@ -5584,16 +5584,22 @@ int srt::CUDT::rcvDropTooLateUpTo(int seqno) dropFromLossLists(SRT_SEQNO_NONE, CSeqNo::decseq(seqno)); - const int iDropCnt = m_pRcvBuffer->dropUpTo(seqno); - if (iDropCnt > 0) + const std::pair iDropDiscardedPkts = m_pRcvBuffer->dropUpTo(seqno); + const int iDropCnt = iDropDiscardedPkts.first; + const int iDiscardedCnt = iDropDiscardedPkts.second; + const int iDropCntTotal = iDropCnt + iDiscardedCnt; + + // In case of DROP_TOO_LATE discarded packets should also be counted because they are not read from another member socket. + const int iDropStatCnt = (reason == DROP_DISCARD) ? iDropCnt : iDropCntTotal; + if (iDropStatCnt > 0) { enterCS(m_StatsLock); // Estimate dropped bytes from average payload size. const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropStatCnt * avgpayloadsz, (uint32_t)iDropStatCnt)); leaveCS(m_StatsLock); } - return iDropCnt; + return iDropCntTotal; } void srt::CUDT::setInitialRcvSeq(int32_t isn) @@ -7835,7 +7841,7 @@ void srt::CUDT::dropToGroupRecvBase() return; ScopedLock lck(m_RcvBufferLock); - int cnt = rcvDropTooLateUpTo(CSeqNo::incseq(group_recv_base)); + const int cnt = rcvDropTooLateUpTo(CSeqNo::incseq(group_recv_base), DROP_DISCARD); if (cnt > 0) { HLOGC(grlog.Debug, @@ -8063,6 +8069,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) string reason; // just for "a reason" of giving particular % for ACK #if ENABLE_BONDING + // TODO: The group drops other members upon reading, maybe no longer needed here? dropToGroupRecvBase(); #endif diff --git a/srtcore/core.h b/srtcore/core.h index 10746c8c9..73b053197 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -755,11 +755,18 @@ class CUDT // TSBPD thread main function. static void* tsbpd(void* param); + enum DropReason + { + DROP_TOO_LATE, //< Drop to keep up to the live pace (TLPKTDROP). + DROP_DISCARD //< Drop because another group member already provided these packets. + }; + /// Drop too late packets (receiver side). Update loss lists and ACK positions. /// The @a seqno packet itself is not dropped. /// @param seqno [in] The sequence number of the first packets following those to be dropped. + /// @param reason A reason for dropping (see @a DropReason). /// @return The number of packets dropped. - int rcvDropTooLateUpTo(int seqno); + int rcvDropTooLateUpTo(int seqno, DropReason reason = DROP_TOO_LATE); static loss_seqs_t defaultPacketArrival(void* vself, CPacket& pkt); static loss_seqs_t groupPacketArrival(void* vself, CPacket& pkt); diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 52b31d29f..282e8f25a 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2338,7 +2338,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) ScopedLock lg(ps->core().m_RcvBufferLock); if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) { - const int cnt = ps->core().rcvDropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); + const int cnt = ps->core().rcvDropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo), CUDT::DROP_DISCARD); if (cnt > 0) { HLOGC(grlog.Debug, From 973be583a30c76c250744c6eea7ae93982715072 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 22 Apr 2024 10:13:35 +0200 Subject: [PATCH 41/51] [core] Removed group syncing when sending control ACK (#2935). --- srtcore/core.cpp | 39 --------------------------------------- srtcore/core.h | 6 ------ 2 files changed, 45 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 6752d7e9f..d12628220 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7818,40 +7818,6 @@ void srt::CUDT::releaseSynch() leaveCS(m_RecvLock); } - -#if ENABLE_BONDING -void srt::CUDT::dropToGroupRecvBase() -{ - int32_t group_recv_base = SRT_SEQNO_NONE; - if (m_parent->m_GroupOf) - { - // Check is first done before locking to avoid unnecessary - // mutex locking. The condition for this field is that it - // can be either never set, already reset, or ever set - // and possibly dangling. The re-check after lock eliminates - // the dangling case. - ScopedLock glock (uglobal().m_GlobControlLock); - - // Note that getRcvBaseSeqNo() will lock m_GroupOf->m_GroupLock, - // but this is an intended order. - if (m_parent->m_GroupOf) - group_recv_base = m_parent->m_GroupOf->getRcvBaseSeqNo(); - } - if (group_recv_base == SRT_SEQNO_NONE) - return; - - ScopedLock lck(m_RcvBufferLock); - const int cnt = rcvDropTooLateUpTo(CSeqNo::incseq(group_recv_base), DROP_DISCARD); - if (cnt > 0) - { - HLOGC(grlog.Debug, - log << CONID() << "dropToGroupRecvBase: dropped " << cnt << " packets before ACK: group_recv_base=" - << group_recv_base << " m_iRcvLastAck=" << m_iRcvLastAck - << " m_iRcvCurrSeqNo=" << m_iRcvCurrSeqNo << " m_bTsbPd=" << m_bTsbPd); - } -} -#endif - namespace srt { #if ENABLE_HEAVY_LOGGING static void DebugAck(string hdr, int prev, int ack) @@ -8068,11 +8034,6 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) #endif string reason; // just for "a reason" of giving particular % for ACK -#if ENABLE_BONDING - // TODO: The group drops other members upon reading, maybe no longer needed here? - 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); diff --git a/srtcore/core.h b/srtcore/core.h index 73b053197..983704682 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1156,12 +1156,6 @@ class CUDT static void addLossRecord(std::vector& lossrecord, int32_t lo, int32_t hi); int32_t bake(const sockaddr_any& addr, int32_t previous_cookie = 0, int correction = 0); -#if ENABLE_BONDING - /// @brief Drop packets in the recv buffer behind group_recv_base. - /// Updates m_iRcvLastSkipAck if it's behind group_recv_base. - void dropToGroupRecvBase(); -#endif - void processKeepalive(const CPacket& ctrlpkt, const time_point& tsArrival); From 882dff96e1800b1c2f286e23aaf28bf22c7342ec Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 24 Apr 2024 16:18:52 +0200 Subject: [PATCH 42/51] [core] Minor internal logging format changes (#2939). Also renamed CUDT::m_bTsbPdAckWakeup. Added some function to the CRcvBuffer. Co-authored-by: Sektor van Skijlen --- srtcore/buffer_rcv.h | 19 +++++++++++++--- srtcore/core.cpp | 54 ++++++++++++++++++++++++++++++++------------ srtcore/core.h | 12 ++++++++-- srtcore/queue.cpp | 4 ++-- 4 files changed, 68 insertions(+), 21 deletions(-) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 3c60be21d..f783ac2a2 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -200,6 +200,20 @@ class CRcvBuffer return (m_iMaxPosOff == 0); } + /// 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; + } + + // Returns true if the buffer is full. Requires locking. + bool full() const + { + return size() == capacity(); + } + /// Return buffer capacity. /// One slot had to be empty in order to tell the difference between "empty buffer" and "full buffer". /// E.g. m_iFirstNonreadPos would again point to m_iStartPos if m_szSize entries are added continiously. @@ -333,9 +347,8 @@ class CRcvBuffer EntryStatus status; }; - //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 diff --git a/srtcore/core.cpp b/srtcore/core.cpp index d12628220..a0c6e4dbd 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -298,7 +298,7 @@ void srt::CUDT::construct() m_iPeerTsbPdDelay_ms = 0; m_bPeerTsbPd = false; m_bTsbPd = false; - m_bTsbPdAckWakeup = false; + m_bTsbPdNeedsWakeup = false; m_bGroupTsbPd = false; m_bPeerTLPktDrop = false; m_bBufferWasFull = false; @@ -5405,7 +5405,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_bTsbPdNeedsWakeup = true; while (!self->m_bClosing) { steady_clock::time_point tsNextDelivery; // Next packet delivery time @@ -5425,6 +5425,21 @@ 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; +#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) { rxready = !info.seq_gap && is_time_to_deliver; @@ -5470,8 +5485,8 @@ void * srt::CUDT::tsbpd(void* param) if (rxready) { HLOGC(tslog.Debug, - log << self->CONID() << "tsbpd: PLAYING PACKET seq=" << info.seqno << " (belated " - << (count_milliseconds(steady_clock::now() - info.tsbpd_time)) << "ms)"); + log << self->CONID() << "tsbpd: PLAYING PACKET seq=" << info.seqno << " (belated " + << 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 @@ -5534,6 +5549,8 @@ void * srt::CUDT::tsbpd(void* param) if (self->m_bClosing) break; + SRT_ATR_UNUSED bool bWokeUpOnSignal = true; + if (!is_zero(tsNextDelivery)) { IF_HEAVY_LOGGING(const steady_clock::duration timediff = tsNextDelivery - tnow); @@ -5541,12 +5558,12 @@ 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_bTsbPdNeedsWakeup = false; HLOGC(tslog.Debug, - log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno - << " T=" << FormatTime(tsNextDelivery) << " - waiting " << count_milliseconds(timediff) << "ms"); + log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno + << " T=" << FormatTime(tsNextDelivery) << " - waiting " << FormatDuration(timediff)); THREAD_PAUSED(); - tsbpd_cc.wait_until(tsNextDelivery); + bWokeUpOnSignal = tsbpd_cc.wait_until(tsNextDelivery); THREAD_RESUMED(); } else @@ -5563,13 +5580,15 @@ 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_bTsbPdNeedsWakeup = true; THREAD_PAUSED(); tsbpd_cc.wait(); THREAD_RESUMED(); } - HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP!!!"); + HLOGC(tslog.Debug, + log << self->CONID() << "tsbpd: WAKE UP [" << (bWokeUpOnSignal ? "signal" : "timeout") << "]!!! - " + << "NOW=" << FormatTime(steady_clock::now())); } THREAD_EXIT(); HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING"); @@ -6951,6 +6970,12 @@ bool srt::CUDT::isRcvBufferReadyNoLock() const return m_pRcvBuffer->isRcvDataReady(steady_clock::now()); } +bool srt::CUDT::isRcvBufferFull() const +{ + ScopedLock lck(m_RcvBufferLock); + return m_pRcvBuffer->full(); +} + // int by_exception: accepts values of CUDTUnited::ErrorHandling: // - 0 - by return value // - 1 - by exception @@ -7738,8 +7763,8 @@ bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) m_iCongestionWindow = cgwindow; #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) << cgwindow); #endif } @@ -8141,7 +8166,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) /* 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) + if (m_bTsbPdNeedsWakeup) tslcc.notify_one(); } else @@ -8204,7 +8229,8 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) else if (!bNeedFullAck) { // 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; } diff --git a/srtcore/core.h b/srtcore/core.h index 983704682..e24bd8152 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -327,6 +327,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; } @@ -388,6 +389,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); @@ -697,6 +699,8 @@ class CUDT /// the receiver fresh loss list. void unlose(const CPacket& oldpacket); void dropFromLossLists(int32_t from, int32_t to); + + SRT_ATTR_REQUIRES(m_RecvAckLock) bool getFirstNoncontSequence(int32_t& w_seq, std::string& w_log_reason); SRT_ATTR_EXCLUDES(m_ConnectionLock) @@ -752,6 +756,9 @@ class CUDT SRT_ATTR_REQUIRES(m_RcvBufferLock) bool isRcvBufferReadyNoLock() const; + SRT_ATTR_EXCLUDES(m_RcvBufferLock) + bool isRcvBufferFull() const; + // TSBPD thread main function. static void* tsbpd(void* param); @@ -987,7 +994,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 + bool m_bTsbPdNeedsWakeup; // Signal TsbPd thread to wake up on RCV buffer state change. sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining CallbackHolder m_cbAcceptHook; @@ -1136,7 +1143,8 @@ class CUDT /// @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); - /// Get the packet's TSBPD time. + /// Get the packet's TSBPD time - + /// the time when it is passed to the reading application. /// The @a grp passed by void* is not used yet /// and shall not be used when ENABLE_BONDING=0. time_point getPktTsbPdTime(void* grp, const CPacket& packet); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 8ad27217c..98999a81f 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1092,8 +1092,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 f99ce57760aefb2cd21af275e8de0fff9132c6e3 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 24 Apr 2024 16:27:03 +0200 Subject: [PATCH 43/51] [core] Fixed group RCV drop sequence range log. A follow-up for #2934. --- srtcore/group.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 282e8f25a..f0392e151 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2319,7 +2319,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) m_stats.recvDrop.count(stats::BytesPackets(iNumDropped * static_cast(avgRcvPacketSize()), iNumDropped)); LOGC(grlog.Warn, log << "@" << m_GroupID << " GROUP RCV-DROPPED " << iNumDropped << " packet(s): seqno %" - << m_RcvBaseSeqNo << " to %" << w_mc.pktseq); + << CSeqNo::incseq(m_RcvBaseSeqNo) << " to %" << CSeqNo::decseq(w_mc.pktseq)); } } From 11f081c984d6e6f1c6a8e2d2ce6df1f5323866c0 Mon Sep 17 00:00:00 2001 From: Aleksei Minaev <1472675+zulkis@users.noreply.github.com> Date: Wed, 24 Apr 2024 15:44:33 +0200 Subject: [PATCH 44/51] [build] Add visionOS support --- CMakeLists.txt | 3 +- scripts/visionOS.cmake | 171 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 scripts/visionOS.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c30e49ac..47d9b88df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,8 @@ string(TOLOWER ${CMAKE_SYSTEM_NAME} SYSNAME_LC) set_if(DARWIN (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") OR (${CMAKE_SYSTEM_NAME} MATCHES "iOS") OR (${CMAKE_SYSTEM_NAME} MATCHES "tvOS") - OR (${CMAKE_SYSTEM_NAME} MATCHES "watchOS")) + OR (${CMAKE_SYSTEM_NAME} MATCHES "watchOS") + OR (${CMAKE_SYSTEM_NAME} MATCHES "visionOS")) set_if(LINUX ${CMAKE_SYSTEM_NAME} MATCHES "Linux") set_if(BSD ${SYSNAME_LC} MATCHES "bsd$") set_if(MICROSOFT WIN32 AND (NOT MINGW AND NOT CYGWIN)) diff --git a/scripts/visionOS.cmake b/scripts/visionOS.cmake new file mode 100644 index 000000000..0f0f60b69 --- /dev/null +++ b/scripts/visionOS.cmake @@ -0,0 +1,171 @@ +# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake +# files which are included with CMake 2.8.4 +# It has been altered for VISIONOS development + +# Options: +# +# VISION_PLATFORM = OS (default) or SIMULATOR or SIMULATOR64 +# This decides if SDKS will be selected from the XROS.platform or XRSimulator.platform folders +# OS - the default, used to build for Vision Pro physical device, which have an arm arch. +# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch. +# +# VISIONOS_ARCH = arm64 (default for OS), x86_64 (addiitonal support for SIMULATOR64) +# +# CMAKE_VISIONOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder +# By default this location is automatcially chosen based on the VISIONOS_PLATFORM value above. +# If set manually, it will override the default location and force the user of a particular Developer Platform +# +# CMAKE_VISIONOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder +# By default this location is automatcially chosen based on the CMAKE_VISIONOS_DEVELOPER_ROOT value. +# In this case it will always be the most up-to-date SDK found in the CMAKE_VISIONOS_DEVELOPER_ROOT path. +# If set manually, this will force the use of a specific SDK version +# + +# Standard settings +set (CMAKE_SYSTEM_NAME Darwin) +set (CMAKE_SYSTEM_VERSION 1) +set (UNIX True) +set (APPLE True) +set (VISIONOS True) + +# Required as of cmake 2.8.10 +set (CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for visionOs" FORCE) + +# Determine the cmake host system version so we know where to find the visionOS SDKs +find_program (CMAKE_UNAME uname /bin /usr/bin /usr/local/bin) +if (CMAKE_UNAME) + execute_process(COMMAND uname -r + OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION) + string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}") +endif (CMAKE_UNAME) + + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +set(CMAKE_AR ar CACHE FILEPATH "" FORCE) + +set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") +set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") +set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") +set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") + +if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR ENABLE_DEBUG) + set(VISIONOS_DEBUG_OPTIONS "-glldb -gmodules") +else() + set(VISIONOS_DEBUG_OPTIONS "-fvisibility=hidden -fvisibility-inlines-hidden") +endif() + +set (CMAKE_C_FLAGS_INIT "${VISIONOS_DEBUG_OPTIONS} ${EMBED_OPTIONS}") +set (CMAKE_CXX_FLAGS_INIT "${VISIONOS_DEBUG_OPTIONS} ${EMBED_OPTIONS}") + +set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${EMBED_OPTIONS} ${CMAKE_C_LINK_FLAGS}") +set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${EMBED_OPTIONS} ${CMAKE_CXX_LINK_FLAGS}") + + +set (CMAKE_PLATFORM_HAS_INSTALLNAME 1) +set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib") +set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle") +set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") +set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") +set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a") + +# Specify install_name_tool and pkg-config since it outside of SDK path and therefore can't be found by CMake +if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool) +endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + +if (NOT DEFINED PKG_CONFIG_EXECUTABLE) + find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config) + if (DEFINED PKG_CONFIG_EXECUTABLE) + execute_process(COMMAND pkg-config --version OUTPUT_VARIABLE PKG_CONFIG_VERSION_STRING) + endif(DEFINED PKG_CONFIG_EXECUTABLE) +endif(NOT DEFINED PKG_CONFIG_EXECUTABLE) + + +# fffio Specify path to install shared library on device +set (CMAKE_INSTALL_NAME_DIR "@executable_path/Frameworks") +set (CMAKE_BUILD_WITH_INSTALL_NAME_DIR TRUE) + +# Setup visionOS platform unless specified manually with VISIONOS_PLATFORM +if (NOT DEFINED VISIONOS_PLATFORM) + set (VISIONOS_PLATFORM "OS") +endif (NOT DEFINED VISIONOS_PLATFORM) +set (VISIONOS_PLATFORM ${VISIONOS_PLATFORM} CACHE STRING "Type of visionOS Platform") + +# Check the platform selection and setup for developer root +if (${VISIONOS_PLATFORM} STREQUAL OS) + set (VISIONOS_PLATFORM_LOCATION "XROS.platform") + + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-xros") +elseif (${VISIONOS_PLATFORM} STREQUAL SIMULATOR) + set (SIMULATOR true) + set (VISIONOS_PLATFORM_LOCATION "XRSimulator.platform") + + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-xrsimulator") +elseif (${VISIONOS_PLATFORM} STREQUAL SIMULATOR64) + set (SIMULATOR true) + set (VISIONOS_PLATFORM_LOCATION "XRSimulator.platform") + + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-xrsimulator") +else (${VISIONOS_PLATFORM} STREQUAL OS) + message (FATAL_ERROR "Unsupported VISIONOS_PLATFORM value selected. Please choose OS or SIMULATOR") +endif (${VISIONOS_PLATFORM} STREQUAL OS) + +# Setup visionOS developer location unless specified manually with CMAKE_VISIONOS_DEVELOPER_ROOT +if (NOT DEFINED CMAKE_VISIONOS_DEVELOPER_ROOT) + execute_process(COMMAND /usr/bin/xcode-select -print-path + OUTPUT_VARIABLE CMAKE_XCODE_DEVELOPER_DIR) + string(STRIP "${CMAKE_XCODE_DEVELOPER_DIR}" CMAKE_XCODE_DEVELOPER_DIR) # FIXED: remove new line character, otherwise it complain no visionOS SDK's found in default search path + set (CMAKE_VISIONOS_DEVELOPER_ROOT "${CMAKE_XCODE_DEVELOPER_DIR}/Platforms/${VISIONOS_PLATFORM_LOCATION}/Developer") +endif (NOT DEFINED CMAKE_VISIONOS_DEVELOPER_ROOT) +set (CMAKE_VISIONOS_DEVELOPER_ROOT ${CMAKE_VISIONOS_DEVELOPER_ROOT} CACHE PATH "Location of visionOS Platform") + +# Find and use the most recent visionOS sdk unless specified manually with CMAKE_VISIONOS_SDK_ROOT +if (NOT DEFINED CMAKE_VISIONOS_SDK_ROOT) + file (GLOB _CMAKE_VISIONOS_SDKS "${CMAKE_VISIONOS_DEVELOPER_ROOT}/SDKs/*") + if (_CMAKE_VISIONOS_SDKS) + list (SORT _CMAKE_VISIONOS_SDKS) + list (REVERSE _CMAKE_VISIONOS_SDKS) + list (GET _CMAKE_VISIONOS_SDKS 0 CMAKE_VISIONOS_SDK_ROOT) + else (_CMAKE_VISIONOS_SDKS) + message (FATAL_ERROR "No visionOS SDK's found in default search path ${CMAKE_VISIONOS_DEVELOPER_ROOT}. Manually set CMAKE_VISIONOS_SDK_ROOT or install the visionOS SDK.") + endif (_CMAKE_VISIONOS_SDKS) + message (STATUS "Toolchain using default visionOS SDK: ${CMAKE_VISIONOS_SDK_ROOT}") +endif (NOT DEFINED CMAKE_VISIONOS_SDK_ROOT) +set (CMAKE_VISIONOS_SDK_ROOT ${CMAKE_VISIONOS_SDK_ROOT} CACHE PATH "Location of the selected visionOS SDK") + +# Set the sysroot default to the most recent SDK +set (CMAKE_OSX_SYSROOT ${CMAKE_VISIONOS_SDK_ROOT} CACHE PATH "Sysroot used for visionOS support") + +# set the architecture for visionOS +if (NOT DEFINED VISIONOS_ARCH) + if (${VISIONOS_PLATFORM} STREQUAL OS) + set (VISIONOS_ARCH arm64) + elseif (${VISIONOS_PLATFORM} STREQUAL SIMULATOR) + set (VISIONOS_ARCH arm64) + elseif (${VISIONOS_PLATFORM} STREQUAL SIMULATOR64) + set (VISIONOS_ARCH x86_64) + endif (${VISIONOS_PLATFORM} STREQUAL OS) +endif(NOT DEFINED VISIONOS_ARCH) +set (CMAKE_OSX_ARCHITECTURES ${VISIONOS_ARCH} CACHE STRING "Build architecture for visionOS") + +# Set the find root to the visionOS developer roots and to user defined paths +set (CMAKE_FIND_ROOT_PATH ${CMAKE_VISIONOS_DEVELOPER_ROOT} ${CMAKE_VISIONOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE STRING "visionOS find search path root") + +# default to searching for frameworks first +set (CMAKE_FIND_FRAMEWORK FIRST) + +# set up the default search directories for frameworks +set (CMAKE_SYSTEM_FRAMEWORK_PATH + ${CMAKE_VISIONOS_SDK_ROOT}/System/Library/Frameworks + ${CMAKE_VISIONOS_SDK_ROOT}/System/Library/PrivateFrameworks + ${CMAKE_VISIONOS_SDK_ROOT}/Developer/Library/Frameworks +) + +# only search the visionOS sdks, not the remainder of the host filesystem (except for programs, so that we can still find Python if needed) +set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + From d31d83e2ce85bdea5eae84d4f255d4956d72e641 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 26 Apr 2024 11:34:20 +0200 Subject: [PATCH 45/51] [core] Group set the RCV base seqno using peer ISN. --- srtcore/group.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index f0392e151..b37504874 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -870,18 +870,9 @@ void CUDTGroup::syncWithSocket(const CUDT& core, const HandshakeSide side) set_currentSchedSequence(core.ISN()); } - // XXX - // Might need further investigation as to whether this isn't - // wrong for some cases. By having this -1 here the value will be - // laziliy set from the first reading one. It is believed that - // it covers all possible scenarios, that is: - // - // - no readers - no problem! - // - have some readers and a new is attached - this is set already - // - connect multiple links, but none has read yet - you'll be the first. - // - // Previous implementation used setting to: core.m_iPeerISN - resetInitialRxSequence(); + // Only set if was not initialized to avoid problems on a running connection. + if (m_RcvBaseSeqNo == SRT_SEQNO_NONE) + m_RcvBaseSeqNo = CSeqNo::decseq(core.m_iPeerISN); // Get the latency (possibly fixed against the opposite side) // from the first socket (core.m_iTsbPdDelay_ms), @@ -2310,7 +2301,8 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) } fillGroupData((w_mc), w_mc); - // TODO: What if a drop happens before the very first packet was read? Maybe set to ISN? + // m_RcvBaseSeqNo is expected to be set to the PeerISN with the first connected member, + // so a packet drop at the start should also be detected by this condition. if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) { const int32_t iNumDropped = (CSeqNo(w_mc.pktseq) - CSeqNo(m_RcvBaseSeqNo)) - 1; From ceb4cca3f453061e7ebda7835a7407d5cadf951b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 22 Apr 2024 18:25:26 +0200 Subject: [PATCH 46/51] [core] Fixed group recv read-ready check. --- srtcore/group.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index b37504874..0927d085a 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2040,10 +2040,14 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& } else { - // No read-readiness reported by epoll, but probably missed or not yet handled - // as the receiver buffer is read-ready. + // No read-readiness reported by epoll, but can be missed or not yet handled + // while the receiver buffer is in fact read-ready. ScopedLock lg(sock->core().m_RcvBufferLock); - if (sock->core().m_pRcvBuffer && sock->core().m_pRcvBuffer->isRcvDataReady()) + if (!sock->core().m_pRcvBuffer) + continue; + // Checking for the next packet in the RCV buffer is safer that isReadReady(tnow). + const CRcvBuffer::PacketInfo info = sock->core().m_pRcvBuffer->getFirstValidPacketInfo(); + if (info.seqno != SRT_SEQNO_NONE && !info.seq_gap) readReady.push_back(sock); } } @@ -2213,6 +2217,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) } // Find the first readable packet among all member sockets. + steady_clock::time_point tnow = steady_clock::now(); CUDTSocket* socketToRead = NULL; CRcvBuffer::PacketInfo infoToRead = {-1, false, time_point()}; for (vector::const_iterator si = readySockets.begin(); si != readySockets.end(); ++si) @@ -2233,7 +2238,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) } const CRcvBuffer::PacketInfo info = - ps->core().m_pRcvBuffer->getFirstReadablePacketInfo(steady_clock::now()); + ps->core().m_pRcvBuffer->getFirstReadablePacketInfo(tnow); if (info.seqno == SRT_SEQNO_NONE) { HLOGC(grlog.Debug, log << "grp/recv: $" << id() << ": @" << ps->m_SocketID << ": Nothing to read."); @@ -2253,6 +2258,12 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) { socketToRead = ps; infoToRead = info; + + if (m_RcvBaseSeqNo != SRT_SEQNO_NONE && ((CSeqNo(w_mc.pktseq) - CSeqNo(m_RcvBaseSeqNo)) == 1)) + { + // We have the next packet. No need to check other read-ready sockets. + break; + } } } From 57a4d9f3926d8ecf06fee1b916eca55ccd0eae5b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 25 Apr 2024 12:06:04 +0200 Subject: [PATCH 47/51] [core] Fixed time base sync in a group. --- srtcore/core.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index a0c6e4dbd..6aec48b94 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8721,16 +8721,15 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr // srt_recvfile (which doesn't make any sense), you'll have a deadlock. if (m_config.bDriftTracer) { - const bool drift_updated SRT_ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), tsArrival, rtt); #if ENABLE_BONDING - if (drift_updated && m_parent->m_GroupOf) - { - ScopedLock glock(uglobal().m_GlobControlLock); - if (m_parent->m_GroupOf) - { - m_parent->m_GroupOf->synchronizeDrift(this); - } - } + ScopedLock glock(uglobal().m_GlobControlLock); + const bool drift_updated = +#endif + m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), tsArrival, rtt); + +#if ENABLE_BONDING + if (drift_updated) + m_parent->m_GroupOf->synchronizeDrift(this); #endif } From 07859c85074f8abf5f13b2fef59a6dd1d5c25cc3 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 26 Apr 2024 11:44:42 +0200 Subject: [PATCH 48/51] [core] Fixed group synchronization of accepted sockets. Fixes #2941. --- srtcore/core.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 6aec48b94..bc4e0d3de 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5755,14 +5755,6 @@ void srt::CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs) void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& w_hs) { HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: setting up data according to handshake"); -#if ENABLE_BONDING - // Keep the group alive for the lifetime of this function, - // and do it BEFORE acquiring m_ConnectionLock to avoid - // lock inversion. - // This will check if a socket belongs to a group and if so - // it will remember this group and keep it alive here. - CUDTUnited::GroupKeeper group_keeper(uglobal(), m_parent); -#endif ScopedLock cg(m_ConnectionLock); @@ -5850,6 +5842,16 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& throw CUDTException(MJ_SETUP, MN_REJECTED, 0); } +#if ENABLE_BONDING + // The socket and the group are only linked to each other after interpretSrtHandshake(..) has been called. + // Keep the group alive for the lifetime of this function, + // and do it BEFORE acquiring m_ConnectionLock to avoid + // lock inversion. + // This will check if a socket belongs to a group and if so + // it will remember this group and keep it alive here. + CUDTUnited::GroupKeeper group_keeper(uglobal(), m_parent); +#endif + if (!prepareBuffers(NULL)) { HLOGC(cnlog.Debug, From 38a3a165a1af92f6423de8c97646de07cba5e120 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 30 Apr 2024 11:30:19 +0200 Subject: [PATCH 49/51] [core] Fixed checking the m_GroupOf is not NULL. A follow-up fix for #2938. --- srtcore/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index bc4e0d3de..1612830e7 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8730,7 +8730,7 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), tsArrival, rtt); #if ENABLE_BONDING - if (drift_updated) + if (drift_updated && m_parent->m_GroupOf) m_parent->m_GroupOf->synchronizeDrift(this); #endif } From ebe2c7118fe23fcb171170913f59e9e1f0641d9b Mon Sep 17 00:00:00 2001 From: kageds <65413014+kageds@users.noreply.github.com> Date: Wed, 12 Jun 2024 14:37:07 +0100 Subject: [PATCH 50/51] [core] Fix build issues with ENFORCE_SRT_DEBUG_BONDING_STATES (#2948). --- srtcore/group.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 0927d085a..3e57ef5df 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2574,7 +2574,7 @@ class StabilityTracer str_tnow.replace(str_tnow.find(':'), 1, 1, '_'); } const std::string fname = "stability_trace_" + str_tnow + ".csv"; - m_fout.open(fname, std::ofstream::out); + m_fout.open(fname.c_str(), std::ofstream::out); if (!m_fout) std::cerr << "IPE: Failed to open " << fname << "!!!\n"; From 72303d7934f9c6b1cbe23c438672f0eba0f318cb Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 12 Jun 2024 15:55:53 +0200 Subject: [PATCH 51/51] [core] Fixed bug: srt_accept failure may make accepted socket leak (#1884). Added unit test. --- srtcore/api.cpp | 43 ++++++++++++-------- srtcore/api.h | 2 +- test/test_connection_timeout.cpp | 67 ++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 18 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 56c581fec..ca26600d1 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -766,7 +766,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, enterCS(ls->m_AcceptLock); try { - ls->m_QueuedSockets.insert(ns->m_SocketID); + ls->m_QueuedSockets[ns->m_SocketID] = ns->m_PeerAddr; } catch (...) { @@ -1110,8 +1110,22 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int } else if (ls->m_QueuedSockets.size() > 0) { - set::iterator b = ls->m_QueuedSockets.begin(); - u = *b; + map::iterator b = ls->m_QueuedSockets.begin(); + + if (pw_addr != NULL && pw_addrlen != NULL) + { + // Check if the length of the buffer to fill the name in + // was large enough. + const int len = b->second.size(); + if (*pw_addrlen < len) + { + // In case when the address cannot be rewritten, + // DO NOT accept, but leave the socket in the queue. + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + } + + u = b->first; ls->m_QueuedSockets.erase(b); accepted = true; } @@ -1182,14 +1196,8 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int if (pw_addr != NULL && pw_addrlen != NULL) { - // Check if the length of the buffer to fill the name in - // was large enough. - const int len = s->m_PeerAddr.size(); - if (*pw_addrlen < len) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - memcpy((pw_addr), &s->m_PeerAddr, len); - *pw_addrlen = len; + memcpy((pw_addr), s->m_PeerAddr.get(), s->m_PeerAddr.size()); + *pw_addrlen = s->m_PeerAddr.size(); } return u; @@ -2751,23 +2759,24 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) // if it is a listener, close all un-accepted sockets in its queue // and remove them later - for (set::iterator q = s->m_QueuedSockets.begin(); q != s->m_QueuedSockets.end(); ++q) + for (map::iterator q = s->m_QueuedSockets.begin(); + q != s->m_QueuedSockets.end(); ++ q) { - sockets_t::iterator si = m_Sockets.find(*q); + sockets_t::iterator si = m_Sockets.find(q->first); if (si == m_Sockets.end()) { // gone in the meantime LOGC(smlog.Error, - log << "removeSocket: IPE? socket @" << (*q) << " being queued for listener socket @" - << s->m_SocketID << " is GONE in the meantime ???"); + log << "removeSocket: IPE? socket @" << (q->first) << " being queued for listener socket @" + << s->m_SocketID << " is GONE in the meantime ???"); continue; } CUDTSocket* as = si->second; as->breakSocket_LOCKED(); - m_ClosedSockets[*q] = as; - m_Sockets.erase(*q); + m_ClosedSockets[q->first] = as; + m_Sockets.erase(q->first); } } diff --git a/srtcore/api.h b/srtcore/api.h index 9ba77d23a..fddbfc294 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -151,7 +151,7 @@ class CUDTSocket CUDT m_UDT; //< internal SRT socket logic public: - std::set m_QueuedSockets; //< set of connections waiting for accept() + std::map m_QueuedSockets; //< set of connections waiting for accept() sync::Condition m_AcceptCond; //< used to block "accept" call sync::Mutex m_AcceptLock; //< mutex associated to m_AcceptCond diff --git a/test/test_connection_timeout.cpp b/test/test_connection_timeout.cpp index 0b8bb7874..dca7595b8 100644 --- a/test/test_connection_timeout.cpp +++ b/test/test_connection_timeout.cpp @@ -1,4 +1,5 @@ #include +#include #include #include "test_env.h" @@ -12,6 +13,7 @@ typedef int SOCKET; #include"platform_sys.h" #include "srt.h" +#include "netinet_any.h" using namespace std; @@ -204,3 +206,68 @@ TEST_F(TestConnectionTimeout, BlockingLoop) } +TEST(TestConnectionAPI, Accept) +{ + using namespace std::chrono; + using namespace srt; + + srt_startup(); + + const SRTSOCKET caller_sock = srt_create_socket(); + const SRTSOCKET listener_sock = srt_create_socket(); + + const int eidl = srt_epoll_create(); + const int eidc = srt_epoll_create(); + const int ev_conn = SRT_EPOLL_OUT | SRT_EPOLL_ERR; + srt_epoll_add_usock(eidc, caller_sock, &ev_conn); + const int ev_acp = SRT_EPOLL_IN | SRT_EPOLL_ERR; + srt_epoll_add_usock(eidl, listener_sock, &ev_acp); + + sockaddr_any sa = srt::CreateAddr("localhost", 5555, AF_INET); + + ASSERT_NE(srt_bind(listener_sock, sa.get(), sa.size()), -1); + ASSERT_NE(srt_listen(listener_sock, 1), -1); + + // Set non-blocking mode so that you can wait for readiness + bool no = false; + srt_setsockflag(caller_sock, SRTO_RCVSYN, &no, sizeof no); + srt_setsockflag(listener_sock, SRTO_RCVSYN, &no, sizeof no); + + srt_connect(caller_sock, sa.get(), sa.size()); + + SRT_EPOLL_EVENT ready[2]; + int nready = srt_epoll_uwait(eidl, ready, 2, 1000); // Wait 1s + EXPECT_EQ(nready, 1); + EXPECT_EQ(ready[0].fd, listener_sock); + // EXPECT_EQ(ready[0].events, SRT_EPOLL_IN); + + // Now call the accept function incorrectly + int size = 0; + sockaddr_storage saf; + + EXPECT_EQ(srt_accept(listener_sock, (sockaddr*)&saf, &size), SRT_ERROR); + + std::this_thread::sleep_for(seconds(1)); + + // Set correctly + size = sizeof (sockaddr_in6); + EXPECT_NE(srt_accept(listener_sock, (sockaddr*)&saf, &size), SRT_ERROR); + + // Ended up with error, but now you should also expect error on the caller side. + + // Wait 5s until you get a connection broken. + nready = srt_epoll_uwait(eidc, ready, 2, 5000); + EXPECT_EQ(nready, 1); + if (nready == 1) + { + // Do extra checks only if you know that this was returned. + EXPECT_EQ(ready[0].fd, caller_sock); + EXPECT_EQ(ready[0].events & SRT_EPOLL_ERR, 0); + } + srt_close(caller_sock); + srt_close(listener_sock); + + srt_cleanup(); +} + +